Boston Key Party CTF 2015 Writeup
英語読めないしpythonかけないし辛かったCTFです。
チームとしては620点117位でした。最後の一問コミットしてれば二桁。。。
School Bus 25 Museum of Fine Arts
Because cryptography is hard, we only implemented a hand-made PRNG. What could possibly go wrong? : 25
Level4
http://52.10.107.64:8004/
参加が遅かったため、殆どのWeb問がやぎはしゅに掃除されてたなか残ってた奴。
<html> <head> <title>level4</title> <link rel='stylesheet' href='style.css' type='text/css'> </head> <body> <?php session_start(); require 'flag.php'; if (isset ($_GET['password'])) { if ($_GET['password'] == $_SESSION['password']) die ('Flag: '.$flag); else print '<p class="alert">Wrong guess.</p>'; } // Unpredictable seed mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000)); ?> <section class="login"> <div class="title"> <a href="./index.txt">Level 4</a> </div> <ul class="list"> <?php for ($i=0; $i<3; $i++) print '<li>' . mt_rand (0, 0xffffff) . '</li>'; $_SESSION['password'] = mt_rand (0, 0xffffff); ?> </ul> <form method="get"> <input type="text" required name="password" placeholder="Next number" /><br/> <input type="submit"/> </form> </section> </body> </html>
アッはいSESSION初期化してませんね。
てわけで、/?password=にアクセスして終了。
Burp立ち上げた意味……。
School Bus 100 Heath Street
During my time at KGB I learned how to hide all the stuff from alpha-dog. But damn it, I somehow lost some of the most important files...
ext4のファイルが落ちてくるのでとりあえずautopsy使ってバラす。
そうすると、大量のドキュメントが。。。
secret32 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 96 44 不明 rrw-r--r-- /img_sec/secret32 secret33 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 115 45 不明 rrw-r--r-- /img_sec/secret33 secret34 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 144 46 不明 rrw-r--r-- /img_sec/secret34 secret35 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 116 47 不明 rrw-r--r-- /img_sec/secret35 secret36 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 140 48 不明 rrw-r--r-- /img_sec/secret36 secret37 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 115 49 不明 rrw-r--r-- /img_sec/secret37 secret38 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 119 50 不明 rrw-r--r-- /img_sec/secret38 secret39 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 115 51 不明 rrw-r--r-- /img_sec/secret39 secret40 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 131 52 不明 rrw-r--r-- /img_sec/secret40 secret41 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 109 53 不明 rrw-r--r-- /img_sec/secret41 secret42 r 2015-02-06 19:25:33 JST 0000-00-00 00:00:00 2015-02-06 19:25:33 JST 110 54 不明 rrw-r--r-- /img_sec/secret42
で、この中にパスワードのかかったZIPがあるのですが、パスワードが不明なので放置。
で、とりあえず王道の未割り当て領域へ。ここにも断片があったり。
ここを目grepしてたやぎはしゅがこんなファイルを見つける。
KGB_archでググると
http://jarp.does.notwork.org/diary/201210c.html#20121031
圧縮ファイルかよ!!!!!!
てことで解凍して終了。
ダミーテキストの中はスパイの事だったので、これもダミーかと見逃してましたが、まさかの圧縮ファイルだったという。。。
しかもforemostにかからないし。。。ぐぬぬぬ
School Bus 200 Riverside
omg tha NSA hacked my super secret login, I caught them exfillin this pcap, am I t3h fuxxed?
一緒についてたPCAPがUSBの通信だったため、とりあえず放置。
その後解くものが無くなったので見てみる。
Wireshark先生のお陰で俺にも読めるううううううう
http://www.linux-hardware-guide.com/ja/2014-12-07-logitech-m-bj58-mouse-optical-usb-3-button-wheel
こいつか。
さてどんな通信なんだろ……。
linux/drivers/hid/usbhid/usbmouse.c
input_report_key(dev, BTN_LEFT, data[0] & 0x01); input_report_key(dev, BTN_RIGHT, data[0] & 0x02); input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); input_report_key(dev, BTN_SIDE, data[0] & 0x08); input_report_key(dev, BTN_EXTRA, data[0] & 0x10); input_report_rel(dev, REL_X, data[1]); input_report_rel(dev, REL_Y, data[2]); input_report_rel(dev, REL_WHEEL, data[3]);
なるほどねー。てわけでその通りにやってみる。しかし謎の図形に。うーん……?イミフ。時間もアレだったので、とりあえずバラした結果だけJSON形式で投げて寝た。
翌日スレを見たら
ほよたか氏「それ相対座標じゃない?プロットしてみたよ」
んー……?
ごちゃごちゃした線が交差してるよく分からない画像に。
alcさん「クリックした座標だけ出してみれば?」
んんんーーー????
やぎはしゅ「それスクリーンキーボードじゃね?」
あっっっ
てことでこうなる
これを重ねて順にPlotさせて終了。因みに線は座標測定用の目安ですw
最後、flagをコミットする場所を間違えたりミスタイプがあったりしたのをイケメンパケリストことほよたか氏に救って頂いた。
流石イケメンパケリスト!!!
チーム全員が一丸となって解いた感ある問題でした。
Crypt 150 Wood Island
You can try to sign messages and send them to the server, 52.0.217.48 port 60231. Sign the right message and you'll get the flag! Only problem---you don't have the signing key. I will give you this, though: sigs.txt is a file containing a bunch of signatures. I hope it helps. (P.S. Don't try and send the exact signatures in that file---that's cheating!) : 150
時間内にコードが書けずに終わった未練の残る問題。解けたのに。。。。。。
とりあえず圧縮ファイルが落ちてきたので解凍。
distディレクトリに色々あるけどコアはこれ。
from dsa_prime import SAFEPRIME, GENERATOR from dsa_key import PUBKEY, SECKEY import hashlib def elgamal_verify(r, s, m): if r <= 0 or r >= SAFEPRIME: return False if s <= 0 or s >= SAFEPRIME-1: return False h = int(hashlib.sha384(m).hexdigest(), 16) left = pow(GENERATOR, h, SAFEPRIME) right = (pow(PUBKEY, r, SAFEPRIME) * pow(r, s, SAFEPRIME)) % SAFEPRIME return left == right DUPLICATES = [] def is_duplicate(s): return s in DUPLICATES import base64, SocketServer, os, sys, json class ServerHandler(SocketServer.BaseRequestHandler): def fail(self, message): self.request.sendall(message + "\nGood-bye.\n") self.request.close() return False def captcha(self): proof = base64.b64encode(os.urandom(9)) self.request.sendall(proof) test = self.request.recv(20) ha = hashlib.sha1() ha.update(test) if test[0:12]!=proof or not ha.digest().endswith('\xFF\xFF\xFF'): self.fail("You're a robot!") def handle(self): self.captcha() sig = self.request.recv(5000) sig = json.loads(sig) if "r" not in sig or "s" not in sig or "m" not in sig: self.request.close() return r = sig["r"] s = sig["s"] m = sig["m"] if not elgamal_verify(r, s, m): self.request.close() elif is_duplicate(sig): self.request.close() elif m != "There is no need to be upset": self.request.close() else: self.request.sendall(FLAG) self.request.close() class ThreadedServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): pass FLAG = "" if __name__ == "__main__": HOST = sys.argv[1] PORT = int(sys.argv[2]) FLAG = open('flag.txt', 'r').read() DUPLICATES = open('sigs.txt', 'r').read().split('\n')[:-1] DUPLICATES = map(json.loads, DUPLICATES) server = ThreadedServer((HOST, PORT), ServerHandler) server.allow_reuse_address = True server.serve_forever()
Elgamal署名かぁ。。。と思ってWikipediaに書いてある通り、int(hash(m))の重複を探してみる。
しかし無い。
うーん……。てかそういやどうしたらFLAGが出るんだっけ
if not elgamal_verify(r, s, m): self.request.close() elif is_duplicate(sig): self.request.close() elif m != "There is no need to be upset": self.request.close() else: self.request.sendall(FLAG) self.request.close()
……ん?
is_duplicate(sig)?
sig = self.request.recv(5000) sig = json.loads(sig) #--------------------# def is_duplicate(s): return s in DUPLICATES
ふぁーーーーーwwwwwwww
JSONの各要素じゃなくてオブジェクト自体を比較してるしwwww
脆弱性あるじゃねーーーーかwwwwww
てわけで、rms以外に余計なhoge要素を加えたJSONを返せばFLAGが出てくると分かる。
というわけでコード
from dsa_prime import SAFEPRIME, GENERATOR from dsa_key import PUBKEY, SECKEY import hashlib import base64, SocketServer, os, sys, json, socket import string import random s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("52.0.217.48", 60231)) proof = s.recv(12) print proof + "\n" text = proof ha = hashlib.sha1() ha.update(proof) while not ha.digest().endswith('\xFF\xFF\xFF'): ha = hashlib.sha1() text = proof+''.join([random.choice(string.ascii_letters + string.digits) for i in range(8)]) ha.update(text) print text s.sendall(text) s.sendall('{"s": 21054468908426331574045415757705596312490608697034679252162205415258689488938792588831531807614306762187107454355496991706280940746682086684651382392894440056488208110510117519357395297916485175485376815711109501315034875521798843436925869757482592549470778789601115554255653914305843242151026272506459011629018207321332425287985213417534380034990214393005755147720206544994968697500781616394595450665975848378431384407629728617806162081729861628808736537317534949224293348236319554315763862173514513324698536191701038439870012572806910098312290000388970881226934180445872436810927891008412644093329663435621581021347, "r": 14556450625812013575484254421723445809678777888922992010061032704679042904146923866457426159198384764425155312389074479869246038712459861363560250202380689697680368931395645962819850774655376322711776181621394119949333235937168169126299818515407603115358673337217251397556879636425925559398418798688723285385352425490135869490681971673314086977191132841003292451662521230410289882511683145128157815153694864096645608834771938938231775760394427067003660948668863059181615823395710566418746839898936118863944244851022473133036526007136492008659921439714025852802299228292891053541064974277813868083959677233126603099132, "m": "There is no need to be upset","hoge":1}') print "\nJSONs\n" flag = s.recv(1024) print flag
で、ランダムでchapcha飛ばしてるので通らない事もあるため、これを並列させて終了。
しかしコードを書いている間に試合終了。。。pythonでまともにコード書いたの、実は今回のCTFが始まってからだったので、色々詰まりまくって辛かった。。。(´・ω・`)
全体的には中々面白かったかなと思います。
ただWeb成分がもう少し欲しかったですね。
人権はいまだ確保出来ず。とりあえずpythonに慣れなきゃなぁ。