2015年10月26日月曜日

TUMCTFに参加しました

2015年10月24日の22:00(日本時間)から24時間開催されたTUMCTFにオンラインで参加しました。
3人でチームを組んで、睡眠も取って15時間以上は参加できたと思います。
世界から455チームが挑戦して(最低でも244チームは1問以上解いていました)、私達のチームは65ポイントで58位でした。

問題はCrypt, Web, Rev, Recon, Misc, Exploitから各5,6問程度ずつ出ました。今回は、私が解いた問題の内、覚えているwhitebox, bad_appleについてwriteupを書きます。

whitebox crypt - rev - 20[pt]
問題文:
Do not panic, it's only XTEA! I wonder what the key was...
ctf.link/assets/downloads/rev/xtea
問題ファイルをobjdumpで見ると、まずstrlenでargv[1]が16文字かをチェックしています。
そこで適当な16文字を渡していき、ブラックボックス的にテストすると、後半の8文字は出力に影響が無いことが分かりました。
さらに、問題文のXTEAで調べると、wikipediaに辿り着き、これを参考に解析を開始します。
wikiによると、暗号化の途中で sum + key[sum & 3] を行うのですが、keyがバレないように、この値は全て予め書き込まれています。
こんな感じ:
  4005a7:       89 ca                   mov    edx,ecx
  4005a9:       89 c8                   mov    eax,ecx
  4005ab:       c1 ea 05                shr    edx,0x5
  4005ae:       c1 e0 04                shl    eax,0x4
  4005b1:       31 d0                   xor    eax,edx
  4005b3:       01 c8                   add    eax,ecx
  4005b5:       35 68 78 70 7b          xor    eax,0x7b707868
  4005ba:       03 07                   add    eax,DWORD PTR [rdi]
  4005bc:       41 89 c2                mov    r10d,eax
  4005bf:       89 c2                   mov    edx,eax
  4005c1:       c1 e0 04                shl    eax,0x4
  4005c4:       41 c1 ea 05             shr    r10d,0x5
  4005c8:       41 31 c2                xor    r10d,eax
  4005cb:       41 01 d2                add    r10d,edx
  4005ce:       41 81 f2 2e ea 58 1b    xor    r10d,0x1b58ea2e
  4005d5:       45 8d 1c 0a             lea    r11d,[r10+rcx*1]



ということで、この sum + key[sum & 3] にあたる値を収集しました。

そのあと、なぜかこのencipherをC言語で書いたのですが、sumやkeyは入力に影響しないと気付いて、すぐにkeyの計算に取りかかりました。
wikiのdecipherを参考に、
・delta = 0x9E3779B9 で固定。
・sum = delta * num_round でnum_roundは今回の場合32個分あった。
・sum -= delta を繰り返す。
・keyは4要素ある32bit整数の配列
と分かりました。
すぐに解読コードを書きました:
#include <stdio.h>
#include <stdint.h>


int main()
{
  int num_rounds = 32, i;
  uint32_t delta = 0x9E3779B9, sum = delta * num_rounds;
  uint32_t l[64] = {0x7b707868, 0x1b58ea2e, 0xba9ae30, 0x9bd661db, 0x9bd661db, 0x4818a1a2, 0x57c7dda0, 0xf44e5f4c, 0xf44e5f4c, 0x9285d905, 0x84879514, 0x326e4acb, 0x14b448bf, 0xb2ebc278, 0xd0a5c484, 0x5f2e023f, 0x6d2c4630, 0xb63bfe9, 0xfd657bf8, 0xa99b39a2, 0x8d922fa3, 0x4983ab68, 0x4983ab68, 0xca012315, 0xe60a2d14, 0x764362dc, 0x764362dc, 0x147adc95, 0x6701687, 0xc0b09a3f, 0xc261924c, 0x60990c05, 0x5ee813f8, 0xe11683b2, 0xef2149c0, 0x8d58c379, 0x7f4dfd6b, 0x2b903d32, 0x3b3f7930, 0xd7c5fadc, 0xd7c5fadc, 0x77ae6ca, 0x67ff30a4, 0xf82be44f, 0xf82be44f, 0xa46e2416, 0xb41d6014, 0x42a59dcf, 0x50a3e1c0, 0xeedb5b79, 0xe0dd1788, 0x8ec3cd3f, 0x7109cb33, 0xf4144ec, 0x2cfb46f8, 0xad78bea5, 0xc981c8a4, 0x59bafe6c, 0x59bafe6c, 0x5f0bc16, 0xe9e7b217, 0xa5d92ddc, 0xa5d92ddc, 0x2656a589};
 
  uint32_t key[4] = {0,0,0,0};
 
  for(i = 0; i < num_rounds; i++) {
    key[(sum >> 11) & 3] = l[63 - i*2] - sum;
    sum -= delta;
    key[sum & 3] = l[62 - i*2] - sum;
  }

  printf("%08x %08x %08x %08x\n", key[3], key[2], key[1], key[0]);

  return 0;
}


