Level 8 - Blind SQL Injection
Domain(s): Web, RE, Pwn, Cloud
Last updated
Domain(s): Web, RE, Pwn, Cloud
Last updated
As part of the anti-PALINDROME task force, you find yourself face to face with another task. "We found this horribly made website on their web servers," your superior tells you. "It's probably just a trivial SQL injection vulnerability to extract the admin password. I'm expecting this to be done in about an hour." You ready your fingers on the keyboard, confident that you'll be able to deliver. http://chals.tisc23.ctf.sg:28471/
Opening the provided URL, we are shown a simple login page for a reminder application.
Before we try anything, let's take a look at the source code.
In the Dockerfile provided, we see that AWS credentials are copied from the host machine to the Docker container, seeing that the challenge is cloud related, this could be useful later on.
In server.js, we see an API endpoint that does something suspicious.
It uses the pug template library to render a local file that a user can specify in the request. This means that we have a potential LFI vulnerability. Since we know the location of the AWS credentials in the container, we can exfiltrate it with this.
In the login API endpoint in server.js, we see that a lambda function named craft_query
is invoked as a way to filter any SQL injection attempts.
The lambda function returns an SQL query which gets run by the backend server.
With all of the information we have gathered, it seems that we are supposed to bypass this SQL injection filter to exfiltrate the flag in the admin's password.
Using the credentials we found earlier, we can enumerate the permissions of the AWS user. First, configure an AWS CLI profile.
We can then get the username of the user associated with the credentials
Since we know that the account can access Lambda functions, specifically craft_query
, we can retrieve information about the function, including its code.
In the output of the above command, we see a URL that points to an S3 bucket that contains the code.
When we open this URL, we receive a zip archive with some JavaScript files and a WASM file.
The index.js file pulls site.js, which loads the CraftQuery
function from site.wasm.
The output from tools like wasm2wat, wasm2c and wasm-decompile were quite difficult to understand to create an exploit. However, from some tests, it seemed that the craft_query
function only accepted alphanumeric characters. This is likely where the Pwn comes into play.
After installing a WASM plugin for Ghidra, we can view the exported functions from site.wasm and look through the code to understand it better.
After decompiling, looking through the code and renaming some variables and functions, this is roughly what craft_query
does.
We can see that the check_password
function takes in the password length (0x3b=59
), and uses it to ensure that the buffer is not written out of.
However, the check_username
function does not. This means that there is a buffer overflow vulnerability in the username input.
Something else that we should take note of is line 19.
This line uses the uStack12
variable and multiplies it by 4 to determine the function that is called, which in this case we know is is_blacklisted
.
is_blacklisted
runs a function (that I renamed is_valid
) to check that the string provided is alphanumeric.
If the password contains non-alphanumeric characters, the function returns the blacklisted message, otherwise it calls the load_query
function. The load_query
function calls another function that essentially formats the string:
Now we have found 3 things.
The username input is vulnerable to buffer overflow
The function called in craft_query
is determined by the value of uStack12
is_blacklisted
and load_query
take in the same arguments
With this information, our goal is to use the buffer overflow vulnerability to change the value of uStack12
, changing the offset to call the load_query
function instead. And it just so happens that uStack12
is placed right after the username buffer on the stack.
Using the following code, we can test this theory.
This code writes an SQL injection payload into the username buffer and fills it with "A"s to fill the 68 byte-long buffer. It appends the 0x02
byte to change the value of uStack12
to 2, making the function offset 2*4=8
.
With that, we can see that the lambda function returns the SQL-injected query and we can start exfiltrating the flag.
Write a brute-force script to carry out blind SQL injection on the User table to retrieve the admin's password, using the ORD function to make it case-sensitive.
Run the script and wait for it to brute force all characters, and you should receive the full flag.
Flag: TISC{a1PhAb3t_0N1Y}