Ricerca CTF 2023 writeup (forensics)

参加して1問解きました。

[forensics] My name is Power! (257pt)

展開すると4.5GBのmemory.rawファイル。
なんだかわからないが、とりあえずメモリフォレンジックと言えばVolatilityということでやってみる。

$ vol3 -f memory.raw windows.info
Volatility 3 Framework 2.4.2
Progress:  100.00               PDB scanning finished
Variable        Value

Kernel Base     0xf80648600000
DTB     0x1ae000
Symbols file:///home/tkito/volatility3/volatility3/symbols/windows/ntkrnlmp.pdb/797E613DB16DB6C0E57795A0CB03F471-1.json.xz
Is64Bit True
IsPAE   False
layer_name      0 WindowsIntel32e
memory_layer    1 FileLayer
KdVersionBlock  0xf806492099b8
Major/Minor     15.22621
MachineType     34404
KeNumberProcessors      4
SystemTime      2023-04-18 08:45:26
NtSystemRoot    C:\Windows
NtProductType   NtProductWinNt
NtMajorVersion  10
NtMinorVersion  0
PE MajorOperatingSystemVersion  10
PE MinorOperatingSystemVersion  0
PE Machine      34404
PE TimeDateStamp        Tue Aug 10 18:00:02 1982

Windows 10ということでほっと一安心。Windows/Linux/Macじゃなかったらどうしようかと。

Windowsとわかったので、怪しいやつがいないか見ていこう。

$ vol3 -f memory.raw windows.pstree
Volatility 3 Framework 2.4.2
Progress:  100.00               PDB scanning finished
PID     PPID    ImageFileName   Offset(V)       Threads Handles SessionId       Wow64   CreateTime      ExitTime

...snip...

*** 748 640     winlogon.exe    0xd38576750080  3       -       1       False   2023-04-18 08:36:17.000000      N/A
**** 952        748     fontdrvhost.ex  0xd385768630c0  5       -       1       False   2023-04-18 08:36:18.000000      N/A
**** 4344       748     userinit.exe    0xd38577cc1080  0       -       1       False   2023-04-18 08:36:24.000000      2023-04-18 08:36:54.000000
***** 4384      4344    explorer.exe    0xd38577cd9080  71      -       1       False   2023-04-18 08:36:24.000000      N/A
****** 5472     4384    OneDrive.exe    0xd38579656080  22      -       1       False   2023-04-18 08:36:45.000000      N/A
****** 2468     4384    VBoxTray.exe    0xd3857998a080  13      -       1       False   2023-04-18 08:36:44.000000      N/A
****** 4484     4384    SecurityHealth  0xd3857997e080  2       -       1       False   2023-04-18 08:36:43.000000      N/A
****** 2788     4384    cmd.exe 0xd385741eb140  2       -       1       False   2023-04-18 08:37:55.000000      N/A
******* 5536    2788    winpmem_mini_x  0xd3857af020c0  3       -       1       False   2023-04-18 08:45:12.000000      N/A
******* 2900    2788    conhost.exe     0xd385741ed140  4       -       1       False   2023-04-18 08:37:55.000000      N/A
****** 4688     4384    regedit.exe     0xd38571e42140  3       -       1       False   2023-04-18 08:39:38.000000      N/A
****** 6772     4384    cmd.exe 0xd3857ae980c0  2       -       1       False   2023-04-18 08:44:16.000000      N/A
******* 1460    6772    conhost.exe     0xd38579426080  6       -       1       False   2023-04-18 08:44:16.000000      N/A
******* 2068    6772    powershell.exe  0xd3857a35e080  20      -       1       False   2023-04-01 08:44:54.000000      N/A
**** 724        748     LogonUI.exe     0xd3857698d1c0  0       -       1       False   2023-04-18 08:36:18.000000      2023-04-18 08:36:31.000000
**** 1028       748     dwm.exe 0xd3857694c080  18      -       1       False   2023-04-18 08:36:18.000000      N/A

