TSALVIA技術メモ

CTFのWriteupや気になったツールについて書いていきます。また、このサイトはGoogle Analyticsを利用しています。

Tasteless CTF 2019 Writeup

Tasteless CTF 2019 について

Tasteless CTF 2019 が開催されました。
2019年10月26日午後9時~2019年10月27日午後9時(24時間)

ctf.tasteless.eu

このCTFも難易度の高い問題が多かったです。色々問題を見ていましたが、チュートリアル問題も含めて結局2問しか解くことができませんでした。 一応今回もチームで参加しました。結果は、47/157位で155点でした。 今回、私が実際に解いた2問のWriteupを紹介します。

f:id:tsalvia:20191028011157p:plain

同日にBackdoorCTF 2019も開催されていました。 こちらも同時に参加していました。Writeupを書きましたので、参考にしてみてください。

tsalvia.hatenablog.com

Tasteless CTF 2019 Writeup(2問)

sanity(misc)

問題

this challenge is protected.
hitme.tasteless.eu:10001

protected challenges will require a proof-of-work like this:
sha1(abc123, input) prefix = 00000...

you need to respond with a single line suffix to abc123, so that sha1(abc123[input]) has a 00000 prefix example: sha(abc12344739190).hexdigest = 000000872D5625DEE5FD0EA44B230D7A98C1B2CA

you can use go run pow.go abc123 00000 or python pow.py abc123 00000 to generate your own. the pow binary is go, compiled for linux/amd64.

  • pow
  • pow.go
  • pow.py

解答例

Tasteless CTF のチュートリアル問題です。 このCTFは、少し変わっていて、netcatで接続する問題にプロテクトがかかっている場合があります。 他のCTFと同じようにnetcatで接続するだけでは、問題にたどり着くことができません。

netcatで接続してみると、以下のようになります。

$ nc hitme.tasteless.eu 10001
sha1(cf27ff1e75faba1c, input) prefix = 00000...

Tasteless CTFは、接続用のツールを提供しており、これを使って問題を解いていく必要があるようです。 ヘルプを見ると以下のように表示されます。

$ ./pow
usage

solve args:
Usage: ./pow <prefix> <hash>
Usage: ./pow d616656ece36eb66 00000

solve serverresponse:
Usage: ./pow '<serverresponse>'
Usage: ./pow 'sha1(d616656ece36eb66, input) prefix = 00000...'

netcat mode:
Usage: ./pow connect <host> <port>
Usage: ./pow connect hitme.tasteless.eu 10001

server mode:
Usage: ./pow listen <addr> <host> <port>
Usage: ./pow listen :12345 hitme.tasteless.eu 10001

実行すると、フラグが表示されました。

$ ./pow connect hitme.tasteless.eu 10001
welcome! tctf{are_y0u_readdddyyyy}

FLAG

tctf{are_y0u_readdddyyyy}

babypad(crypto)

問題

We heard this kind of enription is super securr, so we'll just give you the flag encripted!
f:id:tsalvia:20191028011804p:plain nc hitme.tasteless.eu 10401
$ sha1sum chall.c
d64fc2e2f979b693696efe1762e18153df1b6170 chall.c
Author: plonk

chall.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

int main() {
    char *plaintext = NULL;
    char *one_time_pad = NULL;
    char *ciphertext = NULL;
    size_t flag_length = 0;
    FILE *flag = fopen("flag.txt", "r");
    FILE *urandom = fopen("/dev/urandom", "r");
    assert(flag && urandom);

    /*
     * Get flag length, and allocate memory for the plaintext,
     * one-time pad and ciphertext.
     */
    fseek(flag, 0, SEEK_END);
    flag_length = ftell(flag);
    rewind(flag);

    plaintext = malloc(flag_length + 1);
    one_time_pad = malloc(flag_length + 1);
    ciphertext = malloc(flag_length + 1);
    assert(plaintext && one_time_pad && ciphertext);

    /* Read the plaintext and the one-time-pad */
    fread(plaintext, flag_length, 1, flag);
    fread(one_time_pad, flag_length, 1, urandom);
    plaintext[flag_length] = '\0';
    one_time_pad[flag_length] = '\0';

    /* Make sure that the one-time-pad isn't too short. */
    assert(strlen(plaintext) == strlen(one_time_pad));

    for (int i = 0; i < flag_length; i++) {
        ciphertext[i] = plaintext[i] ^ one_time_pad[i];
    }

    fwrite(ciphertext, flag_length, 1, stdout);
    return 0;
}

解答例

