FTZ level13 스택 가드 ( Stack Guard )

2017. 5. 23. 14:39SystemHacking/FTZ

 

 

 

level13 문제는 스택가드에 대한 문제입니다 스택가드란 버퍼오버플로우의 방어책으로 제시된 방법입니다

스택가드의 작동 원리는 간단합니다. 아래 소스를 예로들어서 설명하겠습니다



#include<stdio.h>

#incluse<stdlib.h>

int main(){

long guard = 0x12121212;            // 스택가드의 역할을 수행한다

char str[100];

gets(str);

if ( guard != 0x12121212 ){         // 스택가드의 값이 변하면 해당 프로세스를 종료시킨다

printf(" It is Buffer OverFlow !! Proccess is stopped ");

exit(0);

}

printf(str);

return 0;

}



작성한 소스코드를 기반으로 Stack구조를 그려보았습니다 ( dummy는 무시합니다 )


Stack구조

< 그림 13.1 >



gets() 함수로 문자 입력시 108byte 만큼 버퍼오버플로우 시켜서 RET에 접근하려면 i의 값도 변하게 됩니다

따라서 프로세스가 종료되어 버퍼오버플로우 공격을 막아냈습니다

이러한 원리를 통해서 버퍼 오버 플로우를 방어법을 만들었지만, 이에 대한 파해법 또한 발견되었습니다

이번 문제는 그 파해법을 통해 공격하는 법을 배울 것입니다


문제 풀이 시작하겠습니다. 먼저 힌트를 봅시다


< 그림 13.2 >


공격해야할 attackme 실행파일의 소스코드가 보입니다

[1] setreuid( 3094 , 3094 ) 

해당 함수로 인해서 파일을 실행하는 동안에는 level14의 권한을 가진다


[2] if ( i != 0x01234567 )

스택가드의 역할을 하는 변수 i 입니다 해당 값이 변하면 프로세스는 종료됩니다


[3] kill( 0 , 11 )

프로세스에게 시그널 신호를 보내는 kill 함수입니다

kill( pid_t pid , int signo )

pid         0    : 함수를 호출하는 프로세스와 같은그룹에 있는 모든 프로세스에게 시그널을 전송

-1    : 함수를 호출하는 프로세스가 전송할 수 있는 권한을 가진 모든 프로세스에게 시그널을 전송

양수    : 지정판 프로세스 ID에만 시그널을 전송


[level13 @ftz level13 ]$ kill -l 명령어로 시그널 신호의 종류를 모두 볼 수 있습니다


< 그림 13.3 >


attackme 파일을 gdb분석하려고 했으나 권한이 제한되어 있어서 힌트파일의 소스를 복사해서 새로운 파일을 생성합니다


< 그림 13.4 >


[1] Procedure Prelude

$0x418 == 1048 byte 공간을 지역변수에게 할당해준다    => buf ( 1024byte ) + dummy ( 24byte ) 


[2] <main+29>~<main+39>

setreuid( 3094, 3094 )    =>    해당 프로세스를 실행중에는 level14의 권한을 가진다


[3] <main+47>:    cmpl    $0x1, 0x8(%ebp)

if ( argc > 1 ) 조건식을 비교하는 부분    ( 명령어와 인자의 개수가 1보다 큰 경우 )

     <main+51>:    jle    <main+79>

jle ( Jump if Less or Equal )    :    argc <= 1 인 경우에 <main+79> 지점으로 Jump -> 스택가드검사 -> 프로세스 종료


[4] <main+71>:    call    0x8048308 <strcpy>

strcpy( buf , argv[1] )    :     buf 변수에 프로세스 실행시 입력한 인자값을 복사한다


[5] <main+79>:    cmpl    $0x01234567 , 0xfffffff4(%ebp)

if ( i == 0x01234567 )     스택가드가 변했는지 검사하는 부분입니다

     <main+86>:    je    0x80483f <main+119>

je ( Jump if Equal )    :    스택가드가 변하지 않았다면 <main+119>지점으로 Jump합니다

     <main+119>지점에서부터 시작해서 <main+125>:    ret    리턴주소에서 해당프로세스는 종료됩니다


[6] 

스택가드가 변하여서 <main+119>지점으로 Jump하지못하고 코드가 계속 진행되는 경우입니다

printf( "Warnning: Buffer Overflow !!! \n "); 실행


[7] 

스택가드가 변하여서 <main+119>지점으로 Jump하지못하고 코드가 계속 진행되는 경우입니다

kill ( 0 , 11 )    =>    해당 프로세스를 포함한 모든 프로세스들을 SIGSEGV 시킨다 (Segmentation Fault)



gdb분석을 통해서 프로세스의 흐름을 파악했습니다

그렇다면 택가드 방어법을 어떻게 해결해서 버퍼 오버플로우 공격에 성공할 수 있는지 생각해봅시다방법은 간단합니다

1> 스택가드 변수 " i " 의 주소를 알아냅니다 그리고 그 " i " 의 주소에 "0x01234567" 값을 넣어줍니다

2> 그리고 " i " 공간은 제외하고 버퍼 오버플로우 시켜서 RET주소 까지 도달해서 RET주소에 있는 값을 원하는 값으로 변질시킵니다