explorer.exeの子プロセスを見ていく。
OneDrive.exeVBoxTray.exeSecurityHealth(Systray.exe)は特に変なものではない。
cmd.exeからのwinpmem_mini_xはこのメモリイメージを取得したWinPmemのプロセスであろう。
残るregedit.exeとcmd.exeからのpowershell.exeは問題ないとは言えないので、この辺を詳しく見ていった方がよさそう。
regedit.exeはGUIなので後回しにして、powershell.exeについて詳しく知るため、コマンドラインを取得してみる。

※Markdownでメモ取ってたらWindows Defenderに怒られたので、ここからは画像で。

明らかに怪しいコマンドライン。以下コマンドラインの簡単な解説。

  • pOwERsHEll
    • Windowsのコマンドラインでは大文字小文字を区別しないので要するにpowershell
  • -eP bYpASs ( = -ep bypass)
  • -e JgAg...
    • -eは以降の文字列をBase64でエンコードされたコマンド列とみなす。要するに後ろの文字列をBase64ででコードすれば実行しようとしているコマンドが出てくる。

というわけでBase64デコードしてみる。

$PSHOMEは通常の環境だと'C:\Windows\System32\WindowsPowerShell\v1.0'なので、$PsHOme[4]+$psHoME[34]+'X''iex'になる。
iexInvoke-Expressionのエイリアスで、パラメータとして渡された文字列をPowerShellコマンドとして実行するコマンドレットである。

<余談>
ここで、この次のステップに進むためにiexの後ろのコードでやっていることをCyberChefで再現しようと”From Base64″→”Raw Deflate”でやろうとしてもうまくいかなくて一敗。正しくはDeflateではなくInflate。Deflateで展開と思い込んでしまって負け。
</余談>

iexのパラメータは文字列なので、iexの後ろを実行すれば文字列が出てくるはずである。というわけでPowerShellに入れて実行させればよい。なお、このステップのみWindows Defenderでブロックされるので、この手法でやりたい場合は利ありタイム保護をオフにする必要がある。
CyberChefでちゃんとInflateを選んでいればそっちでもよい。

