CSAW CTF Qualification Round 2017 Writeup
開催期間(JST)
09/16 AM5:00 ~ 09/18 AM5:00
結果
・チーム名:wabisabi
・得点:851 pt
・順位:得点したチーム中,231/1444
解いた問題
・CVV (Misc100)
・tablEZ (Reversing100)
・Best Router (Forensics200)
・realism (Reversing400)
取り組んだが解けなかった問題
・pilot (Pwn75)
・Missed Registration (Forensics150)
・Gopherz (Reversing350)
はじめに
今回はEKOPARTYのほうと被っていて,一応両方登録はしたんですが,結局CSAWだけやってました.
土曜日は@kobadlveと@phustlyと集まって少しやっていて,日曜は家に引きこもってやってました.
去年のCSAWは576ptで252/1274だったので,一応去年より高い順位は取れたっぽい.
Writeup
CVV (Misc100)
ncで繋ぐと,こんな感じでいろいろな種類のクレジットカード番号を求められます.
$ nc misc.chal.csaw.io 8308 I need a new American Express! ^C $ nc misc.chal.csaw.io 8308 I need a new Visa!
クレジットカードのパターンは,'MasterCard','Discover','American','Visa'の4つ.
確か「面白くて眠れなくなる数学」とかいう本を以前読んだ時に,クレジットカード番号はとあるアルゴリズムによって決まっているってことを知って,つまりランダムに数字を入力すればいいというわけではなくて,そのアルゴリズムに沿った数字を入力する必要がありそうだなぁとか思いました.
各クレジットカードについてもカード番号の桁数が違っていたり,それぞれカード番号にルールが決まっていたりするので,そこら辺を気を付けながらスクリプトを書きます.
使われているアルゴリズムの名前はLuhnアルゴリズムというものらしく,どうせ実装してる人いるやろって思ったらWikipediaに載ってたり,いろいろな書き方で実装している人がいたので,それをお借りしました.
Luhnアルゴリズム(やってみた Python2.7) · GitHub
雑にスクリプトを書いて回してみたら,Visaのカード番号を毎回失敗するVisaが苦手なスクリプトが生まれてしまって,どうしようかなと思った結果,以下の任意のクレジットカード番号を生成する神サイトを見つけたので,適当に100個くらい生成して,それを使うようにしました.
あとは,何回か正解すると途中から出題が変わって,ある数字で始まるもの,終わるもの,与えられた番号がvalidかinvalidか返すもの,とかにそれぞれスクリプトを雑に対応させました.
与えられた番号がvalidかinvalidか返すやつは,Luhnアルゴリズム自体が全ての間違いを検出できるわけではないっぽくて,2回目で成功しました.
以下,書いたスクリプト.(めっちゃ汚いけど,解ければいいやろと思ってる)
Luhnアルゴリズムでvalidが返るまでランダムに数字を生成して,それを送るってことをやってる.1度使ったものは使えないっぽいけど,これだけ大きければほぼ被ることはないだろうということで,その対策はしてない.
#!/usr/bin/env python # coding: utf-8 import socket import random import itertools s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('misc.chal.csaw.io', 8308)) card_list = ['MasterCard', 'Discover', 'American', 'Visa'] # https://gist.github.com/oyakata/5955744 より def check_digits(text): """itertools.cycleを使って実装。""" def even_value(x): y = x * 2 return y - 9 if y > 9 else y values = (int(x) for x in reversed(text)) switcher = itertools.cycle((lambda x: x, even_value)) digits = (func(x) for func, x in itertools.izip(switcher, values)) return sum(digits) % 10 == 0 def main(): count = 0 f = open('visa.txt', 'r') visa = f.read().split('\n') f.close() while(True): data = s.recv(256) print data all_data = data.split(' ') card = all_data[4].replace('!','').rstrip() if card == 'card': card = all_data[8].replace('!', '').rstrip() print card if card == 'MasterCard': while True: num = '5' + str(random.randint(100000000000000, 999999999999999)) print num if check_digits(num): break elif card == 'Discover': while True: num = '60110' + str(random.randint(10000000000, 99999999999)) print num if check_digits(num): break elif card == 'American': while True: num = '34' + str(random.randint(1000000000000, 9999999999999)) print num if check_digits(num): break elif card == 'Visa': print visa[count] num = visa[count] elif card == 'if': card_num = all_data[5] if check_digits(card_num): num = '1' else: num = '0' else: info = all_data[6] if len(card) == 4 and info == "starts": while True: num = card + str(random.randint(100000000000, 999999999909)) print num if check_digits(num): break elif len(card) == 1 and info == "ends": while True: num = str(random.randint(100000000000000, 999999999999999)) + card print num if check_digits(num): break elif len(card) == 4 and info == "ends": while True: num = str(random.randint(100000000000, 999999999909)) + card print num if check_digits(num): break s.sendall(num + '\n') count = count + 1 if __name__ == '__main__': main()
$ python solve.py . . . if True Thanks! I need to know if 7358400188115005 is valid! (0 = No, 1 = Yes) if True Thanks! I need to know if 8664780687553453 is valid! (0 = No, 1 = Yes) if False Thanks! I need to know if 1514774928101638 is valid! (0 = No, 1 = Yes) if False Thanks! I need to know if 9017155594857850 is valid! (0 = No, 1 = Yes) if True Thanks! I need to know if 3591469017167338 is valid! (0 = No, 1 = Yes) if False Thanks! flag{ch3ck-exp3rian-dat3-b3for3-us3}
tablEZ (Reversing100)
一番最初に解いたやつ.
問題文
Bobby was talking about tables a bunch, so I made some table stuff. I think this is what he was talking about...
$ ./true Please enter the flag: hoge WRONG
crackme系のやつ.
確か,入力値を元にテーブルからindexを求めて,それを使って再度テーブルから値を取り出す,ってことをやっていた気がします.
以下,解いていた時のメモ.
gdb-peda$ x/80 0x555555755281 0x555555755281 <trans_tbl+1>: 0x056c04c4039b02bb 0x09450822072e064a 0x555555755291 <trans_tbl+17>: 0x0d060cd50bb80a33 0x117910fa0fbc0e0a 0x5555557552a1 <trans_tbl+33>: 0x15bf14b213e11224 0x1960188617ad162c 0x5555557552b1 <trans_tbl+49>: 0x1d591cd81bb61aa4 0x217720941f411e87 0x5555557552c1 <trans_tbl+65>: 0x256124cb234f22f0 0x292a289727c02625 0x5555557552d1 <trans_tbl+81>: 0x2d9f2cc92b082a5c 0x31f930cf2f4e2e43 0x5555557552e1 <trans_tbl+97>: 0x35e73465336f323e 0x39ef38b7373936c5 0x5555557552f1 <trans_tbl+113>: 0x3daa3c2f3bc83ad0 0x4181403c3f473ec7 0x555555755301 <trans_tbl+129>: 0x45a644d343494232 0x49404858472b4696 0x555555755311 <trans_tbl+145>: 0x4d1a4cee4b9c4af1 0x518050d64fc64e5b 0x555555755321 <trans_tbl+161>: 0x553d549a536d522d 0x59e05884579356a7 0x555555755331 <trans_tbl+177>: 0x5d095cb95b3b5a12 0x614860995fba5e69 0x555555755341 <trans_tbl+193>: 0x6582647c63b16273 0x69fb689d672766be 0x555555755351 <trans_tbl+209>: 0x6db36cf46b7e6a67 0x711b705f6fc26e05 0x555555755361 <trans_tbl+225>: 0x7511747173237254 0x796878a577d27630 0x555555755371 <trans_tbl+241>: 0x7d7a7cf57b3f7a9e 0x8185800c7f0b7ece 0x555555755381 <trans_tbl+257>: 0x858e845e836382de 0x89da886a87fe86bd 0x555555755391 <trans_tbl+273>: 0x8dac8ce88b888a26 0x91f690a88f628e03 0x5555557553a1 <trans_tbl+289>: 0x95c3946b937592f7 0x998f98e697519646 0x5555557553b1 <trans_tbl+305>: 0x9d919c5a9b769a28 0xa152a0449f1f9eec 0x5555557553c1 <trans_tbl+321>: 0xa53aa48ba3fca201 0xa910a816a7a3a6a1 0x5555557553d1 <trans_tbl+337>: 0xad95accaab50aa14 0xb10eb035af4bae92 0x5555557553e1 <trans_tbl+353>: 0xb55db41db320b2b5 0xb90fb86eb7e2b6c1 0x5555557553f1 <trans_tbl+369>: 0xbdd9bcd4bb90baed 0xc157c098bfddbe42 0x555555755401 <trans_tbl+385>: 0xc556c478c319c237 0xc904c8d1c774c6af 0x555555755411 <trans_tbl+401>: 0xcd4ccce5cb55ca29 0xd1dbd089cff2cea0 0x555555755421 <trans_tbl+417>: 0xd5ead483d338d2e4 0xd98cd8dcd707d617 0x555555755431 <trans_tbl+433>: 0xdde9dc7bdbb4da8a 0xe10de015dfebdeff 0x555555755441 <trans_tbl+449>: 0xe534e4f3e3a2e202 0xe913e8f8e718e6cc 0x555555755451 <trans_tbl+465>: 0xed21ecaeeb7fea8d 0xf170f04defcdeee3 0x555555755461 <trans_tbl+481>: 0xf572f4abf3fdf253 0xf9a9f866f71cf664 0x555555755471 <trans_tbl+497>: 0xfddffcd7fb1efab0 0xe0000031ff7dfe36 0x5555555549c1 <main+289>: cmp QWORD PTR [rbp-0xc8],0x25 FLAGは0x25文字(37文字) This is the flag. gdb-peda$ x/30 0x7fffffffcac0 0x7fffffffcac0: 0xb1e711 f59d73b327 0x30f4f9f9b399beb3 0x7fffffffcad0: 0xb19965237399711b 0xf9279923be111165 0x7fffffffcae0: 0x000000 ce65059923
逆の処理を書くより,入力値を与えてテーブル作って,そこからflagを求めたほうが早そうなので,その方法でやりました.
string = "d0efb739c5e7656f3ef999cef53b12e08493a73d9a6d2d80d6c65b1aee9cf140582b96a6d349323f9e68a5d230117123541b5fc205b3f47e67fb9d27be827cb173" char = (('a'..'z').to_a.join + ('A'..'Z').to_a.join + '{}_' + ('0'..'9').to_a.join) list1 = string.scan(/.{1,2}/).reverse list2 = char.scan(/.{1,1}/) table = {} list1.zip(list2).each do |val1, val2| table.store(val1, val2) end flag_str = "ce65059923f9279923be111165b19965237399711b30f4f9f9b399beb3b1e711f59d73b327" flag_list = flag_str.scan(/.{1,2}/).reverse flag = "" flag_list.each do |val| flag = flag + table[val].to_s end puts flag
$ ruby solve.rb flag{t4ble_l00kups_ar3_b3tter_f0r_m3}
Best Router (Forensics200)
問題文
http://forensics.chal.csaw.io:3287 NOTE: This will expand to ~16GB!
解いているチームが多かったのでできそうだなぁとは思ってたのですが,16GBも容量がなかったので,土曜家に帰ってきてからDesktopのほうで解いたやつ.
与えられたbest_router.tar.gzを解凍すると,14.5GBのイメージファイルが出てきます.
FTK Imagerで解けそうなので,開いてみる.
いろいろありそうだけど,問題文を思い出してみる.
ログインフォームのURLが与えられているので,これにログインできるとflagが出そう.
/var/wwwが怪しそうなので見てみるとビンゴ.
flag.txtは空でここからは見れないので,やっぱり与えられたURLにログインする必要がありそう.
username:admin psassword:iforgotaboutthemathtest
でログインするとflagが貰える.
realism (Reversing400)
高得点の解きたいなぁと思って,rev問見てたら数十人が解いていたのでやってみた.
問題文
Did you know that x86 is really old? I found a really old Master Boot Record that I thought was quite interesting! At least, I think it's really old... qemu-system-i386 -drive format=raw,file=main.bin
QEMU初めて使った.
与えられたmain.binはブートセクタ.
与えれらたコマンド通りにmain.binを起動してみると,以下のような画面が出てきて,またこれもcrackme系の問題.
適当に入力すると,WRONG FLAG.
ブートセクタは512バイトで読むべきコード自体は少ないのですが,どう解析したらいいかわからなくて困った.
ググってたらndisasm
でできるよ,みたいなのを見つけて逆アセンブルしたらそれっぽく出てきたけど,やっぱり動的解析しないと厳しそう.
めっちゃ調べていろいろやってみたら,以下のようにして解析できた.
$ qemu-system-i386 -drive format=raw,file=main.bin -S -gdb tcp::1234
別terminalにてgdbを起動して,set architecture
,target remote
でそれぞれ指定.
ブートセクタは0x7C00にロードされるらしいので,そこにブレークポイントをしかける.
$ gdb gdb-peda$ set architecture i8086 gdb-peda$ target remote localhost:1234 gdb-peda$ b *0x7c00
pedaのプロンプトは出ているのに使えなかったので,ノーマルなgdbでノーマルなコマンドで解析する.
最初の4文字が"flag"かどうかチェックしている部分.入力値の長さのチェックはこれの前にある.
0x7c6f: cmp DWORD PTR ds:0x1234,0x67616c66 0x7c78: jne 0x7d4d
その後,入力値チェックがある.あとはここを読んで求解処理を書くだけ.
0x7c7c: movaps xmm0,XMMWORD PTR ds:0x1238 0x7c81: movaps xmm5,XMMWORD PTR ds:0x7c00 0x7c86: pshufd xmm0,xmm0,0x1e 0x7c8b: mov si,0x8 0x7c8e: movaps xmm2,xmm0 0x7c91: andps xmm2,XMMWORD PTR [si+0x7d90] 0x7c96: psadbw xmm5,xmm2 0x7c9a: movaps XMMWORD PTR ds:0x1268,xmm5 0x7c9f: mov di,WORD PTR ds:0x1268 0x7ca3: shl edi,0x10 0x7ca7: mov di,WORD PTR ds:0x1270 0x7cab: mov dx,si 0x7cad: dec dx 0x7cae: add dx,dx 0x7cb0: add dx,dx 0x7cb2: cmp edi,DWORD PTR [edx+0x7da8] 0x7cba: jne 0x7d4d 0x7cbe: dec si 0x7cbf: test si,si # If si is 0, finish. 0x7cc1: jne 0x7c8e
8回ループして入力値チェックしている.間違っていたら即Wrong.
最初にxmm0レジスタに読み込んだ入力値をpshufd
命令でシャッフルしている.
0x7c86: pshufd xmm0,xmm0,0x1e
このpshufd
命令までが初期化っぽい.
その後,andps
命令でシャッフルされた入力値をマスクする.(16バイトの内,ある2バイトを0にする.この0にする位置は徐々にずれていく)
0x7c91: andps xmm2,XMMWORD PTR [si+0x7d90]
そして,マスクした入力値とxmm5レジスタを使ってpsadbw
命令で差の絶対値を取ったり足したりする.(このxmm5レジスタの値は初期値だけ決まっていて,あとはpsadbwで求まった値を次の計算でも使う)
0x7c96: psadbw xmm5,xmm2
psadbw命令は以下がわかりやすい.
http://www.officedaytime.com/tips/simdimg/si.php?f=psadbw
その後,求まった値を使って8バイトの数値を作って,それが特定の値と等しいかどうかチェックしている.
1回目と2回目で各命令を実行したときのレジスタの値とかを逐一調べてメモしてた.
2回目の値も調べるために,バイナリ本体にパッチを当てちゃうと便利.
この部分を
0x7cba: jne 0x7d4d
以下のように書き換えて,オペコードをjeにする.
解いていた時のメモ.
Initialize ======================================================================================= => 0x7c6f: cmp DWORD PTR ds:0x1234,0x67616c66 0x7c78: jne 0x7d4d 0x7c7c: movaps xmm0,XMMWORD PTR ds:0x1238 gdb-peda$ p $xmm0 $2 = { v4_float = {12.0784864, 12.0784311, 12.0784311, 1.60549888e+37}, v2_double = {2261634.5098039485, 2.2040338477057924e+295}, v16_int8 = {0x7b, 0x41 <repeats 14 times>, 0x7d}, v8_int16 = {0x417b, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x7d41}, v4_int32 = {0x4141417b, 0x41414141, 0x41414141, 0x7d414141}, v2_int64 = {0x414141414141417b, 0x7d41414141414141}, uint128 = 0x7d41414141414141414141414141417b } 0x7c81: movaps xmm5,XMMWORD PTR ds:0x7c00 gdb-peda$ p $xmm5 $3 = { v4_float = {-134298496, -2.50091934, -1.48039995e-36, 1.93815862e-18}, v2_double = {-8.0294250547975565, 1.241726856953559e-144}, v16_int8 = {0xb8, 0x13, 0x0, 0xcd, 0x10, 0xf, 0x20, 0xc0, 0x83, 0xe0, 0xfb, 0x83, 0xc8, 0x2, 0xf, 0x22}, v8_int16 = {0x13b8, 0xcd00, 0xf10, 0xc020, 0xe083, 0x83fb, 0x2c8, 0x220f}, v4_int32 = {0xcd0013b8, 0xc0200f10, 0x83fbe083, 0x220f02c8}, v2_int64 = {0xc0200f10cd0013b8, 0x220f02c883fbe083}, uint128 = 0x220f02c883fbe083c0200f10cd0013b8 } 0x7c86: pshufd xmm0,xmm0,0x1e gdb-peda$ p $xmm0 $4 = { v4_float = {12.0784311, 1.60549888e+37, 12.0784311, 12.0784864}, v2_double = {2.2040338477057924e+295, 2261750.5098039214}, v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, v8_int16 = {0x4141, 0x4141, 0x4141, 0x7d41, 0x4141, 0x4141, 0x417b, 0x4141}, v4_int32 = {0x41414141, 0x7d414141, 0x41414141, 0x4141417b}, v2_int64 = {0x7d41414141414141, 0x4141417b41414141}, uint128 = 0x4141417b414141417d41414141414141 } 0x7c8b: mov si,0x8 ======================================================================================= Loop ======================================================================================= 0x7c8e: movaps xmm2,xmm0 gdb-peda$ p $xmm2 $6 = { v4_float = {12.0784311, 1.60549888e+37, 12.0784311, 12.0784864}, v2_double = {2.2040338477057924e+295, 2261750.5098039214}, v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, v8_int16 = {0x4141, 0x4141, 0x4141, 0x7d41, 0x4141, 0x4141, 0x417b, 0x4141}, v4_int32 = {0x41414141, 0x7d414141, 0x41414141, 0x4141417b}, v2_int64 = {0x7d41414141414141, 0x4141417b41414141}, uint128 = 0x4141417b414141417d41414141414141 } 0x7c91: andps xmm2,XMMWORD PTR [si+0x7d90] # first, si is 0x8. second, si is 0x7. 0 の位置が徐々にずれていく. First: gdb-peda$ x/30wx $si+0x7d90 0x7d98: 0xffffff00 0xffffffff 0xffffff00 0xffffffff 0x7da8: 0x02110270 0x02290255 0x025e0291 0x01f90233 0x7db8: 0x027b0278 0x02090221 0x0290025d 0x02df028f 0x7dc8: 0x00000014 0x00000000 0x00000000 0x00000000 0x7dd8: 0x00000000 0x00000000 0x00000000 0x00000000 0x7de8: 0x00000000 0x00000000 0x00000000 0x00000000 0x7df8: 0x00000000 0xaa550000 0x00000000 0x00000000 0x7e08: 0x00000000 0x00000000 Second: gdb-peda$ x/20wx $si+0x7d90 0x7d97: 0xffff00ff 0xffffffff 0xffff00ff 0xffffffff 0x7da7: 0x110270ff 0x29025502 0x5e029102 0xf9023302 0x7db7: 0x7b027801 0x09022102 0x90025d02 0xdf028f02 0x7dc7: 0x00001402 0x00000000 0x00000000 0x00000000 0x7dd7: 0x00000000 0x00000000 0x00000000 0x00000000 First: gdb-peda$ p $xmm2 $7 = { v4_float = {12.0783691, 1.60549888e+37, 12.0783691, 12.0784864}, v2_double = {2.2040338477057629e+295, 2261750.5098038912}, v16_int8 = {0x0, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x0, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, v8_int16 = {0x4100, 0x4141, 0x4141, 0x7d41, 0x4100, 0x4141, 0x417b, 0x4141}, v4_int32 = {0x41414100, 0x7d414141, 0x41414100, 0x4141417b}, v2_int64 = {0x7d41414141414100, 0x4141417b41414100}, uint128 = 0x4141417b414141007d41414141414100 } Second: gdb-peda$ p $xmm2 $6 = { v4_float = {12.062562, 1.60549888e+37, 12.062562, 12.0784864}, v2_double = {2.2040338476982412e+295, 2261750.5097961728}, v16_int8 = {0x41, 0x0, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x0, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, v8_int16 = {0x41, 0x4141, 0x4141, 0x7d41, 0x41, 0x4141, 0x417b, 0x4141}, v4_int32 = {0x41410041, 0x7d414141, 0x41410041, 0x4141417b}, v2_int64 = {0x7d41414141410041, 0x4141417b41410041}, uint128 = 0x4141417b414100417d41414141410041 } 0x7c96: psadbw xmm5,xmm2 First: gdb-peda$ p $xmm5 $9 = { v4_float = {8.88423226e-43, 0, 1.06919073e-42, 0}, v2_double = {3.1323761946335031e-321, 3.7697208777687111e-321}, v16_int8 = {0x7a, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfb, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v8_int16 = {0x27a, 0x0, 0x0, 0x0, 0x2fb, 0x0, 0x0, 0x0}, v4_int32 = {0x27a, 0x0, 0x2fb, 0x0}, v2_int64 = {0x27a, 0x2fb}, uint128 = 0x00000000000002fb000000000000027a } ==================== >>> hex(0x22 - 0x41) '-0x1f' >>> hex(0x0f - 0x41) '-0x32' >>> hex(0x02 - 0x41) '-0x3f' >>> hex(0xc8 - 0x7b) '0x4d' >>> hex(0x83 - 0x41) '0x42' >>> hex(0xfb - 0x41) '0xba' >>> hex(0xe0 - 0x41) '0x9f' >>> hex(0x83 - 0x00) '0x83' >>> hex(0x1f + 0x32 + 0x3f + 0x4d + 0x42 + 0xba + 0x9f + 0x83) '0x2fb' ==================== Second: gdb-peda$ p $xmm5 $7 = { v4_float = {7.13260918e-43, 0, 8.91225823e-43, 0}, v2_double = {2.5147941373319449e-321, 3.142257507550328e-321}, v16_int8 = {0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v8_int16 = {0x1fd, 0x0, 0x0, 0x0, 0x27c, 0x0, 0x0, 0x0}, v4_int32 = {0x1fd, 0x0, 0x27c, 0x0}, v2_int64 = {0x1fd, 0x27c}, uint128 = 0x000000000000027c00000000000001fd } 0x7c9a: movaps XMMWORD PTR ds:0x1268,xmm5 0x7c9f: mov di,WORD PTR ds:0x1268 First: gdb-peda$ p $edi $10 = 0x27a Second: gdb-peda$ p $di $9 = 0x1fd 0x7ca3: shl edi,0x10 First: gdb-peda$ p $edi $11 = 0x27a0000 Second: gdb-peda$ p $edi $11 = 0x1fd0000 0x7ca7: mov di,WORD PTR ds:0x1270 First: gdb-peda$ p $edi $12 = 0x27a02fb gdb-peda$ p $si $13 = 0x8 Second: gdb-peda$ p $edi $12 = 0x1fd027c gdb-peda$ p $si $13 = 0x7 0x7cab: mov dx,si 0x7cad: dec dx gdb-peda$ p $dx $14 = 0x7 ------------------------------ # 単純にdxの値を4倍している 0x7cae: add dx,dx First: gdb-peda$ p $dx # 7 + 7 $15 = 0xe Second: gdb-peda$ p $dx # 6 + 6 $15 = 0xc 0x7cb0: add dx,dx First: gdb-peda$ p $dx $16 = 0x1c Second: gdb-peda$ p $dx $16 = 0x18 ------------------------------ 0x7cb2: cmp edi,DWORD PTR [edx+0x7da8] First: gdb-peda$ x/10 $edx+0x7da8 0x7dc4: 0x02df028f 0x00000014 0x00000000 0x00000000 0x7dd4: 0x00000000 0x00000000 0x00000000 0x00000000 0x7de4: 0x00000000 0x00000000 Second: gdb-peda$ x/10 $edx+0x7da8 0x7dc0: 0x0290025d 0x02df028f 0x00000014 0x00000000 0x7dd0: 0x00000000 0x00000000 0x00000000 0x00000000 0x7de0: 0x00000000 0x00000000 0x7cba: jne 0x7d4d # If input is not correct, return. 0x7cbe: dec si gdb-peda$ p $si $2 = 0x7 0x7cbf: test si,si # If si is 0, finish. 0x7cc1: jne 0x7c8e ======================================================================================= This is the flag!!!!!!!!!!!!!!!!!!!!! Just do it!!!!!!!!!! 0x02df028f 0x0290025d 0x02090221 0x027b0278 0x01f90233 0x025e0291 0x02290255 0x02110270
psadbw
のところが不可逆っぽいので,逆算する処理ではなくz3を使ってこの処理を満たすような入力値を求める.
z3の使い方がわからず,BitVecsで値を生成してそれにabs()使おうとしたらできなくて,他にもいろいろ苦戦して,ようやくスクリプトが書けました.
(かなり汚いです)
#!/usr/bin/env python from z3 import * # 0x20 <= si <= 7e # # [flag] # 0x02df028f # 0x0290025d # 0x02090221 # 0x027b0278 # 0x01f90233 # 0x025e0291 # 0x02290255 # 0x02110270 # # {s0s1s2s3s4s5s6s7s8s9s10s11s12s13} # # s7s8s9s10s11s12s13}s3s4s5s6{s0s1s2 # s15 s14 # 0x** ** ** 7b ** ** ** ** ** 7d ** ** ** ** ** ** ** (little endian) def get_abs(x): return If(x >= 0,x,-x) def main(): solver = Solver() s = [Int('s_%d' % i) for i in range(14)] for i in range(len(s)): solver.add(s[i] != 0) solver.add(0x20 <= s[i], s[i] <= 0x7e) s14 = 0x7b s15 = 0x7d t0 = 0x22 t1 = 0x0f t2 = 0x02 t3 = 0xc8 t4 = 0x83 t5 = 0xfb t6 = 0xe0 t7 = 0x83 t8 = 0xc0 t9 = 0x20 t10 = 0x0f t11 = 0x10 t12 = 0xcd t13 = 0x00 t14 = 0x13 t15 = 0xb8 psadbw1 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - 0x00) psadbw2 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - 0x00) solver.add(psadbw1 == 0x28f) solver.add(psadbw2 == 0x2df) t0 = 0x0 t1 = 0x0 t2 = 0x0 t3 = 0x0 t4 = 0x0 t5 = 0x0 t6 = 0x02 t7 = 0x8f t8 = 0x0 t9 = 0x0 t10 = 0x0 t11 = 0x0 t12 = 0x0 t13 = 0x0 t14 = 0x02 t15 = 0xdf psadbw3 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - 0x00) + get_abs(t7 - s[3]) psadbw4 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - 0x00) + get_abs(t15 - s[7]) solver.add(psadbw3 == 0x25d) solver.add(psadbw4 == 0x290) t7 = 0x5d t15 = 0x90 psadbw6 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - 0x00) + get_abs(t6 - s[4]) + get_abs(t7 - s[3]) psadbw7 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - 0x00) + get_abs(t14 - s[8]) + get_abs(t15 - s[7]) solver.add(psadbw6 == 0x221) solver.add(psadbw7 == 0x209) t7 = 0x21 t15 = 0x09 psadbw8 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - 0x00) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3]) psadbw9 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - 0x00) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7]) solver.add(psadbw8 == 0x278) solver.add(psadbw9 == 0x27b) t7 = 0x78 t15 = 0x7b psadbw10 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - 0x00) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3]) psadbw11 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - 0x00) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7]) solver.add(psadbw10 == 0x233) solver.add(psadbw11 == 0x1f9) t7 = 0x33 t14 = 0x01 t15 = 0xf9 psadbw12 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - 0x00) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3]) psadbw13 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - 0x00) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7]) solver.add(psadbw12 == 0x291) solver.add(psadbw13 == 0x25e) t7 = 0x91 t14 = 0x02 t15 = 0x5e psadbw14 = get_abs(t0 - s[2]) + get_abs(t1 - 0x00) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3]) psadbw15 = get_abs(t8 - s15) + get_abs(t9 - 0x00) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7]) solver.add(psadbw14 == 0x255) solver.add(psadbw15 == 0x229) t7 = 0x55 t15 = 0x29 psadbw16 = get_abs(t0 - 0x00) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3]) psadbw17 = get_abs(t8 - 0x00) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7]) solver.add(psadbw16 == 0x270) solver.add(psadbw17 == 0x211) t7 = 0x70 t15 = 0x11 if solver.check() == sat: m = solver.model() print m flag = '' for i in s: flag += chr(m[i].as_long()) print "\nflag: flag{%s}" % flag else: print "Not found." if __name__ == '__main__': main()
$ python solve.py [s_0 = 52, s_6 = 95, s_2 = 51, s_3 = 97, s_4 = 108, s_10 = 51, s_11 = 95, s_8 = 48, s_7 = 109, s_9 = 100, s_5 = 122, s_12 = 121, s_13 = 48, s_1 = 114] flag: flag{4r3alz_m0d3_y0}
まとめ
z3の書き方,よくわからない部分が多い.
解けなかった問題に関しては,
pilot (Pwn75):オーバーフローさせてシェルコード実行させようとしたらできなくて,gdbで見てみたらシステムコールを呼ぶ直前でコードが変わるという謎の現象が起きたりしてた.よくわからん.
Missed Registration (Forensics150):nの値とxの値 is 何.末尾に何か付いてたけどわからん.
Gopherz (Reversing350):Gopherというプロトコルを初めて知った.なんの成果も得られませんでした.