この問題は、ソースコードが提供されました。 読んでみると、以下のような処理を行っていました。

  1. flag.txtのデータサイズを求める。
  2. flag.txtのデータサイズ+1の長さのバッファ(plaintextone_time_padciphertext)を3つ確保する。
  3. flag.txtからデータを読み出し、plaintextに格納する。
  4. /dev/urandomからデータを読み出し、one_time_padに格納する。
  5. plaintextone_time_padを1文字ずつ取り出して、それぞれをXOR演算し、ciphertextに格納する(flag.txtのデータサイズ分繰り返す)。
  6. ciphertextを標準出力に出力する。

ただし、よく読んでみると、以下のような処理が確認できます。 C言語のstrlenは、Null文字までの文字数を返す関数です。 そのため、/dev/urandomから読み出したデータに\0が含まれる場合、ここでエラーとなり終了してしまいます。

/* Make sure that the one-time-pad isn't too short. */
assert(strlen(plaintext) == strlen(one_time_pad));

実際にコンパイルして、何度か実行していると想定箇所で終了しました。

$ gcc chall.c
$ ./a.out
a.out: chall.c:35: main: Assertion `strlen(plaintext) == strlen(one_time_pad)' failed.
Aborted (core dumped)

よって、XORキーには、0x00が含まれないということが分かりました。 そのため、何度もXORで暗号化された文字列を取得し、0x01~0xFFで復号していけば絞り込むことができそうです。

以下のようなスクリプトを作成し、復号パターンを絞り込んでいきました。

from pwn import *
import string

def check_dec_pattern(pattern, enc_flag, offset):
    new_pattern = ""
    for key in range(1, 0x100):
        ch = chr(enc_flag[offset] ^ key)
        if ch in pattern:
            new_pattern += ch
    return new_pattern

def get_enc_flag():
    while True:
        context.log_level = "error"
        con = remote("hitme.tasteless.eu", 10401)
        enc_flag = con.recvall()
        if len(enc_flag) > 0:
            return enc_flag

def main():
    max_len = len(get_enc_flag())

    pattern_list = []
    for _ in range(max_len):
        pattern = string.ascii_letters + string.digits + string.punctuation
        pattern_list.append(pattern)

    loop_end = False
    while loop_end == False:
        enc_flag = get_enc_flag()

        loop_end = True
        for i in range(max_len):
            new_pattern = check_dec_pattern(pattern_list[i], enc_flag, i)
            pattern_list[i] = new_pattern
            if len(new_pattern) > 1:
                print(len(new_pattern), end=", ")
                loop_end = False
            else:
                print(new_pattern, end=", ")
        print()
    
    print("".join(pattern_list))

if __name__ == "__main__":
    main()

結構時間がかかりましたが、完全に復号することができました。

$ python solve.py
93, 94, 93, 94, 94, 93, 94, 94, 94, 94, 94, 94, 93, 94, 93, 94, 94, 93, 94, 93, 94, 93, 94, 93, 93, 94, 94, 94, 94, 93, 94, 94, 93, 94, 94, 94, 94, 
92, 93, 93, 93, 94, 93, 93, 94, 93, 94, 93, 93, 93, 94, 92, 93, 94, 92, 94, 93, 94, 93, 93, 93, 92, 94, 94, 94, 93, 92, 94, 94, 92, 93, 93, 94, 94, 
91, 93, 93, 93, 94, 93, 93, 94, 92, 94, 93, 93, 92, 93, 92, 93, 94, 91, 94, 92, 94, 92, 93, 92, 91, 94, 93, 93, 93, 92, 94, 93, 92, 93, 93, 94, 93, 
90, 92, 92, 93, 94, 92, 92, 93, 92, 94, 92, 92, 92, 92, 92, 93, 94, 91, 93, 91, 94, 92, 93, 91, 91, 94, 92, 93, 93, 92, 94, 92, 91, 92, 92, 94, 93,

# 省略

t, c, t, f, {, p, 1, z, _, u, s, 2, :, 4, l, l, -, t, 3, h, _, b, y, 7, e, 5, >, 0, n, 3, _, t, i, m, 3, }, , 
t, c, t, f, {, p, 1, z, _, u, s, 2, :, 4, l, l, -, t, 3, h, _, b, y, 7, e, 5, >, 0, n, 3, _, t, i, m, 3, }, , 
t, c, t, f, {, p, 1, z, _, u, s, 3, :, 4, l, l, -, t, 3, h, _, b, y, 7, e, 5, >, 0, n, 3, _, t, i, m, 3, }, , 
tctf{p1z_us3:4ll-t3h_by7e5>0n3_tim3}

FLAG

tctf{p1z_us3:4ll-t3h_by7e5>0n3_tim3}