All Articles

K3RN3LCTF 2021 Writeup

2021/11/13から開催されていたK3RN3LCTFにチーム0neP@ddingと(言いつつソロで)参加してきました。

今回は省エネ参加だったのでRevの2問しか解いてないのですが、2問しか解いていない割に501チーム中139位という結果でした。(以外と難しかったのかもしれない)

Rev問で実マルウェアを容赦なく配信するエキセントリックなCTFでしたが、簡単にWriteUpを書こうと思います。

もくじ

Zabomb(Rev)

Description
You received a suspicious file from the k3rn3l4rmy hacking group, the title says ‘Not a Zip Bomb, Please Open’, you decide NOT to open it and instead try to reverse it.

It is recommended that you do NOT open this, it will fill your entire disk.

ガチマルウェアのZipBombが渡されました。

参考:新しいZip Bombが4.5PBのデータを46MBのファイルに詰め込む-2007es.com - コンピューティング

ZIPファイルの中身を見ると、異常なサイズのファイルと小さなサイズのファイルの2種類が圧縮されていました。

7ZIPなどを使って、特定のファイルのみ解凍すればOK。

7za.exe x -y -oC:\output\ -ir!filename bomb.zip
# flag{w0w_c0mpres51on_&_d3comp53ssi0N_!s_s0_c3wl_ju5t_d0n7_gO_b0OM}

参考:コマンドラインで、zipファイルから特定のファイルのみ解凍する(Windows) : 若手基盤系SEの日記。

WiRE(Rev)

Description

We wire an encryption message that contains flag from remote server and dumped it out to kernelCTF_dump.pcapng file, i'm pretty sure that client has implementation of algorithm to decrypt data and get flag, will you take up the challenge?

謎のpcapファイルとPEバイナリが渡されます。

どうやら、PEバイナリの方がメッセージを暗号化して送受信するクライアントプログラムであり、pcapファイルがFlagを暗号化してやりとりしたときの記録のようです。

デコンパイルしてみたところ、クライアントプログラムは起動した後、以下の順序で動くことがわかりました。

  • ローカルホストの9905ポートに対してTCP接続を試行する
  • TCP接続に成功した場合、メッセージデータを暗号化して送信する
  • 接続先のサーバから、暗号化されたFLAGデータを取得する
  • コネクションをクローズする

上記の処理に沿ってpcapファイルのパケットを解析したところ、d33411044a6202726302656e6901636e637462017d6702756e760101756e7b0173104c0aという暗号化されたFlagデータを受け取っていることがわかりました。

暗号化の処理はこちら。

void main.safeWrapMessage(void)

{
  byte *pbVar1;
  byte *pbVar2;
  code *pcVar3;
  byte *pbVar4;
  byte **local_res8;
  
  pbVar1 = *local_res8;
  pbVar2 = local_res8[1];
  for (pbVar4 = (byte *)0x0; (longlong)pbVar4 < (longlong)pbVar2; pbVar4 = pbVar4 + 1) {
    if (local_res8[1] <= pbVar4) goto LAB_004dbf48;
    (*local_res8)[(longlong)pbVar4] = pbVar1[(longlong)pbVar4] ^ 0x31;
  }
  if ((longlong)local_res8[1] < 5) {
    return;
  }
  **local_res8 = **local_res8 ^ 0x84;
  if ((byte *)0x1 < local_res8[1]) {
    (*local_res8)[1] = (*local_res8)[1] ^ 0x69;
    if ((byte *)0x2 < local_res8[1]) {
      (*local_res8)[2] = (*local_res8)[2] ^ 0x41;
      if ((byte *)0x3 < local_res8[1]) {
        (*local_res8)[3] = (*local_res8)[3] ^ 0x52;
        return;
      }
      runtime.panicIndex();
    }
    runtime.panicIndex();
  }
  runtime.panicIndex();
LAB_004dbf48:
  runtime.panicIndex();
  pcVar3 = (code *)swi(3);
  (*pcVar3)();
  return;
}

Flagの全文字を0x31でXOR暗号化した上で、前4文字をさらにXOR暗号化しているようです。

というわけで、d33411044a6202726302656e6901636e637462017d6702756e760101756e7b0173104c0aを0x31でXORして、最初の4文字をflagに置き換えることでFlagが取得できました。

まとめ

WiREは、Ghidraのデコンパイル結果がいまいちでなかなかFlagにたどり着けませんでした。

IDA Freeでデコンパイルしてみたところ、プログラムの処理を把握するのに有用な情報がGhidraに比べて見やすかったため、解くことができました。

バイナリによってはGhidraとIDAなどのデコンパイラの出力の見やすさに差異がでるようなので、今後解けないときは両方のデコンパイラを試してみるのはありだと思いました。