わかりにくいがreaDtoEND()の次の行からが出力である。
冒頭の$vErbOsePrEFeReNcE'SilentlyContinue'なので、$vErbOsePrEFeReNcE.TosTrIng()[1,3]+'X'-jOiN'''iex'になる。
またiexなので同じようにする。

今度は末尾にiexが出ている。その前にパイプ(|)があるので、前半で文字列を出力してそれをiexに食わせるということで構造は変わらない。
また、冒頭に条件分岐がある。特に難しいものではなく「コンピュータ名が’RICSEC’である場合に以下の処理をする」というだけである。このスクリプトを動的解析しようとする場合に引っ掛かりそうなくらいか。

今回も末尾にiexが来ている。

ついにiexがなくなった。
後は手で難読化を解くか、部分部分をPowerShellで実行して元の文字列に戻していき、整形していけばよい。
整形した結果は以下の通り。

if (
    (Get-Date).Month -eq 4 -and (Get-Date).Day -eq 1
) {
    Set-Item 'Variable:s9qIk1' ([Type]"System.Text.Encoding");
    ${B}=(&('gp') "HKCU:\Software\Microsoft\CTF")."fieND";
    ${k}=  $s9QIK1::"ASCII".("GetBytes").Invoke("f1bb3r");
    for (${i} = 0; ${i} -lt ${B}."Length"; ${i}++) {
        ${B}[${i}] = ${b}[${i}] -bxor ${k}[${i} % ${k}."Length"]
    };
    ${A} = .("New-Object") ("System.Security.Cryptography.AesCryptoServiceProvider");
    ${Sh} = .("New-Object") ("System.Security.Cryptography.SHA256Managed");
    ${U} = .("New-Object") ("System.Text.UTF8Encoding");
    ${H} = ${sh}."ComputeHash"(${U}.("GetBytes").Invoke(${K}));
    ${A}."key" = ${H};
    [byte[]]${Iv} = 0..15;
    ${A}."IV" = ${iV};
    ${e} = ${a}.("CreateEncryptor").Invoke();
    ${eD} = ${e}.("TransformFinalBlock").Invoke(${b}, 0, ${b}."Length");
    ${Ed};
    &('sp') ((("HKCU:\Software\Microsoft\CTF"))) -Name ("fiend") -Value ${ed};
    .("Start-Sleep") -Seconds 600;
};

冒頭のif文は、実行した日付が4/1の場合にブロック内の処理をするという内容。
ブロックではHKCU:\Software\Microsoft\CTFfiendというレジストリ値のデータを読んでXORでマスクしてAESで暗号化してまたfiendに書き戻す、ということをしている。
XORの鍵は'f1bb3r'という文字列、AESの鍵は'f1bb3r'という文字列をこねくり回して作る、ということでこの処理に必要なパラメータはすべてわかっている。

そして、実行時のメモリイメージがあることから、書き戻し後のfiendの値はメモリ内に残っているはず。
ということでメモリイメージに戻ってレジストリデータを取り出す。

$ vol3 -f memory.raw windows.registry.printkey --key "Software\Microsoft\CTF"
Volatility 3 Framework 2.4.2
Progress:  100.00               PDB scanning finished
Last Write Time Hive Offset     Type    Key     Name    Data    Volatile

...snip...

2023-04-01 08:44:57.000000      0x850e6e280000  REG_BINARY      \??\C:\Users\User\ntuser.dat\Software\Microsoft\CTF   fiend   "
39 da 2a 85 c9 5b 42 17 9.*..[B.
84 11 d8 23 3b 0b f2 0e ...#;...
26 8c 95 89 ff e6 f1 7e &......~
4b f8 43 42 d0 24 37 70 K.CB.$7p"       False

...snip...

32バイトのデータを獲得できた。
あとはこれを使って上記の逆の処理をしてやれば元のレジストリ値のデータが得られる。

Set-Item 'Variable:s9qIk1' ([Type]"System.Text.Encoding");
${k}=  $s9QIK1::"ASCII".("GetBytes").Invoke("f1bb3r");
${A} = .("New-Object") ("System.Security.Cryptography.AesCryptoServiceProvider");
${Sh} = .("New-Object") ("System.Security.Cryptography.SHA256Managed");
${U} = .("New-Object") ("System.Text.UTF8Encoding");
${H} = ${sh}."ComputeHash"(${U}.("GetBytes").Invoke(${K}));
${A}."key" = ${H};
[byte[]]${Iv} = 0..15;
${A}."IV" = ${iV};
${d} = ${a}.("CreateDecryptor").Invoke();
${c} = [byte[]]@(0x39, 0xda, 0x2a, 0x85, 0xc9, 0x5b, 0x42, 0x17, 0x84, 0x11, 0xd8, 0x23, 0x3b, 0x0b, 0xf2, 0x0e, 0x26, 0x8c, 0x95, 0x89, 0xff, 0xe6, 0xf1, 0x7e, 0x4b, 0xf8, 0x43, 0x42, 0xd0, 0x24, 0x37, 0x70)
${p} = ${d}.("TransformFinalBlock").Invoke(${c}, 0, ${c}."Length");
for (${i} = 0; ${i} -lt ${p}."Length"; ${i}++) {
    ${p}[${i}] = ${p}[${i}] -bxor ${k}[${i}%${k}."Length"]
};
${U}.GetString(${p})

<余談>
当初'f1bb3r''fibbr3'と間違えてしまい、AES復号からしてうまくいかなくて悩みまくった。二敗。
</余談>

フラグが出てきたのでこれが答え。

Flag: RicSec{6r347_90w3r!}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください