level12 --- it is like this
우선 level12의 디렉터리를 확인한다.
attackme라는 의심스러운 파일이 존재한다. attackme는 level13의 권한을 가지고 있고, setuid가 걸려있는 파일이다.
우리가 공격할 대상이 attackme라는 생각을 가지고 hint를 보자.
배열이 선언되어있고, gets() 함수를 통해서 사용자로부터 입력된 문자열을 str 배열에 저장한다.
gets() 함수는 사용자로부터 받은 문자열의 길이를 측정하지 않는 함수이다.
만일 사용자가 300 byte 길이의 문자열을 입력하면 어떻게 될까?
gets() 함수는 문자열의 길이를 측정하지 않기 때문에 배열의 범위를 넘어 다른 메모리를 침범한다.
이러한 상황에서 발생할 수 있는 취약점은 버퍼 오버플로우 취약점이다.
버퍼 오버플로우 취약점을 가지느 간단한 예제 소스를 보고 메모리 구조를 알아보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { char str[256]; char *ptr; int a; printf("insert string!!!"); gets(str); printf("%s\n", str); } | cs |
위와 같이 소스를 작성하면 stack 영역에 메모리가 어떻게 쌓일까??
위와 같은 그림은 지역변수만 쌓이는 그림이다.
전에 설명했던것과 같이 stack을 거꾸로 자라는 구조이다.
그렇다면 함수, 프로그램이 실행될 때 stack 메모리 구조는 어떻게 될까???
마찬가지로 환경변수, 인자, 리턴주소, 베이스 포인터(SFP), 지역변수 순으로 메모리에 올라간다.
함수가 끝나면 RET을 pop하여 eip에 저장한 뒤 eip에 의해 다음 실행될 메모리 주소로 이동한다.
만일 이러한 메모리 구조에서 gets() 함수의 특성 때문에 사용자가 256 byte를 초과한 문자열을 입력하면 어떻게 될까?
260 byte의 문자열을 입력하면 SFP를 덮어쓰고, 264 byte의 문자열을 입력하면 RET을 덮어쓴다.
일반적으로 어떠한 문자를 정확히 256개 입력하기란 어렵다.
이러한 경우 스크립트 언어를 사용하여 특정 길이의 문자열을 입력할 수 있다.
하지만 4 byte씩 증가시키면서 출력을 해보면 264 byte를 출력했을 때 RET을 덮어 써야 하는데 268 byte를 출력하니 segmentation fault가 발생하였다.
8 byte의 공간이 dummy 값으로 컴파일러가 최적화를 위해 만들어낸 공간이다.
segmentation fault는 RET값 읽어들여 실행할 때 정상적인 코드가 아니라면(여기선 0x41414141) 오류를 출력하는 것이다.
지금까지 공부한 것을 바탕으로 level12의 attackme를 분석해보자.
우리는 분명 256 byte(0x100) 크기의 배열을 선언했는데, 실제로 264 byte(0x108) 크기가 할당 되었다.
즉, 지역변수를 메모리에 올리기 전에 8 byte의 dummy가 컴파일 최적화를 위해 사용된 것이다.
attackme에서 배시셸을 실행시키기 위해서 256 byte 크기의 배열에 쉘코드를 작성하고 RET을 256 byte 배열의 주소로 덮어쓰면 배시셸이 실행될 것이다.
str 배열로부터 272 byte번째에 RET이 있다.
따라서 이 부분을 shell code의 시작주소 (여기서는 환경변수 주소)로 덮어써 shell을 띄울 수 있다.
'Wargame > FTZ' 카테고리의 다른 글
해커스쿨 FTZ level14 (0) | 2018.08.02 |
---|---|
해커스쿨 FTZ level13 (스택 가드) (0) | 2018.08.02 |
해커스쿨 FTZ level11 (포맷 스트링 버그) (0) | 2018.08.01 |
해커스쿨 FTZ level10 (0) | 2017.09.02 |
해커스쿨 FTZ level9 (0) | 2017.08.21 |