3> 그러면 프로세스의 Stack 구조에 대해 파악해봅시다 그림을 그려서 이해하는게 제일 쉽습니다 ㅡ 그림 13.


[ 스택가드 공격법 ] ( 문제 해결 )


먼저 buf 와 SFP, RET 주소를 알아내야합니다. 적당한 BreakPoint지점을 설정하고 프로세스를 실행시킵니다


< 그림 13.5 >


strcpy( buf , argv[1] ) 함수가 실행되고 난 직후에 Break 를 걸어주었습니다 ( argv[1] = AAAA )

why? $esp레지스터 ( Stack 최상위 레지스터 ) 에 buf가 남아있으므로 해당 레지스터를 살펴보면 buf의 메모리구조를 파악할 수 있다

(gdb) x/262xw $esp    =>    최상위 레지스터 esp레지스터의 메모리주소 값을16진수 262개로 표현하라

262개로 화면에 출력하기 부족해서 (gdb) x/280xw $esp 명령어로 재실행했습니다


< 그림 13.6 >

[ 메모리구조 분석 ]

 [1] buf의 주소 "0xbffff240" 

프로세스 실행시 입력한 인자 "AAAA" 가 보입니다. 따라서 buf의 주소는 "0xbffff240" 이 됩니다

여기에 지역변수의 공간으로 할당받은 1048byte를 더하면 SFP의 주소가 나온다 ( 0xbffff240 + 10248byte = 0xbffff658 )


 [2] SFP의 주소"0xbffff658"

해당 주소에 4byte를 더해주면 RET의 주소가 됩니다 ( 0xbffff658 + 4byte = 0xbffff65c )

(gdb) x/x 0xbffff658    => "0xbffff678" : SFP주소안에 들어있는 값


 [3] RET의 주소"0xbffff65c"

(gdb) x/x 0xbffff65c    => " 0x42015564 " : RET주소안에 들어있는 값


=>  0xbffff650:    0x42130a14    dummy *

0xbffff654:    0x40015360    dummy *

0xbffff658:    0xbffff678       SFP

0xbffff65c:    0x42015574     RET


SFP와 RET주소에 들어있는 값을 확인한 결과입니다

< 그림 13. 7 >

 [4] 스택가드의 주소 " 0xbffff64c "

해당 스택가드의 위치에는 현재 " 0x01234567 " 이 존재하고 이상태를 그대로 유지시켜야하니다


 [5] buf의 끝나는 주소 

0xbffff240 + 1024byte = 0xbffff640


=>  0xbffff640:    0x42130a14     dummy *

0xbffff644:    0x4000c660     dummy *

0xbffff648:    0xbffff658       dummy   *

0xbffff64c:    0x01234567         i   




결론적으로 " buf " 와 " i " 사이에 12byte의 dummy가 존재하고있다

또한, " i " 와 " SFP " 사이에 8byte의 dummy가 존재하고 있다 그림으로 나타내보면 다음과 같다


< 그림 13.8 >


메모리 구조 파악이 끝났습니다 이제 오버플로우시킬 문자열만 입력하면 됩니다

1024byte + 12byte만큼 오버플로우 시키고 " i " 변수 공간에는 "0x01234567" 을 덮어줍니다

그리고 다시 8byte + 4byte 만큼 오버플로우 시킨 후 RET주소에 쉘 코드가 작성되어있는 메모리 주소로 변질시킵니다

그러면 level14권한의 setreuid가 걸려있는 상태에서 쉘코드가 실행되고 level14의 쉘이 떨어지게 됩니다



쉘코드 작성법을 아직 몰라서 문제 해결은 못했습니다

쉘코드 작성 공부 후 보충하겠습니다



===================================================================


쉘 코드 작성법 : http://itsaessak.tistory.com/111 <<

에그쉘 ( 쉘코드 주소얻기 ) : http://itsaessak.tistory.com/113 <<


< 그림 13.9 >

먼저 에그셀을 이용해서 환경변수 EGG에 쉘생성코드를 저장시켰습니다

그리고 " /tmp/realaddr " 파일을 통해서 환경변수 EGG의 실제 저장 주소를 얻었습니다

level13의 소스코드가 argv[1] 을 사용해서 " ` " 을 이용해서 " attackme " 파일의 인자로 넣어서 오버플로우 시켯습니다

1036byte만큼 버퍼를 채운 다음 " i " 변수의 공간에 도달하고 i 변수공간에는 0x01234567 을 넣어주었습니다

그리고 나머지 12byte를 문자 "A"로 채우고 RET주소에 환경변수 EGG의 주소를 덮어씌웟습니다

level14의 권한으로 쉘이 떨어짐을 확인이 됩니다




'SystemHacking > FTZ' 카테고리의 다른 글

FTZ level15 분기 루틴 2  (0) 2017.05.23
FTZ level14 분기루틴 1  (0) 2017.05.23
FTZ level12 Buffer OverFlow  (0) 2017.05.23
FTZ level11포맷 스트링 버그를 이용한 풀이  (0) 2017.05.23
FTZ level10 공유메모리  (0) 2017.05.23