[P4C] W8: Out of bounds & R/W primitives 취약점 공부하기
# Out of bounds
[dreamhack] Memory Corruption: Out of Bounds
1. 배열의 속성
- 배열은 연속된 메모리 공간을 점유하며, 배열이 점유하는 공간의 크기는 요소의 개수와 요소 자료형의 크기를 곱한 값이 됨
- 배열이 포함하는 요소의 개수를 배열의 길이(Length)라고도 부름
- 배열 각 요소의 주소는 배열의 주소, 요소의 인덱스, 요소 자료형의 크기를 이용하여 계산됨
2. Out of Bounds
- OOB는 요소를 참조할 때, 인덱스 값이 음수이거나 배열의 길이를 벗어날 때 발생
- 프로세스는 계산한 주소가 배열의 범위 안에 있는지 검사하지 않음
- 만약 사용자가 배열 참조에 사용되는 인덱스를 임의 값으로 설정할 수 있다면, 배열의 주소로부터 특정 오프셋에 있는 메모리 값을 참조할 수 있음
- Out of Bounds: 배열의 범위를 벗어나는 참조
3. Proof-of-Concept
- Figure4: OOB 이해를 돕기 위한 예제 코드
- int형 변수 10개를 요소로하는 배열 arr을 선언하고, 다양한 인덱스를 사용하여 배열 내부와 외부의 주소들을 출력
// 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)는 배열의 범위를 벗어나는 -1과 100을 인덱스로 사용했음에도 경고를 띄우지 않음
- 이를 통해 oob를 방치하는 것은 개발자의 몫임을 알 수 있음
- arr[0]와 arr[100]의 주소 차이는 0x7ffd76487d00 - 0x7ffd76487b70 = 400(100*4)
4. 임의 주소 읽기
- 배열의 임의 주소 값을 읽으려면, 읽으려는 변수와 배열의 offset을 알아야 함
- 배열과 변수가 같은 세그먼트에 할당되어 있다면, 둘 사이의 오프셋은 항상 일정하므로 디버깅을 통해 쉽게 알아 낼 수 있음
- 만약 같은 세그먼트가 아니라면, 다른 취약점을 통해 두 변수의 주소를 구하고, 차이를 계산해야 함
- Figure6: 인덱스에 대한 검증이 미흡해 임의 주소 읽기가 가능한 예제
- 길이가 3인 배열 docs를 참조하는데, 인덱스 값이 3보다 큰지만 검사하고, 음수인지는 검사하지 않음
- 목표: secreat.txt 파일을 만들고, oob_read의 OOB를 이용하여 secret.txt의 값 읽기
- hint: docs와 secret_code는 모두 스택에 할당되어 있으므로, docs에 대한 OOB를 이용하면 secret_code 값을 쉽게 읽을 수 있음
// 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;
}
- 해결 방법
- 프로그램 실행시 아래 그림과 같이 stack 구조가 생성되므로 docs에서 음수값의 인덱스를 참조하게 되면 가장 먼저 참조되는 값은 secret_code일 것임. 따라서 '-1' 인덱스로 접근하면 secret.txt의 내용을 알 수 있을 것이라고 예상
- 실행 결과
-1 인덱스에 접근하기 위해 입력값은 0을 주었음
5. 임의 주소 쓰기
- Figure7: 인덱스에 대한 검증이 미흡해 임의 주소에 값을 쓸 수 있는 예제
- 24 바이트 크기의 Student 구조체 10개를 포함하는 배열 stu와 isAdmin를 전역변수로 선언
- 사용자로부터 인덱스를 입력받아 인덱스에 해당하는 Student구조체의 attending에 1을 대입
- isAdmin이 참인지 검사하는 부분이 있음. 해당 변수에 값을 직접 쓰는 부분은 없지만, 코드에 OOB 취약점이 있으므로 이를 이용하면 isAdmin의 값을 조작할 수 있음
// 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의 값 조작을 위해 디버거로 stu와 isAdmin의 주소 및 두 주소 사이의 offset 확인
- 실행결과
- 구조체의 크기가 24바이트(8(long)+8(*name)+8(long)) 이므로, 240/24 = 10이며 0~9 인덱스를 참조하면 이 240 바이트에 저장될 것임. 따라서 10번째 인덱스를 참조하면 isAdmin을 조작할 수 있을 것
인덱스 10에 접근하기 위해 11을 입력
# R/W primitives 취약점
- 문제와 풀이는 올리지 말라고하셨지만, OOBR, OOBW 두 문제를 풀어봄
* 개인적으로 EasyOOB는 이름과 다르게 어려운 것 같음(처음에 쉘이 따진줄 알았는데 아니여서 ^^,,)
'etc... > 빡공팟(P4C) 4기' 카테고리의 다른 글
[P4C] W7: 올드 스쿨 취약점과 올드 스쿨 공격 기법 공부하기 (0) | 2022.06.05 |
---|---|
[P4C] W6: Double Linked List CRUD 구현, 어셈블리로 구구단 구현, Stack 개념 공부하기 (0) | 2022.05.29 |
[P4C] W4-W5: C언어로 HTTP 서버 구현 (0) | 2022.05.21 |
[P4C] W3: 코드업 기초 100제 70번 대 이후 문제들 중 가장 어려웠던 10문제 write-up 작성하기 (0) | 2022.05.08 |
[P4C] W2: 코드업 기초 100제 70번 이하 문제들 중 가장 어려웠던 5문제 write-up 작성하기 (0) | 2022.04.29 |