出力結果をasciiにし、エンディアンを直したら、}!pu_gnimr4w{pxhとなりました。
これを逆にして、hxp{w4rming_up!}

bad_apple - Crypt -  15[pt]
問題文:
Baby's 1st

ctf.link/assets/downloads/cry/bad_apple.tar.xz

try:
    ncat 1.ctf.link 1027 < good.bin
expect:
    "hello"
xzにはgood.binとbad_apple.pyが含まれていました。
以下がbad_apple.pyです:
#!/usr/bin/env python3
import sys, binascii
from Crypto.Hash import SHA256

key = open('key.bin', 'rb').read()

message = sys.stdin.buffer.read(0x100)
if len(message) < SHA256.digest_size:
  print('len')
  exit(0)

tag, message = message[:SHA256.digest_size], message[SHA256.digest_size:]

if SHA256.new(key + message).digest() != tag:
  print('bad')
  exit(0)

if b'hello pls' in message:
  print('hello')
elif b'flag pls' in message:
  print(open('flag.txt', 'r').read())
送信されたファイルをハッシュ(tag)と文字(message)に分け、sha256(key + message)とtagの値が等しいことを確認します。
さらにmessageが"flag pls"ならフラグが取得できます。
不明な文字を足してハッシュを取る、というプログラムの時点で頭の中はhashpumpだけだったので、hashpumpを使う方向で解きました。
さて、good.binの中の前半のハッシュは「2628455f6617ecea024895a48578ebce00fa9204983d09b6d1757d05dc430567」で、文字は「hello pls」でした。
これを元に、flag plsを付け足したハッシュを作ります。
keyの長さが分からなかったのでpythonでループしました:
import commands
import binascii
i = 32
for i in range(1, 1024):
    ret = commands.getoutput("hashpump -s 2628455f6617ecea024895a48578ebce00fa9204983d09b6d1757d05dc430567 -d \"hello pls\" -a \"flag pls\" -k " + str(i))
    get = ret.split("\n")
    _hash_ = binascii.unhexlify(get[0])
    data = get[1].decode('string_escape')
    buf = _hash_ + data
    f = open("data", "wb")
    f.write(buf)
    f.close()
    ret = commands.getoutput("nc 1.ctf.link 1027 < data")
    print(str(i) + " : " + ret)
    if ret == "len" : continue
    if ret == "bad" : continue
    if ret == "hello" : continue
    break
32回目でフラグが返ってきましたとさ...。
hxp{M3rkL3_D4mg4rd_h4s_s0m3_Pr0bl3mZ}

全体を通して難しかったです。Web30とRev10とExploit10が解けなかったのが非常に悔しいです。あとは上級者向けが多く、1人〜3人程度しか解いていない問題ばかりで、難易度がすごい分かれていました。
もしかしたら暗号が向いているのかなぁ...

CTFで思ったのは、まず、Reconを予告しておきながら、最後の最後で0[pt]の答え無しのページにした運営に驚きました。あと、web20の問題の答えが後半でIRCに書き込まれたせいか、20[pt]にも関わらず145人もの人が後半でこのポイントを獲得しています。

ちょっと難しかったですが、解けたときは楽しかったです。

0 件のコメント:

コメントを投稿