시작
안녕하세요 :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
'CTF_Write_UP > etc' 카테고리의 다른 글
[정보보안산업기사] 14회 필기 합격!! (0) | 2019.10.01 |
---|---|
HackCon CTF pwn binary (0) | 2019.08.23 |
[Pwn] 나도 만들어봤다, 쉘코드!! (0) | 2019.07.14 |
[Facebook CTF 2019] Facebook CTF 시작!! // 후기 (0) | 2019.06.01 |
[BOF] rop 알았으면 srop까진 해야지 (ver. 32bit) (0) | 2019.05.18 |