본문 바로가기

CTF_Write_UP/etc

[heap] 힙 영역을 ARABOZA

시작

안녕하세요 :D

7월 29일에 정보보안 산업기사 시험 접수가 있어서 그 때까지 이론 공부를 좀 하자!! 란 마음을 먹었었는데..

미뤄두었던 힙 영역을 공부하는데 너무너무 재밌는 거에요ㅠㅠㅠ

무작정 문제부터 풀 생각을 했더니 뭔 소린지도 모르겠고 더 헷갈리기만 해서

이 글로 힙 영역의 메모리 구조를 정리해볼까 합니다.

Heap 취약점?

힙 영역은 아시다시피 동적 할당을 할 때 사용되는 공간이에요.

스택을 익스때릴 땐 우리가 원하는대로 RET을 돌리거나 EIP의 흐름을 수정하는 것이 가능했죠?

코드의 진행에 필요한 부분들이 스택에 push pop 되기 때문에 가능했습니다.

하지만 힙 익스는 조금 달라요! 동적 할당을 위해 사용되는 영역이니 만큼 직접적으로 RET이나 레지스터를 건들 수 없어요.

때문에 여러 포인터들을 똑똑하게 이용해서 함수의 got를 덮어버리거나 free된 주소를 다시 가리키는 등.. 다양한 방법이 존재합니다.

오늘은 간단하게 힙 청크의 구조를 알아보도록 해요.

Heap 구조

힙이 할당되면 다음과 같은 구조를 가집니다.


    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Size of previous chunk, if unallocated (P clear)  |

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Size of chunk, in bytes                     |A|M|P|

      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             User data starts here...                          .

            .                                                               .

            .             (malloc_usable_size() bytes)                      .

            .                                                               |

nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             (size of chunk, but used for application data)    |

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Size of next chunk, in bytes                |A|0|1|

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

free()를 하면 아래와 같이 바뀌어요.


    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Size of previous chunk, if unallocated (P clear)  |

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    `head:' |             Size of chunk, in bytes                     |A|0|P|

      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Forward pointer to next chunk in list             |

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Back pointer to previous chunk in list            |

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Unused space (may be 0 bytes long)                .

            .                                                               .

            .                                                               |

nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    `foot:' |             Size of chunk, in bytes                           |

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            |             Size of next chunk, in bytes                |A|0|0|

            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

(출처 : https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/malloc_chunk.html)

차이점이 보이시나요??

Data의 앞 부분이 FD / BK로 바뀝니다.

힙 영역에선 bin이라고 하는 포인터 리스트가 존재하는데, 이는 free된 청크들이 재사용될 때 빠르게 할당히 가능하도록 종류 별로 청크들을 가리키고 있습니다.

fastbin을 제외한 bin들은 Double linked list로 구성됩니다. (fastbin은 single linked list!!)

bin과 관련된 내용들은 나중에 Double Free Bug 문제를 풀 때 한 번 더 다뤄보도록 하겠습니다.

오늘은 위와 같은 힙을 실제로 보고 건드려보는 것을 목표로 해봐요!!

해보기

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

int main(int argc, char *argv[]) {

        char *buf1 = (char*)malloc(0x20);
        char *buf2 = (char*)malloc(0x20);
        char *buf3 = (char*)malloc(0x20);

        strcpy(buf1, argv[1]);
        strcpy(buf2, argv[2]);
        strcpy(buf3, argv[3]);

        printf("buf1 = %s\n", buf1);
        printf("buf2 = %s\n", buf2);
        printf("buf3 = %s\n", buf3);

        free(buf3);
        free(buf2);
        free(buf1);

        return 0;
}

3개의 버퍼에 0x20만큼 공간을 할당해준 후 인자값을 복사한 후 free합니다.

gdb로 분석해보죠!!

   0x080484b1 <+6>:     push   0x20
   0x080484b3 <+8>:     call   0x8048380 <malloc@plt>
   0x080484b8 <+13>:    add    esp,0x4
   0x080484bb <+16>:    mov    DWORD PTR [ebp-0x4],eax
   0x080484be <+19>:    push   0x20
   0x080484c0 <+21>:    call   0x8048380 <malloc@plt>
   0x080484c5 <+26>:    add    esp,0x4
   0x080484c8 <+29>:    mov    DWORD PTR [ebp-0x8],eax
   0x080484cb <+32>:    push   0x20
   0x080484cd <+34>:    call   0x8048380 <malloc@plt>

malloc() 부분입니다.

malloc()의 리턴 값은 할당된 주소이기 때문에 main+13 부분에 bp를 걸고 메모리를 보겠습니다.

인자는 aaaaaaaa bbbbbbbb cccccccc 로 주었습니다!!

