문제분석 & 풀이
bof 코드를 알려주고 있다.
buffer overflow 취약점을 실습하는 첫번째 문제다.
○ 코드 분석
1. buf2[10], buf[10] 배열을 선언한다.
2. fgets()를 이용하여 사용자에게 입력을 받아서 40바이트 만큼 buf[]에 저장한다.
3. strncmp()를 통해 buf2[]의 앞 두자리가 "go"인지 확인하고 맞으면 level10의 password를 알려준다.
사용자가 입력한 값을 buf에 저장하는데, 우리가 값을 넣고 싶은 배열은 buf가 아닌 buf2다.
그렇다면 어떻게 buf2에 "go"를 넣을 수 있을까?
앞에서 언급했듯이 buffer overflow를 이용하면 된다!.
○ BOF (buffer overflow)
메모리에 생성된 버퍼 크기보다 더 많은 양의 입력값을 입력하여 해당 버퍼 외의 메모리의 다른 부분들을 덮어씌우는 것을 말한다.
buf와 buf2가 차례로 선언됐고 사용자에게는 40바이트만큼 입력값을 받는다.
이때, 버퍼오버플로우를 이용하여 buf[10]의 영역인 10바이트를 넘어서 buf2[10] 영역에 원하는 값을 입력하면 된다.
버퍼오버플로우를 이해하기 위해서는 메모리의 구조에 대한 이해가 필요하다. 따라서 level9 문제를 적용한 메모리 예제를 가져왔다.
위 사진은 bof 실행 시, 메모리 내의 스택영역 사진이다.
buf가 최하단 그리고 바로 위에 buf2가 할당된 것을 확인 할 수 있다.
(C 코드에서 buf2, buf 순서로 선언한 것을 토대로 쉽게 추측 가능하다.)
SFP는 main 함수 이전의 EBP를 의미하며, RET은 return address로 main 종료시 return 할 주소다.
(자세한 내용을 알고 싶은 독자는 SFP, RET, EBP, ESP에 대해서 검색하고 반드시 공부해보길 바란다.)
변수를 메모리 공간에 할당하는 것은 윗에서 아랫 방향으로 진행되나, 메모리 내 변수에 값을 저장하는 것은 아래에서 위 방향이다.
즉, buf[10]에 입력값을 넣는 경우에 10바이트 이상의 데이터가 들어온다면 초과된 데이터는 바로 윗공간인 buf2에 저장된다.
이런 방식으로 입력 값을 받을 때, 10바이트 이상의 값을 넣어 buf를 버퍼 오버플로우 시켜서 buf2에 원하는 값을 저장하면 문제가 해결 될 것이다.
버퍼오버플로우시 스택의 예제를 보면 다음과 같다.
ex) 입력 값이 "AAAAAAAAAAgo"인 경우 (A는 더미값)
우리가 원하는 메모리의 상태다.
bof 를 실행해서 "AAAAAAAAAAgo"를 직접 입력해보자.
결과는?
아무것도 뜨지 않는다.
안되는 이유는 스택 그림에서 buf와 buf2가 바로 붙어있다는 가정으로 BOF를 수행했다.
하지만 실제로는 그 사이에 약간의 더미가 존재한다. 따라서 더미값들이 얼마나 존재하는지 정확히 파악해서 payload를 다시 작성해야한다. (물론 브루트포스 형식으로 "A"의 갯수를 늘려가면서 풀 수도 있다.)
이것을 파악하기 위해서는 gdb로 asm 코드를 확인해야한다.
gdb로 디버깅을 시도하면, permission denied가 뜨면서 접근이 거부된다. 현재 디렉토리에서 gdb로 동적분석할 수 있는 권한이 없기 때문이다.
따라서 힌트에서 주어진 bof.c 코드를 가지고 권한이 있는 /tmp 디렉토리로 이동해서 컴파일하고 디버깅 해야한다.
권한이 있는 tmp 폴더로 이동하여 bof.c 파일을 작성했다.
bof.c 작성 후 gcc 컴파일러로 bof 를 생성했다.
bof를 gdb로 디버깅을 시작하면, permission denied 없이 정상적으로 실행된다.
disass main을 입력해서 main의 asm 코드를 확인해보자.
어셈블리어 코드를 분석하고 buf와 buf2 사이에 존재하는 더미 길이를 알아보자.
알아보기 쉽도록 빨간색과 파란색으로 표시했다.
파란색은 fget()가 호출되는 부분으로 fget()가 호출되고 입력 받기 전까지 얼마만큼의 메모리 공간이 할당됐는지 확인해야한다.
빨간색들은 메모리에 공간을 할당하는 코드다.
1. 스택 공간을 0x28 만큼 할당했다.
2. 스택 공간을 %eax 만큼 할당했다. eax는 0이므로 아무 의미 없는 코드이다.
3. 스택 공간을 0xc 만큼 할당했다.
4. 스택 공간을 0x4 만큼 할당했고 fget()로 입력값을 입력 받는다.
여기서 3번의 공간 할당을 크게 두 개로 나눠서 생각해볼 수 있다.(아무 의미 없는 두번째 할당은 제외하겠다.)
buf2[10] 공간할당 → 1번째
buf2[10] 이후 공간 할당 → 3,4번째
buf2[10] 이후에는 3,4번째 할당이 이뤄졌다. 즉, 0xc + 0x4 만큼 공간이 할당된 것이다.
따라서 0xc + 0x4만큼의 더미를 추가해야 버퍼오버플로우를 통해 buf2에 도달 할 수 있다.
0xc+0x4를 10진수로 바꾸면 12 + 4 = 16이다.
즉, 16바이트만큼의 더미와 "go"를 합친 페이로드를 작성해야한다.
bof 프로그램을 실행하여 "AAAAAAAAAAAAAAAAgo" 라는 payload를 입력했다.
level10의 쉘을 얻을 수 있다.
password를 확인하자.