c언어는 배열의 경계를 체크 안함 -> out of bounds(OOB)

배열의 속성

배열은 연속된 메모리 공간을 점유하며, 배열이 점유하는 공간의 크기는 요소의 개수와 요소 자료형의 크기를 곱한 값

배열 요소의 개수를 배열의 길이라고도 부름

OUT OF BOUNDS

OOB는 요소를 참조할 때, 인덱스 값이 음수이거나 배열의 길이를 벗어날 때 발생

EX) 예제 코드

// Name: oob.c
// Compile: gcc -o oob oob.c

#include <stdio.h>

int main() {
  int arr[10];

  printf("In Bound: \n");
  printf("arr: %p\n", arr);
  printf("arr[0]: %p\n\n", &arr[0]);

  printf("Out of Bounds: \n");
  printf("arr[-1]: %p\n", &arr[-1]);
  printf("arr[100]: %p\n", &arr[100]);

  return 0;
}
$ gcc -o oob oob.c
$ ./oob
In Bound:
arr: 0x7ffebc778b00
arr[0]: 0x7ffebc778b00

Out of Bounds:
arr[-1]: 0x7ffebc778afc
arr[100]: 0x7ffebc778c90

 

 

임의 주소 읽기

읽으려는 변수와 배열의 오프셋을 알아야 함

배열과 변수가 같은 세그먼트에 할당되어 있다면, 둘 사이의 오프셋은 항상 일정하므로 디버깅으로 알아낸다.

만약 다른 세그먼트라면 다른 취약점을 통해 두 변수의 주소를 구하고 차이를 계산해야한다.

// Name: oob_read.c
// Compile: gcc -o oob_read oob_read.c

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

char secret[256];

int read_secret() {
  FILE *fp;

  if ((fp = fopen("secret.txt", "r")) == NULL) {
    fprintf(stderr, "`secret.txt` does not exist");
    return -1;
  }

  fgets(secret, sizeof(secret), fp);
  fclose(fp);

  return 0;
}

int main() {
  char *docs[] = {"COMPANY INFORMATION", "MEMBER LIST", "MEMBER SALARY",
                  "COMMUNITY"};
  char *secret_code = secret;
  int idx;

  // Read the secret file
  if (read_secret() != 0) {
    exit(-1);
  }

  // Exploit OOB to print the secret
  puts("What do you want to read?");
  for (int i = 0; i < 4; i++) {
    printf("%d. %s\n", i + 1, docs[i]);
  }
  printf("> ");
  scanf("%d", &idx);

  if (idx > 4) {
    printf("Detect out-of-bounds");
    exit(-1);
  }

  puts(docs[idx - 1]);
  return 0;
}
$ echo "THIS IS SECRET" > ./secret.txt
$ ./oob_read
What do you want to read?
1. COMPANY INFORMATION
2. MEMBER LIST
3. MEMBER SALARY
4. COMMUNITY
> 0
THIS IS SECRET

 

임의 주소 쓰기

// Name: oob_write.c
// Compile: gcc -o oob_write oob_write.c

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

struct Student {
  long attending;
  char *name;
  long age;
};

struct Student stu[10];
int isAdmin;

int main() {
  unsigned int idx;

  // Exploit OOB to read the secret
  puts("Who is present?");
  printf("(1-10)> ");
  scanf("%u", &idx);

  stu[idx - 1].attending = 1;

  if (isAdmin) printf("Access granted.\n");
  return 0;
}

인덱스 범위 검사 없음

ISADMIN 조작하면 가능

pwndbg> i var isAdmin
Non-debugging symbols:
0x0000000000201130  isAdmin
pwndbg> i var stu
Non-debugging symbols:
0x0000000000201040  stu
pwndbg> print 0x201130-0x201040
$1 = 240
$ ./oob_write
Who is present?
(1-10)> 11
Access granted.

+ Recent posts