배열의 범위를 벗어난 메모리에 접근할 수 있는 취약점
개발자가 인덱스에 대한 검사를 제대로 하지 않으면 발생한다.
임의 주소 읽기, 임의 주소 쓰기로 이어질 수 있다.
배열의 특징
배열은 연속된 메모리 공간을 점유한다. 이때 배열이 점유햐는 공간의 크기는 요소의 개수와 요소 자료형의 크기를 곱한 값이 된다.
배열이 포함하는 요소의 개수를 배열의 길이라고 부른다.
dreamhack Memory Corruption: Out of Bounds
배열의 각 요소의 주소는 배열의 주소, 요소의 인덱스, 요소 자료형의 크기를 이용하여 계산한다.
OOB
OOB는 요소를 참조할 때, 인덱스 값이 음수이거나 배열의 길이를 벗어날 때 발생한다.
개발자가 인덱스의 범위에 대한 검사를 명시적으로 프로그래밍하지 않으면, 프로세스는 앞서 배운 식을 따라 요소의 주소를 계산할 뿐, 계산한 주소가 배열의 범위 안에 있는지 검사하지 않는다.
만약 사용자가 배열 참조에 사용되는 인덱스를 임의 값으로 설정할 수 있다면, 배열의 주소로부터 특정 오프셋에 있는 메모리의 값을 참조할 수 있다.
Proof of Concept
// Name: oob.c
// gcc -o oobtest1 oobtest1.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;
}
위에서 알 수 있는 것은 배열의 범위를 벗어나는 인덱스를 사용했음에도 경고를 띄워주지 않는다.
배열의 0번째는 e1b0 이지만
-1 을 인덱스로 사용하면 배열의 0번째 보다 낮은 주소까지 참조가 가능하다.
또한 100을 인덱스로 사용하여 배열의 범위를 벗어난 곳 까지 참조할 수 있다.
배열보다 높은 곳 (높은 주소) 만 overwrite 할 수 있었던 buffer overflow 보다 훨씬 위험한 취약점이란 뜻이다.
OOB read
OOB를 이용해서 임의 읽고 쓰기를 테스트해본다.
// oob 임의 주소 읽기
// oob_read.c
// 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 SLAARY", "COMPANY"};
char * secret_code = secret;
int idx;
if (read_secret() != 0){
exit(-1);
}
puts("What do you wnat 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;
}
if (idx > 4)
{
printf("Detect out-of-bounds");
exit(-1);
}
음수 체크를 안한다.
char * docs[] = {"COMPANY INFORMATION", "MEMBER LIST", "MEMBER SLAARY", "COMPANY"};
char * secret_code = secret;
int idx;
idx | low |
* secret_code | |
* docs | high |
OOB write
// oob 임의 쓰기
// oob_write.c
// 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;
puts("Who is present");
printf("(1-10) ");
scanf("%u", &idx);
stu[idx-1].attending = 1;
if (isAdmin) printf("Access granted. \n");
return 0;
}
struct Student {
long attending;
char * name;
long age;
};
struct Student stu[10];
int isAdmin;
24바이트 크기의 Student 구조체 10개를 포함하는 배열 stu 와 isAdmin을 전역변수로 선언한다.
scanf("%u", &idx);
stu[idx-1].attending = 1;
인덱스를 입력받아 인덱스에 해당하는 Student 구조체의 attending에 1을 대입한다.
OOB 취약점을 이용해 isAdmin에 값을 써야한다.
변수의 주소를 구한다.
isAdmin이 stu보다 240바이트 더 높은 주소에 있다.
배열을 구성하는 Student구조체의 크기가 24바이트 이므로, 10번째 인덱스를 참조하면 isAdmin을 조작할 수 있다.
24*10 = 240
이 글은 옵시디언을 이용해서 작성되었습니다.
'TOOR' 카테고리의 다른 글
[TOOR] 21. __rtld_global (0) | 2023.10.02 |
---|---|
[TOOR] 17. Race Condition (0) | 2023.10.02 |
[TOOR] 15.1 Format String Bug (0) | 2023.10.02 |
[TOOR] 14. RELRO (0) | 2023.09.29 |
[TOOR] 13.1. PIE (0) | 2023.09.29 |