[Dream Hack]Memory Corruption - C

paenge1004·2021년 9월 4일
0

Hacker

목록 보기
1/1

버퍼 오버플로우

버퍼(Buffer)

  • C언어에서 지정된 크기의 메모리 공간.
  • 취약점 : 버퍼가 허용할 수 있는 양의 데이터보다 더 많은 값이 저장되어 버퍼가 넘친다.

버퍼 오버플로우(Buffer Overflow)

  • 발생하는 위치에 따라서 스택 버퍼 오버플로우, 힙 오버플로우로 나뉜다.
  • 인접한 메모리를 오염시키는 취약점이기 때문에 어떤 메모리를 오염시킬 수 있는지에 따라 공격 방법이 달라진다.

  • 8 비트의 버퍼A와 8 바이트 데이터 버퍼 B가 메모리에 선형적으로 할당되었다.
    버퍼 A에 16바이트의 데이터를 복사한다면 이 데이터의 뒷부분은 버퍼 A를 넘어 뒤에 있는
    데이터 영역인 B에 쓰여지게 된다.
  • 이때 버퍼 오버플로우가 발생. 이는 프로그램의 Undefined Behavior
    (정의되어 있지 않은 동작, 런타임중에 어떤 현상이 발생할 지 예측할 수 없다는 뜻)
    데이터 영역 B에 나중에 호출될 함수 포인터를 저장하고 있다면 이 값을 "AAAAAAAA"와 같은 데이터로 덮었을 때 Segmentation Fault를 발생.
    (접근 권한이 없는 메모리 영역을 읽거나 쓰려고 할 때 발생하는 예외)
    공격자가 이를 악용한다면 어딘가에 기계어 코드를 삽입한 후 함수 포인터를 공격자의 코드의 주소로 덮어 코드를 실행할 수도 있다.

스택 버퍼 오버플로우(Stack Buffer Overflow)

EX 1)

// stack-1.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
     char buf[16];
     gets(buf);
     
     printf("%s", buf);
}
  • stack-1.c는 16바이트 버퍼(buf)를 스택에 할당한 후, gets 함수를 통해 사용자로부터 데이터를 입력받아 이를 그대로 출력하는 코드. gets함수는 사용자가 개행을 입력하기 전까지 입력했던 모든 내용을 첫 번째 인자로 전달된 버퍼에 저장하는 함수.

  • gets 함수에는 별도의 길이 제한이 없기 때문에 16 바이트가 넘는 데이터를 입력한다면 스택 버퍼 오버플로우가 발생합니다.

  • 버퍼 오버플로우 취약점은 프로그래머가 버퍼의 길이에 대한 가정을 올바르지 않게 하여 발생합니다. 이는 보통 길이 제한이 없는 API 함수들을 사용하거나 버퍼의 크기보다 입력받는 데이터의 길이가 더 크게 될때 자주 일어나는 실수.

  • ret 영역을 0x41414141로 만드는 법.(AAAAAAAAAAAAAAAAAAAAAAAAA)

EX 2)

// stack-2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_auth(char *password) {
    int auth = 0;
    char temp[16];
    
    strncpy(temp, password, trlen(password));
    
    if(!strcmp(temp, "SECRET_PASSWORD"))
       auth = 1;
  
  return auth;
}

int main(int argc, char *argv[]) {
    if (argc !=2) {
    printf("Usage: ./stack-1 ADMIN_PASSWORD\n");
    exit(-1);
}

   if (check_auth(argv[1]))
      printf("Hello Admin!\n");
   else
      printf("Acess Denied!\n");
}
  • stack-1.c의 main 함수는 argv[1]을 check 함수의 인자로 전달한 후 그 리턴 값을 받는다.
    리턴 값이 0이 아니라면 "Hello Admin!"을, 0이라면 "Access Denied!"라는 문자열을 출력.

  • 핵심이 되는 check 함수에서는 16 바이트 크기의 temp 버퍼에 입력받은 패스워드를 복사한 후 "SECRET_PASSWORD" 문자열과 비교. 문자열이 같다면 auth 변수를 1로 설정하고 auth를 리턴.

  • strncpy 함수를 통해 temp 버퍼를 복사할 때, temp의 크기인 16 바이트가 아닌 인자로 전달된 password 문자열의 길이만큼을 복사.

  • argv[1]에 16 바이트가 넘는 문자열을 전달한다면 길이 제한 없이 문자열이 복사되어 스택 버퍼 오버플로우가 발생하게 됩니다.

  • temp 버퍼 뒤에 auth 값이 존재하므로, 오버플로우가 발생해 공격자의 데이터가 auth 값을 바꾼다면 auth가 0이 아닌 다른 값이 될 수 있습니다. 이 경우 실제 인증 여부와는 상관없이
    if(check_auth(argv[1]))문은 항상 참을 반환하게 된다.

