본 글은 드림핵 시스템해킹 로드맵 Stage 9를 학습하고
개인적으로 정리한 글입니다.
OBB (Out of Bounds)
지금까지 여러 워게임 및 CTF문제, 개발등을 하면서 자연스레 배열을 사용해왔습니다.
배열 내부 자료를 요소, 요소의 위치를 인덱스라고 하며 파이썬 같은 인덱스 슬라이싱 같은 배열을 다뤄왔습니다.
이러한 배열에 인덱스를 이용해 임의의 주소로 접근이 가능한 취약점이 존재합니다.
이는 개발자가 설정한 배열의 경계를 넘어서 값을 읽거나 쓸수 있어 코스를 벗어났다는 의미로
Out Of Bounds라고 하여 OBB라고 합니다.
보통 배열의 길이는 array[n] = sizeof(요소) * n 으로 결정됩니다.
그렇다면 array[n]에 들어가는 n 즉, 인덱스 값이 음수 이거나 배열의 길이를 벗어나면 어떻게 되는가?
프로세스는 식을 따라 주소를 계산할 뿐 계산한 주소가 배열의 범위안에 있는지 검사하지 않아
공격자가 특정 오프셋에 있는 메모리 값으로 접근이 가능하게 됩니다.
예시로 10의 길이를 가지는 array[10]에
인덱스값으로 -1을 넣거나 100같은 큰 값을 넣었을때 오류가 아닌
정상작동으로 식을 계산해 값을 출력해주게 됩니다.
위 처럼 개발자로써 배열을 사용할 때에는 반드시 인덱스 범위에 대한
검사를 명시적으로 프로그래밍하는 것이 취약점을 보완할 수 있습니다.
임의 주소 읽기
그렇다면 공격자는 이러한 기법을 이용해 어떻게 원하는 값을 읽는가?
아래 코드를 예시로 들면
#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 stu[10]배열이 선언되어 있고 isAdmin 변수가 있습니다.
만약 isAdmin에 접근한다면 if구문을 통과해 print 구문이 출력됩니다.
위 같은 경우 stu[10]배열의 주소와 isAdmin의 주소를 찾고 두값의 offset을 먼저 찾습니다.
디버거로 isAdmin의 주소로부터 stu의 주소까지 offset을 구하면 240이라고 합니다.
여기서 stu구조체의 크기는 24바이트(long타입 2개, 포인터 타입1개)이기에
240/24=10 으로 [0] 인덱스로부터 10번째의 인덱스를 참조하면 isAdmin을 조작할 수 있게 됩니다.
위 코드에서는 stu[idx - 1].attending = 1 으로
0~9가 아닌 1~10으로 입력받고 있기 때문에 1에서 부터 10번째인 11을 입력해주면
다음과 같이 isAdmin에 접근해 Access granted가 출력됩니다.
배열의 범위를 벗어나는 OBB 취약점은 gcc에서도 어떠한 경고를 띄워주지 않기 때문에
이를 검사하는 코드를 생략하면 발견하기가 매우 어렵고 때에 따라서는 치명적인 취약점이 될 수 있습니다.
Reference
1. https://dreamhack.io/lecture/courses/115
Memory Corruption: Out of Bounds
TBD이번 코스에서는 OOB 취약점이 발생하는 코드의 유형과 OOB를 공격해서 얻을 수 있는 효과에 대해 살펴보겠습니다.
dreamhack.io
'Reference > Pwnable_Study' 카테고리의 다른 글
DFB(Double Free Bug) 취약점 (0) | 2023.01.19 |
---|---|
(Linux) Heap 구조 이해하기 1 (0) | 2023.01.17 |
Pwndocker와 LD_PRELOAD, patchelf (libc 제공된 바이너리 문제) (0) | 2023.01.03 |
RELRO 과 Hook Overwrite, PIE (0) | 2023.01.01 |
fork()와 exev() (0) | 2022.10.13 |