Tags: blindsql ssti

Rating:

# Challenge Description
![](https://i.imgur.com/51vgvJh.png)

The source code for this challenge was given and we have two files :
**main-server.py**

import sqlite3
import requests;
import re;

def alphanumericalOnly(str):
return re.sub(r'[^a-zA-Z0-9]', '', str);

@app.route('/', methods=['GET', 'POST'])
if request.method == 'POST':

print(r.text);
if(r.text == "True"):
return render_template_string("OMG you are like so good at guessing our flag I am lowkey jealoussss.");

return render_template("index.html");

app.run(host='127.0.0.1',port=8081,debug=True)

and data-server.py

import sqlite3

cur = con.cursor()

cur.execute('''DROP TABLE IF EXISTS users''')
cur.execute(
)

@app.route('/runquery', methods=['POST'])
def runquery():
request_data = request.get_json()

rows = cur.fetchall()
if(len(rows) > 0):
return "True";
return "False";

app.run(host='127.0.0.1',port=8080,debug=True)


Undrestanding this code is pretty easy:
**Main-server.py**

import sqlite3
import requests;
import re;

def alphanumericalOnly(str): #Remove non alphanumeric characters from our input
return re.sub(r'[^a-zA-Z0-9]', '', str);

@app.route('/', methods=['GET', 'POST'])
if request.method == 'POST':

#Making a post request to the data server with our inputs being sanitized
print(r.text);
if(r.text == "True"):
return render_template_string("OMG you are like so good at guessing our flag I am lowkey jealoussss.");#The respons we got if our credentials are good
#If not we got this response with the password printed back :)

return render_template("index.html");

And **Data-server.py** just returns True or False depends on the result of SELECT statemnet.
# Unintended solution
This may seems very wired (and it is) but when I first tried to solve this I just tried to test the funcionality of the site without really undrestanding the code.
Lucky enough when I tried 1' or 1=1-- - in the password field, I got the "OMG ..." response which really shouldn't be but it was hh.(I don't know why this happened
but probably because the "alphanumericalOnly" function wasn't implemented on the server ).
So my first approch was just a normal blind SQl injection with a sipmle python script and this ' or (1=1 and (SELECT hex(substr(password,{position},1)) FROM users limit 1 offset 0) = hex('{charachter}'))-- - as payload and You know what, it just worked fine ?.
Unfortunately the '.' part in the code was just fine and the flag I got was ###LITCTF{flush3d_3m0ji_o0} which is again weird because the correct flag has '.' in it,
so I shouldn't be able to get the "0}" part.
Anyway,I checked the flag with an admin and he told me that I missed something.I tried several times but the flag was always the same so I just moved to something else
and decide to get back later.
# Intended solution
When I got back my script wan't working anymore hh and it seems that the challenge decided to work properly again (or someone fixed it).
When I read the code I realised that normal SQLi shouldn't have worked in the first time and that there is an SSTI here return render_template_string("ok thank you for your info i have now sold your password (" + password + ") for 2 donuts :)");.
I was just coming out of 2 CTFs with SSTI challenges and the payload was just ready :)
The first thing I tried was reading the main.py file and from it we got the data server IP "http://172.24.0.8:8080" which is a local IP so we can't just make a request to it.
From this the Solution was obvious : Using the SSTI in the main server to do a BLIND SQLi in the data server.My first thought was to use curl or wget but after checking the available commands it seems that we can't.With Python available we can just use the one-liner option.
So I just modified my first script a little bit and used it to get the flag.

import requests
import string

url="http://159.89.254.233:31781/"
flag=""
i=1

while "}" not in flag:
for j in string.printable :
if j==".": #Bypass the "." check in the main server (don't forget to replace it later in the flag)
j="\\x2E"

## Flag : LITCTF{flush3d_3m0ji_o.0}
I used to use the data= parameter in post requests with Python's requests library,however it seems that starting with Requests version 2.4.2, you can use the json= parameter instead.And as I'm always keeping my tools and softwares updated ? I just used the data= as usual which would give you an empty response (to remind you to update your stuff)