[Protostar] Heap2
About
This level examines what can happen when heap pointers are stale.
This level is completed when you see the “you have logged in already!” message
This level is at /opt/protostar/bin/heap2
이 레벨은 힙 포인터가 오래되었을 때 발생할 수 있는 일을 조사하는 것으로 "you have logged in already!”"라는 메시지가 표시되면 이 레벨이 완료된 것입니다.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
struct auth {
char name[32];
int auth;
};
struct auth *auth;
char *service;
int main(int argc, char **argv)
{
char line[128];
while(1) {
printf("[ auth = %p, service = %p ]\n", auth, service);
if(fgets(line, sizeof(line), stdin) == NULL) break;
if(strncmp(line, "auth ", 5) == 0) {
auth = malloc(sizeof(auth));
memset(auth, 0, sizeof(auth));
if(strlen(line + 5) < 31) {
strcpy(auth->name, line + 5);
}
}
if(strncmp(line, "reset", 5) == 0) {
free(auth);
}
if(strncmp(line, "service", 6) == 0) {
service = strdup(line + 7);
}
if(strncmp(line, "login", 5) == 0) {
if(auth->auth) {
printf("you have logged in already!\n");
} else {
printf("please enter your password\n");
}
}
}
}
auth라는 구조체는 32bytes의 name과 4bytes의 auth로 구성되어 있다.
main함수가 시작되면서 while문을 돌면서 fgets을 통해서 사용자로 부터 입력을 받는다.
비교하는 곳이 4군데 있는데
1) strncmp(line, "auth ",5)==0 이부분에서
'auth 입력값'을 사용자가 입력하면, auth = malloc(sizeof(auth));를 통해서 구조체 auth의 크기 만큼 메모리가 할당되고 입력한 값은 auth->name에 저장된다.
2) strncmp(line, "reset",5)==0
'reset'을 입력하게 되면, 기존에 할당된 auth의 메모리가 해제됨.
3) strncmp(line, "service",6)==0
strdup함수를 이용해서 문자열이 복제되고 service포인터는 복제된 문자열을 가리키게 됨.
4) strncmp(line, "login",5)==0
login을 입력하면, auth구조체의 auth필드 값을 확인하여 auth->auth의 값이 변조 되었으면 성공, 아니면 실패한다.
👉 User After Free 기법을 이용한 공격이다.
UAF에 대해서 공부를 하자면,
UAF(Uninitialized Use After Free) 기법은 동적 메모리 할당과 관련된 보안 취약점 중 하나로, 이미 해제된 메모리를 계속 사용하게 되어 발생하는 취약점이다.
일반적으로 동적 메모리 할당 함수(malloc, calloc, realloc 등)를 사용하여 메모리를 할당하면, 해당 메모리의 주소를 반환받는다. 이후 할당된 메모리를 사용하고자 할 때에는 해당 메모리 주소를 이용하여 접근하게 된다.
👉 그러나 이렇게 사용된 메모리를 해제해 주지 않으면, 이미 해제된 메모리를 사용하게 되면서 UAF 취약점이 발생
📌 UAF 취약점은 일반적으로 다음과 같은 상황에서 발생 :
1) 메모리 할당 후 해당 메모리를 해제하지 않은 경우
2) 이미 해제된 메모리를 계속 사용하는 경우
예를 들어, 다음과 같은 코드가 있다고 가정
#include <stdlib.h>
#include <stdio.h>
int main() {
int *p = malloc(sizeof(int));
*p = 10;
free(p);
printf("%d\n", *p);
return 0;
}
1. 위 코드에서는 먼저 malloc 함수를 이용하여
👉 int 자료형 크기만큼의 메모리를 할당
👉 이후 해당 메모리에 10을 저장
2. 그러나 이후 free 함수를 이용하여 p가 가리키는 메모리를 해제합니다.
3. 그리고 나서 p가 가리키는 메모리에 접근하여 그 값을 출력하려고 합니다.
👉 이때 이미 해제된 메모리를 접근하게 되므로 UAF 취약점이 발생합니다.
UAF 예방 방법 :
1) 메모리를 해제한 후에는 해당 메모리를 더 이상 사용하지 않도록 NULL 값을 할당
2) 포인터를 이용하여 메모리에 접근하기 전에는 해당 포인터가 NULL인지 확인
문제를 풀기위해서는 login을 입력하였을 때, auth->auth에 값이 있어서 "you have logged in already!"라는 문자가 나와야 한다.
auth를 malloc으로 메모리를 할당하고, free로 메모리 해제를 한다.
👉 auth 포인터가 가리키는 구조체 auth 필드는 해제되기 이전의 값을 계속 가지고 있음.
strdup 함수는 인자로 전달된 문자열을 동적으로 할당(malloc)하고, 그 문자열을 복사하여 반환하는 함수이다.
👉 strdup으로 service를 할당하게 되면다면, 아마 기존에 해제되었던 부분에 접근 가능해진다.
여기서 auth와 service의 주소를 보면 0x10(10진수로 16bytes)가 차이 난다.
즉 기존의 auth의 name 32bytes에서 포인터 시작부분에서 16bytes 떨어진 곳에 service가 시작된다.
그래서 service 값으로 17bytes를 넣어주면 기존에 auth의 auth부분이 덮어써지게 된다.
그래서 다시 service의 입력값으로 B 16개(16bytes) + 엔터 1byte로 17bytes을 주면, 성공한다!