Gen's blog

CTFとか、競プロとか、その他諸々

Tokyo Westerns CTF 3rd 2017 WriteUp

久しぶりにCTFに参加した。(これは若干嘘で、katagaitai勉強会でXSS千本ノックをしたりSANS NETWARS トーナメント 2017に参加したりしていた)

Harekazeで出て、940ptsで33位だった。 僕はrevのwarmup問題のrev_rev_revだけ解いた。

rev_rev_rev

読むと 、fgets()で入力をとったあとに、入力文字列を4つの関数で処理して、あるバイト列と比較していることがわかる。4つの関数は順に、

  • 改行文字を'\0'に(chomp的なやつ)
  • 文字列を逆順に(reverse的なやつ)
  • 各文字cに対して下の3行を実行する。実は、最初の<< 1add eax, eax(c & 0x55)の結果を自身に足していたんだけれど、これは多分最適化された結果で、下2行の雰囲気を見ると元は<< 1だと思う。
c = ((c & 0x55) << 1) | ((c >> 1) & 0x55);
c = ((c & 0x33) << 2) | ((c >> 2) & 0x33);
c = (c << 4) | (c >> 4);
  • 各文字のビットを反転する。

となっている。手動デコンパイルしたのをチームに共有したあと、お腹が減ったのでシャワーを浴びてご飯を買いに行ってたら、うさぎさんが「256通り試せば行けそう」と言っていたので脳死でソルバを書いた。(一文字ずつ処理してるのでそれはそう)

以下のコードでフラグが出た。

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

const unsigned char ans[] = {
    0x41,0x29,0xd9,0x65,0xa1,0xf1,0xe1,0xc9,
    0x19,0x09,0x93,0x13,0xa1,0x09,0xb9,0x49,
    0xb9,0x89,0xdd,0x61,0x31,0x69,0xa1,0xf1,
    0x71,0x21,0x9d,0xd5,0x3d,0x15,0xd5,0x00
};

void reverse(char *str) {
    int len = strlen(str);
    char *end = str + len - 1;

    while (str < end) {
        char c = *str;
        *str = *end;
        *end = c;
        str++;
        end--;
    }
}

int main() {
    char buf[0x21];
    int len = strlen((char *)ans);

    for (int i = 0; i < len; i++) {
        for (unsigned char chr = 0x21; chr <= 0x7e; chr++) {
            unsigned char c = chr;
            c = ((c & 0x55) << 1) | ((c >> 1) & 0x55);
            c = ((c & 0x33) << 2) | ((c >> 2) & 0x33);
            c = (c << 4) | (c >> 4);
            if ((~c & 0xff) == ans[i]) {
                buf[i] = chr;
                break;
            }
        }
    }
    reverse(buf);
    printf("%s\n", buf);
    return 0;
}

TWCTF{qpzisyDnbmboz76oglxpzYdk}

let’s go とかも解きたかったんですが、goのバイナリは気合が足りなくて読めず…