EX 3)

// stack-3.c
#include <stdio.h>
#include <unistd.h>
int main(void) {
    char win[4];
    int size;
    char buf[24];
    
    scanf("%d", &size);
    read(0, buf, size);
    if (!strncmp(win, "ABCD", 4)){
        printf("Theori{-----------redacted---------}");
    }
}
  • stack-3.c의 main 함수는 24 바이트 크기의 buf를 할당.
    scanf 함수를 통해 size 변수에 값을 입력, size 만큼 buf에 데이터를 입력.

  • stack-1.c에서는 길이 검증이 없는 함수를 사용해 스택 버퍼 오버플로우가 발생.
    고정된 크기의 버퍼보다 더 긴 데이터를 입력받아 스택 버퍼 오버플로우가 발생.

EX 4)

// stack-4.c
#include <stdio.h>
int main(void) {
	char buf[32] = {0, };
	read(0, buf, 31);
	sprintf(buf, "Your Input is: %s\n", buf);
	puts(buf);
}
  • stack-4는 32바이트 키그 buf를 초기화 후, 데이터를 31바이트 입력.
    sprintf 함수를 통해 출력할 문자열을 저장한 뒤 출력하는 코드.

  • read함수에서 받는 입력이 32바이트를 넘지 않지만, spirntf 함수를 통해 버퍼에 값을 쓸 때
    "Yout Input is: "문자열을 추가한다는 사실 인지.

  • 만약 buf에 31바이트를 꽉 채운다면 "Yout Input Is: " 문자열이 앞에 붙어 총 길이가 32 바이트를 넘는다.

정리

  • 버퍼 오버플로우는 프로그래머가 길이에 대한 검증을 정확히 수행하지 못해 발생.
  • 공격 벡터로부터 데이터를 입력받고 이를 버퍼에 저장하는 코디가 있다면 유심히 살펴볼 것.
  • 데이터를 입력받을 때는 입력받은 데이터가 버퍼의 범위를 초과하지 않는지 항상 검사.
  • 입력받을 때 길이 제한이 없는 함수는 잠재적으로 취약. 입력받은 데이터가 버퍼에 저장되기까지의 흐름을 따라가 버퍼의 크기를 넘는 양을 저장할 수 있는지 가능성 검토.
  • 길이를 명시하는 함수를 사용할 경우, 명시된 길이가 버퍼의 크기를 넘을 수 있는지 검토.
  • 버퍼 오버플로우는 스택에서만 발생하는 취약점이 아닌, 프로그래머가 동적으로 메모리를 관리할 수 있는 힙에서도 발생 가능. 메모리 영역의 차이일 뿐, 본질저긍로 원인은 같다.
  • 힙 역역은 스택 영역과 사용 목적이 다르기 때문에, 스택과 다른 방법으로 익스플로잇 해야한다.

힙 오버플로우(Heap OverFlow)

// heap-1.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    char *input = malloc(40);
    char *hello = malloc(40);
    
    memset(input, 0, 40);
    memset(hello, 0, 40);
    
    strcpy(hello, "HI!");
    read(0, input, 100);
    
    printf("Input: %s\n", input);
    printf("hello: %s\n", hello);
}
  • heap-1.c는 40바이트 크기의 힙 버퍼 input과 hello를 할당한 후, hello 버퍼에는 "HI!" 문자열을 복사하고 read 함술르 통해 input에 데이터를 입력받는 코드.
  • read 함수를 통해 입력받는 길이인 100바이트가 input 버퍼의 크기인 40바이트보다 크기 때문에 힙 오버플로우 발생.
  • Input영역에서 버퍼 오버플로우가 발생해 hello의 메모리 영역까지 침범할 경우, hello 메모리 출력할 때 "HI!"문자열이 아니라 공격자에게 오염된 데이터가 출력.

0개의 댓글