gdb-peda$ i r
eax            0x8e9f008        0x8e9f008
ecx            0xf7745420       0xf7745420
edx            0x8e9f008        0x8e9f008
ebx            0xf7745000       0xf7745000
esp            0xffa04f18       0xffa04f18
ebp            0xffa04f28       0xffa04f28
esi            0x0      0x0
edi            0x0      0x0
eip            0x80484b8        0x80484b8 <main+13>
eflags         0x286    [ PF SF IF ]
cs             0x23     0x23
ss             0x2b     0x2b
ds             0x2b     0x2b
es             0x2b     0x2b
fs             0x0      0x0
gs             0x63     0x63

0x8e9f008 부분부터 data가 들어갈 수 있군요. prev_size와 size를 감안해서 0x8e9f000부터 보죠.

gdb-peda$ display /40wx 0x8e9f000
1: x/40xw 0x8e9f000
0x8e9f000:      0x00000000      0x00000029      0x00000000      0x00000000
0x8e9f010:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f020:      0x00000000      0x00000000      0x00000000      0x00020fd9
0x8e9f030:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f040:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f050:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f060:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f070:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f080:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f090:      0x00000000      0x00000000      0x00000000      0x00000000

0x8e9f004 부분에 size 값 + P 플래그가 들어가서 0x29가 찍혀있습니다.

할당한 부분(0x20) + prev_size(0x4) + size(0x4) + P Flag(0x1) = 0x29, 딱 맞네요 ㅎㅎ

top chunk는 0x20fd9 값이 들어있네요. 다음 malloc으로 넘어가보겠습니다.

1: x/40xw 0x8e9f000
0x8e9f000:      0x00000000      0x00000029      0x00000000      0x00000000
0x8e9f010:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f020:      0x00000000      0x00000000      0x00000000      0x00000029
0x8e9f030:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f040:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f050:      0x00000000      0x00020fb1      0x00000000      0x00000000
0x8e9f060:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f070:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f080:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f090:      0x00000000      0x00000000      0x00000000      0x00000000

0x8ef9028 부터 prev_size + size (P Flag) + 0x20이 할당되었습니다.

top chunk는 0x28 감소한 0x20fb1이 되었군요. 딱딱 맞아떨어집니다!! 다음 할당도 똑같겠죠??

0x8e9f000:      0x00000000      0x00000029      0x00000000      0x00000000
0x8e9f010:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f020:      0x00000000      0x00000000      0x00000000      0x00000029
0x8e9f030:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f040:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f050:      0x00000000      0x00000029      0x00000000      0x00000000
0x8e9f060:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f070:      0x00000000      0x00000000      0x00000000      0x00020f89
0x8e9f080:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f090:      0x00000000      0x00000000      0x00000000      0x00000000

알맞게 할당되었습니다 :D

이젠 strcpy 하는 부분을 쭈욱 넘겨서, buf3에 argv[3]이 들어가는 곳까지 가보겠습니다.

1: x/40xw 0x8e9f000
0x8e9f000:      0x00000000      0x00000029      0x61616161      0x61616161
0x8e9f010:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f020:      0x00000000      0x00000000      0x00000000      0x00000029
0x8e9f030:      0x62626262      0x62626262      0x00000000      0x00000000
0x8e9f040:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f050:      0x00000000      0x00000029      0x63636363      0x63636363
0x8e9f060:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f070:      0x00000000      0x00000000      0x00000000      0x00020f89
0x8e9f080:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f090:      0x00000000      0x00000000      0x00000000      0x00000000

위에서 분석한 대로 자리에 꼭 맞게 들어갑니다.

이제 free를 해볼까요?

1: x/40xw 0x8e9f000
0x8e9f000:      0x00000000      0x00000029      0x08e9f028      0x61616161
0x8e9f010:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f020:      0x00000000      0x00000000      0x00000000      0x00000029
0x8e9f030:      0x08e9f050      0x62626262      0x00000000      0x00000000
0x8e9f040:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f050:      0x00000000      0x00000029      0x00000000      0x63636363
0x8e9f060:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f070:      0x00000000      0x00000000      0x00000000      0x00020f89
0x8e9f080:      0x00000000      0x00000000      0x00000000      0x00000000
0x8e9f090:      0x00000000      0x00000000      0x00000000      0x00000000

Data 영역의 첫 번째 4 bytes의 값이 바뀌었네요!!

이 녀석들은 FD, 다음 청크를 가리키는 친구들입니다. 실제로 보면 buf2의 FD는 buf3의 시작주소를 가리켜요.

free list가 buf3 -> buf2 -> buf1 로 되어있기 때문이죠.

이렇게 정말정말 중요한 Heap 영역을 살짝만 ㅎㅎ 뜯어봤습니다.

마무리

사실 힙이라는게.. 정확하게 이해하려면 bin 구조부터 확실하게 익히고 chunk를 봐야되는 것 같습니다.

아직까지 저도 지식이 부족한지라.. bin 공부하고 이해하려면 또 한~참 걸리겠죠?ㅠㅠㅠ

보안 산업기사 이론공부랑 병행해서 열심히 해봐야겠습니다ㅎㅎ 감사합니다 :D