All Articles

Greycat CTF 2023 Writeup

5/19 から開催されていた Greycat CTF に参加してました。

今回はメンバーの都合が合わずライト参加で 131 位でした。

簡単に Writeup 書きます。

もくじ

Web-Assembly(Rev)

Handwriting assembly code is a bad idea…

問題バイナリとして以下の Javascript コードが与えられます。

Flag の検証を行っているwasmModule.instance.exports.check()という関数はどうやら Web Assembly で実装されているようです。

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('Gimme something: ', (flag) => {
    const wasmBinBuf = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 2, 11, 1, 2, 106, 115, 3, 109, 101, 109, 2, 0, 1, 3, 2, 1, 0, 7, 9, 1, 5, 99, 104, 101, 99, 107, 0, 0, 10, 122, 1, 120, 1, 3, 127, 65, 0, 33, 0, 65, 1, 33, 2, 3, 64, 2, 64, 2, 64, 2, 64, 2, 64, 2, 64, 32, 0, 65, 4, 112, 14, 3, 3, 2, 1, 0, 11, 65, 137, 2, 33, 1, 12, 3, 11, 65, 59, 33, 1, 12, 2, 11, 65, 41, 33, 1, 12, 1, 11, 65, 31, 33, 1, 12, 0, 11, 32, 1, 65, 255, 1, 32, 0, 40, 2, 0, 113, 108, 65, 255, 1, 113, 32, 0, 65, 192, 0, 106, 40, 2, 0, 65, 255, 1, 113, 115, 65, 0, 70, 32, 2, 108, 33, 2, 32, 0, 65, 1, 106, 33, 0, 32, 0, 65, 46, 72, 13, 0, 11, 32, 2, 11])
    const wasmMem = new WebAssembly.Memory({ initial: 10, maximum: 100 });
    var strBuf = new TextEncoder().encode(flag.slice(0, 64));
    const memBuf = new Uint8Array(wasmMem.buffer);
    
    for (let i = 0; i < strBuf.length; i++) {
        memBuf[i] = strBuf[i];
    }

    data = [121, 66, 71, 65, 229, 176, 150, 150, 43, 107, 209, 212, 12, 217, 16, 222, 129, 189, 55, 185, 82, 127, 229, 47, 45, 178, 252, 11, 107, 43, 31, 114, 20, 97, 229, 185, 237, 55, 252, 87, 12, 168, 75, 222, 121, 5]

    for (let i = 0; i < data.length; i++) {
        memBuf[i + 64] = data[i] 
    }

    WebAssembly.instantiate(wasmBinBuf, {js: {mem: wasmMem}}).then(wasmModule => {
        result = wasmModule.instance.exports.check();
        if (result) {
            console.log("Correct flag!");
        } else {
            console.log("?")
        }
    });
    rl.close();
});

読み込まれる Web Assembly コードは wasmBinBuf で定義されているバイト配列で定義されていたので、Cyberchef でデコードしたものを .wasm ファイルとして保存しました。

続いて取得した wasm ファイルを Ghidra でデコンパイルし、以下のような関数を得ることができました。

int export::check(void)
{
  uint *puVar1;
  int iVar2;
  int iVar3;
  uint uVar4;
  
  puVar1 = (uint *)0x0;
  iVar3 = 1;
  do {
    uVar4 = (uint)puVar1 % 4;
    if (uVar4 == 0) {
      iVar2 = 0x1f;
    }
    else if (uVar4 == 1) {
      iVar2 = 0x29;
    }
    else if (uVar4 == 2) {
      iVar2 = 0x3b;
    }
    else {
      iVar2 = 0x109;
    }
    iVar3 = (uint)((iVar2 * (*puVar1 & 0xff) & 0xff) == (puVar1[0x10] & 0xff)) * iVar3;
    puVar1 = (uint *)((int)puVar1 + 1);
  } while ((int)puVar1 < 0x2e);
  return iVar3;
}

Flag の検証に使用するバイト配列は Javascript で data として定義されていたので、これを使用しつつ Flag を特定できる Solver を以下の通り作成しました。

base = [121, 66, 71, 65, 229, 176, 150, 150, 43, 107, 209, 212, 12, 217, 16, 222, 129, 189, 55, 185, 82, 127, 229, 47, 45, 178, 252, 11, 107, 43, 31, 114, 20, 97, 229, 185, 237, 55, 252, 87, 12, 168, 75, 222, 121, 5]
flag = ""

for i,b in enumerate(base):
    if i % 4 == 0:
        t = 0x1f
    elif i % 4 == 1:
        t = 0x29
    elif i % 4 == 2:
        t = 0x3b
    elif i % 4 == 3:
        t = 0x109
    
    # (t * (x & 0xff) & 0xff) = (b & 0xff)
    for x in range(0xff):
        if (t * (x & 0xff) & 0xff) == (b & 0xff):
            result = x

    print(chr(result), end="")

    # grey{0bfusc4t10n_u51ng_w3b4s53mbly_1s_4_th1ng}

これで Flag を取得できました。

ReService(Rev)

I found this file that was installed by the virus. Can you find out what it does?

It seems to connect to a c2 server and makes use of the current time.

go バイナリのデコンパイル結果を元に手動で書き起こしてみると、以下のようなコードで生成された 8 文字の HEX 文字列を URL に付与して http.Get() 関数でリクエストを発行するコードでした。

package main

import (
	"fmt"
	"hash/crc32"
	"math/rand"
	"time"
)

func main() {
	now := time.Now().Unix()
	rand.Seed(now)
	hash := crc32.ChecksumIEEE([]byte(fmt.Sprintf("%x", rand.Int())))
	result := fmt.Sprintf("%x", hash)
	fmt.Println(result)
}

ここからこのファイルが作成された時刻をブルートフォースする形で特定するのかと思いきや全くそんなことはなく、バイナリ実行時のパケットストリームをみれば一発で Flag が取れる問題でした。

img

バイナリ解析は全く関係なく、どういう意図の問題なのかよくわかりませんでした。。