[7] Memory Corruption - C (1) :: Out Of Boundary(OOB)

본 글은 DreamHack의 강의 내용을 요약한 글이므로 자세한 내용은 dreamhack.io 에서 학습하시길 바랍니다. 

 

해커들의 놀이터, Dreamhack

해킹과 보안에 대한 공부를 하고 싶은 학생, 안전한 코드를 작성하고 싶은 개발자, 보안 지식과 실력을 업그레이드 시키고 싶은 보안 전문가까지 함께 공부하고 연습하며 지식을 나누고 실력 향

dreamhack.io

 

학습목표

Out Of Boundary 취약점에 대해 학습 후 실습하기

 

 

1. Out Of Boundary

버퍼의 길이 범위를 벗어나는 인덱스에 접근할 때 발생하는 취약점

 

 

2. Out Of Boundary 실습

실습 1.  배열 buf의 범위를 벗어나는 index로 OOB 발생 시키기

[기본코드]

출처 : dreamhack.io

- oob-1.c

code flow는 다음과 같다.

(1) 10개의 index를 가진 int형 배열 buf를 선언 후 값을 넣고 싶은 index 번호를 idx 변수에 scanf로 받는다.

 

(2) idx에 들어간 정수와 동일한 buf의 index에 입력할 값을 넣고, index 번호와 입력된 값을 출력한다.

 

(3) if 조건식을 보면 buf의 범위을 넘어서 win 영역 메모리에 접근하여 31337이라는 값을 넣게 되면, redacted된 값이 leak 된다는 것을 알 수 있다.

 

풀이

buf 배열의 경우 하나의 index당 4바이트를 차지하는 총 40바이트 배열이다.

buf의 정상적인 index 범위는 0~9를 가지는데, 접근하려는 index 번호를 scanf로 idx에 받을 때 올바른 index 범위에 해당하는 값인지 검증하는 코드가 없다. (경계 검사 비존재)

따라서 scanf로 idx 값을 받을 때, 10 이상의 값을 넣게 되면 stack에서 buf 배열 이후의 메모리로 4바이트씩 이동하며 접근이 가능하게 된다.

 

idx 값(buf index 설정값)을 11로 설정하고 들어갈 value를 31337로 주게 되면 쉽게 leak이 가능하다.

아래의 stack 그림을 보면, buf 시작 주소에서부터  4바이트씩 11번 이동한 위치에 있는 win영역에 접근했다는 것을 알 수 있다.

 

 ※ 11번 이동했다고 표현한 이유

배열에서 buf[5]는 *(buf+5)와 동일한 표현인데, 여기서 +5는 일반 산수연산이 아닌 주소 연산으로, 4바이트 * 5 = 총 20바이트를 이동하는 것을 뜻한다.

참고 글 : https://blog.naver.com/tipsware/220992959841

 

배열의 이름은 배열의 시작 주소이다!

1. 이 글을 읽기 전에 봐야 할 내용 이 글은 아래에 링크한 글에 연결되는 내용입니다. 따라서 아래의 글을...

blog.naver.com

 

실습 2.  음수를 이용한 OOB 발생 시키기

[기본코드]

출처 : dreamhack.io

- oob-2.c

oob-1.c와 비슷한 code flow에 한 줄의 코드가 추가되었다.

scanf로 받은 idx에 idx % 10 연산 결과 값을 저장한다.

 

OOB 발생 여부를 판단하기 위해서는

(1) buffer의 index로 사용할 수 있는 올바른 값의 범위 -> 주어진 버퍼 길이 -1 (index는 0부터 시작)

(2) index가 될 수 있는 값의 범위 -> 위에서 추가된 코드와 같이 특정 연산을 통해서 buffer의 정상 범위 내 값을 도출하는 경우

 

이 두 가지를 비교하면 되는데, (2)에서 나온 값의 범위가 buffer의 올바른 값의 범위의 부분 집합이면 안전하다고 판단한다.

 

그렇다면, 위의 추가된 코드는 OOB 발생을 방지할 수 있는지 확인해 보자.

 

풀이

얼핏 보면 idx에 어떤 값을 넣더라도 %10 연산으로 인해 무조건 0~9 사이의 값이 buf의 범위를 넘지 않을 것 같다.

그러나,  c언어에서는 피연산자가 음수일 경우, %연산의 결과 또한 음수로 나올 수 있다.

 

따라서 scanf 함수로 idx에 -1을 넣고, value를 31337로 하면, win 영역을 침범할 수 있게 된다.

stack 그림으로 보면, index를 -1로 인식하여 buf[0] 주소보다 4바이트 낮은 주소의 win 영역에 접근한 것을 볼 수 있다.

💡 배열 변수는 stack 기본 배치대로 push되지만, 배열 내에서의 index data들은 낮은 주소에서 높은 주소로 데이터가 들어간다.

 

실습 3.  정수 표현 범위를 이용한 OOB 발생 시키기

[기본코드]

출처 : dreamhack.io

- oob-3.c

이번에는 음수를 이용해서 OOB를 일으킬 수 없도록 idx 값이 음수일 경우 이를 양수로 변환해주는 코드가 추가되었다.

이로써 OOB 발생은 완벽히 막을 것 같지만 잘 생각해보면 허점은 있다.

 

(1) int형 정수 표현 가능 범위 

4byte int의 정수 표현 가능 범위는 -2,147,483,648 ~ 2,147,483,647 이다.

 

(2) 만일, idx 값에 -2,147,483,648 값을 넣게 되면 line14의 절대값 구하는 연산이 수행된 후의 값이

2,147,483,6484byte int의 표현 범위를 넘어서게 된다.

 

(3) 각 자료형의 표현 범위를 넘어서 증가하게 되면, 최솟값으로 돌아가 다시 증가하며 저장된다.

왜 0x8000이 최솟값인지에 대한 참고 글 : https://modoocode.com/308 

 

씹어먹는 C 언어 - <4 - 2. 컴퓨터가 음수를 표현하는 방법 (2의 보수)>

 

modoocode.com

 

자료형 overflow/ underflow 참고 글 : https://dojang.io/mod/page/view.php?id=32 

 

C 언어 코딩 도장: 7.2 오버플로우와 언더플로우 알아보기

만약 정수 자료형에서 저장할 수 있는 범위를 넘어서면 어떻게 될까요? 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요. integer_overflow.c #include int main() { char num1 = 128; // char에 저장할 수

dojang.io

 

즉, idx가 -2,147,483,648일 경우 idx = -idx; 연산 값은 

 -2,147,483,648 = -2,147,483,64

이 나오게 되는 것이다.

 

풀이

idx 값을 -2,147,483,648로 주고 value를 31337로 입력하면 redacted된 값을 얻을 수 있다.

stack을 보면 win은 buf의 시작 주소에서 32byte(8*4byte) 낮은 주소에 위치한다.

win에 접근할 수 있는 이유는 idx % 10을 한 연산의 결과가 -2,147,483,648 % 10의 나머지 연산 결과값이 -8이므로

buf - 8 주소 연산이 수행된 결과로 win 주소까지 갈 수 있기 때문이다.

 

3. Out Of Boundary 대응책

(1) 음수를 이용한 oob 발생을 막기 위해 Unsigned Int 형으로 선언

(2) idx를 입력받은 후 if문을 활용하여 idx가 0보다 작거나 (음수) 또는 주어진 버퍼길이 범위 이상의 값(idx >=10)인지 경계를 검사하는 구문을 추가해야 한다.

 

 

출처 : Memory Corruption - C (I) (DreamHack, https://dreamhack.io/learn/2/14#10)