Profile

youngsouk

youngsouk

house_of_orange (1)

house of orange의 핵심은

1. 먼저 top chunk의 크기를 본래 크기보다 속여서 크기가 크고 free된 청크를 만들어냅니다.

2. 그리고 가짜 _IO_FILE_PLUS와  _IO_wide_data 구조체를 만듭니다.

3. _IO_list_all을 가짜 구조체의 포인터가 있는 주소로 덮어씁니다.

4. 메모리 손상을 일으킵니다.

5. 그럼 _IO_flush_all_lockup()을 호출하게 됩니다. 그런데 이것은 3번에 의해 변경된 값을 참조하게 됩니다.

6. _IO_OVERFLOW()를 호출하게 되는데 이 주소값은 우리가 2번 과정으로 변조한 값이 됩니다. (ex : one_gadget으로 덮을시에 쉘)

 

처음 보시면 잘 이해가 안되실 수 있습니다. 고로 첫번째부터 차근 차근 알려드리겠습니다. 우선 top chunk의 크기를 속여야 하는데 그 이유는 크기가 크고 free된 청크를 얻기 위해서입니다. 이것을 위해서 단순히 크게 할당했다가 free()시키면 되지 않나?라고 생각하실 수 있는데 이렇게 하는 이유는 malloc()의 인자로 주는 크기가 제한된어있는 경우가 있기때문입니다.

 

이제 구체적인 원리를 설명해보겠습니다. 이 1번 과정에서 핵심은 top chunk의 크기를 작게 수정하여 예전의 탑청크를 free시킨다. 입니다. 그 이유는 sysmalloc()의 코드를 보면 알 수 있습니다. 

2019/08/30 - [힙(heap)/glibc] - sysmalloc() 분석(코드 분석&&순서도)

 

sysmalloc() 분석(코드 분석&&순서도)

/* ----------- 시스템 할당을 다루기 위한 함수이다. -------------- */ /* sysmalloc은 시스템으로부터 더 많은 메모리를 요구하는 경우(즉 top chunk의 크기가 부족할 때) 실행된다. 시작부분에, av->top(top c..

youngsouk-hack.tistory.com

여기서 상세한 코드 분석과 순서도를 보실 수 있습니다. 다만 다 보는 것이 좋지만 이 기법은 아래의 과정을 실행하기 때문에 

이 부분만 보시고 와도 충분합니다.

첫번째 목표를 달성하기 위해 실행해야되는 부분은

1. 위의 순서도 부분입니다.

알아야 하는 지식은

1.  malloc() =  __libc_malloc()입니다.

2. __libc_malloc()안에서 _int_malloc()을 호출하게 됩니다. 근데 이 int_malloc()에서 top chunk를 분할해서 요청을 처리하기에 top chunk의 크기가 부족할 때 sysmalloc()이 호출이 됩니다. (즉 요구한 바이트 수 > top chunk의 크기)

 

위의 순서도에 있는 과정은 malloc() 외부에서 sbrk()나 brk()가 호출되어 데이터 영역이 넓어졌을 때 입니다. 즉 우리의 첫번째 목표는 저 과정에 도달하는 것(malloc외부에서 데이터 영역이 넓어졌다고 속이는 것)입니다. 

 

우리는 이것을 위해 top chunk의 크기를 수정을 할 것인데 우선 정상적인 힙 구조를 보여드리겠습니다. 

malloc(8)을 했을 때 정상적인 힙구조라면 이렇게 됩니다. 하지만 우리는 여기서 이 top chunk의 크기를 작게 조정할 것입니다. 조정을 하게 되면 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
        char *p1_chunk = malloc(8) - 2 * 8; 
        // 2 * 8을 빼주는 이유는 malloc()이 반환하는 주소가 chunk의 fd부분이기 때문에 계산하기가 불편하기 때문입니다.
        // 만약 32bit라면 2 * 4 만큼 빼주면 구할 수 있습니다.
        size_t*  top = p1_chunk + 32;
        //gdb를 통해 본 첫번쨰 청크의 크기가 33이였으나 prev_inuse_bit를 제외하면 32가 됩니다.
        //따라서 이런 방식으로 계산하면 top chunk의 주소를 구할 수 있습니다.
        top[1] = 0xfe1;
        /* 
        	가장 중요한 부분인데 top chunk의 크기를 수정할 때 top chunk와 이전의 청크의 크기를 더했을 때
            0x1000의 배수가 되어야합니다. 왜냐하면 처음 힙을 할당할 때 page 크기단위로 할당하고 주소를 정렬
            하게 되는데 이 페이지 크기가 보통 0x1000 입니다. 만약 크기를 더했을 때 0x1000 의 배수가 되지 않는다면
            다음 할당에 오류가 발생하게 됩니다
            
            또한 prev_inuse_bit를 고려해주어야 합니다. 왜냐하면 top chunk는 다른 청크와 병합되는 것을
            막기 위해 prev_inuse_bit를 항상 셋팅해주기 때문입니다.
        */
}

이 코드를 컴파일하고 gdb로 수정한 뒤의 코드까지 실행한 뒤 힙을 보게되면 

이렇게 성공적으로 top chunk의 크기가 수정 된 것을 볼 수 있습니다. 이제 top chunk의 크기를 초과하는 크기로 malloc()를 호출하게 되면 위의 순서도의 과정이 수행되면서 sbrk()를 호출하게되면서 확장이 되고, 2개의 2 * SIZE_SZ 크기의 울타리 청크가 생겨날 것입니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
        char *p1_chunk = malloc(8) - 2 * 8;
        size_t*  top = p1_chunk + 32;
        top[1] = 0xfe1;

        malloc(0x1000); // 0x1000 > 0xfe0
}

우리가 예상한 결과가 맞는지 확인하기 위해서 컴파일 후 gdb로 보게 되면 

이렇게 우리가 예상 한대로 힙이(0x645000으로) 확장되면서 0xfc0(4032)바이트라는 크기를 가진 free chunk가 완성이 되었습니다. 이로써 house of orange의 첫번째 단계를 무사히 끝마쳤습니다! (grin)