Heap Memory는 큰 메모리 블록 여러개로 이루어져 있다.
각 블록은 블록의 크기와 그 밖의 사항을 설명하는 작은 헤더를 가지고 있다.
힙 버퍼에서 오버플로우가 나면, 힙의 다음 블록 헤덜르 포함하는 영역을 덮어쓰게 된다.
다음 블록의 헤더를 덮어 쓴 후에는 메모리에 임의의 데이터를 쓸 수 있다.
프로그램은 Heap 영역에 존재하는 데이터를 참조할 때에는 포인터 변수를 사용한다.
즉 이름으로 접근하지 않고 주소로서 접근한다!
Heap 은 프로그래밍에서 동적할당을 위한 공간이다.
메모리 할당은 정적할당과 동적할당으로 구분할 수 있다.
컴파일시 미리 크기가 정해져 들어가는 할당->정적할당
프로그램 실행시 크기가 정해져 들어가는 할당->동적할당 malloc()->free()
malloc함수 성공시 Heap Memory내에 할당된 메모리의 첫바이트를 가리키는 포인터를 리턴한다.
사이즈가 0이라면 NULL을 리턴
실패시 NULL을 리턴하며 errno를 세팅한다.
사용자가 malloc()함수를 사용하여 동적 메모리를 요청하면 Heap Management Library는 이 메모리의 크기를 판단한 후
크기별로 분류된 사용가능한 메모리 블록을 탐색하여 사용자가 요청한 메모리크기보다 약간 크거나 적합한 블록을 찾는다.
malloc()함수는 사용가능한 메모리 블록을 찾아서 리턴해주는 함수이며, 만약 사용가능한 메모리 블록이 없다면
malloc()함수에서 brk()함수 혹은 sbrk()함수를 호출하게 된다.
Heap Management Library는 적합한 블록을 찾은 후 사용자가 원하는 크기만큼 메모리를 잘라서 사용자에게 그 주소값을 반환하고 나머지 블록은 다시 리스트에 연결해 두어 나중에 사용할 수 있도록 한다.
일반적으로 heap 공간에 malloc()함수로 메모리를 생성하면 사용자가 원하는 크기 외에 그 크기에 대한 정보 및 기타 부가정보들을 저장하기 위해 4byte가 할당된 메모리 바로 앞에 할당된다. 그리고 2개 이상의 메모리를 생성하였을 때 그 메모리들은 일정한 규칙을 갖는 사이를 두고 할당된다.
동적메모리는 metadata(heap memory의 부가정보를 기록하기 위한 값)와 할당된 공간으로 볼 수 있다. 이를 chunk라고 부른다.
chunk는 malloc()함수에 구현되어 있는 구조체이다.
Heap을 할당하고 해제할 때, 메모리를 좀 더 효율적으로 사용하기 위해 bin이라는 구조를 사용하여 해제된 chunk list를 관리합니다.
이 것을 bin 구조라고 하는데요, free 된 chunk의 필드 중 fd, bk를 이용하여 리스트를 연결하고 있습니다.
참고: https://bpsecblog.wordpress.com/2016/10/06/heap_vuln/
UAF란
• Heap 역에서 해제(free)된 역을 재사용(reuse)하면서 발생하는 취약점
• 다시말해, Heap 역의 데이터를 가리키는 포인터가 해제되었지만 해당 역 을 가리키는 포인터(dangling pointer)가 재사용되는 취약점
#include <stdio.h>
#include <malloc.h>
typedef struct students{
size_t id;
char name[8];
}STD;
typedef struct movement{
void (*playctf)();
char note[8];
}MOV;
void main()
{
MOV * p = malloc(sizeof(MOV));
free(p); // p <- dangling pointer :(
STD * q = malloc(sizeof(STD));
printf("Input your ID number : ");
scanf("%u", &q->id);
p->playctf();
}
void vuln()
{
system("/bin/sh");
}
문제에 대해서
vuln이 0x804854e이므로
134513998을 넣으면 끝!
UAF로 p가 가리키는 곳과 q가 가리키는 곳이 같아요!