2017. 5. 23. 14:38ㆍSystemHacking/FTZ
level9 에서 버퍼오버플로우를 이용해서 RET 메모리 공간을 변질시키는 공격법을 말한 적이 있습니다
level12 에서는 RET 메모리공간의 값을 변질시켜서 level13쉘을 떨어지게 해보겠습니다
문제풀이 들어갑니다
< 그림 12.1 >
힌트에서는 level13권한으로 setuid가 걸려있는 attackme 파일의 소스가 보입니다
level13권한으로 setreuid을 실행하고 있고 gets() 함수로 문자열을 입력받습니다
문자의 길이에 대한 제한이 없어서 오버플로우공격에 대해 취약점을 가지고 있음이 확인됩니다
어떤식으로 공격을 해야할지 파악하려면 gdb로 해당 파일의 메모리 구조를 분석해야합니다
[level13 @ftz level13] gdb // gdb실행
(gdb) file attackme // attackme실행파일 읽어들임
(gdb) disas main // attackme실행파일의 main함수 disassemble
< 그림 12.2 >
[1] Procedure Prelude
0x108 ( 264 ) byte 크기의 공간을 지역변수에게 할당한다
str = 256 byte , dummy = 8byte
[2] <setreuid>
setreuid( 3093 , 3093 ) : level13으로 uid변경하는 함수
[3] <printf>
printf("문자을 입력하세요. \n ");
[4] <gets>
gets(str) : 사용자로부터 문장을 입력받고 str변수에 저장한다
[5] <printf>
printf(str) : str변수에 저장되있는 값을 화면에 출력한다
gdb분석은 마쳣고 이제 오버 플로우 공격을 위해서 필요한 정보를 얻어오겠습니다
필요한 정보는 공격해야할 " RET의 주소값 " 입니다. 아래의 과정을 통해서 RET주소값을 찾아냈습니다
< 오버 플로우 공격점 찾기 >
attackme 파일에 직접 BreakPoint를 걸고 해당 파일을 실행 후 레지스터를 분석하려고 했지만 권한이 제한되어 있습니다.
따라서 hint파일의 소스를 복사해 새로운 실행파일을 생성해서 gdb분석을 해야합니다 ㅡ 그림 12.3 ~ 12.6
< 그림 12.3 >
vi편집기를 통해서 소스를 편집한다
< 그림 12.4 >
gdb를 실행하자
< 그림 12.5 >
main함수를 disassemble 하여 BreakPoint를 걸어줄 지점을 찾아보자
< 그림 12.6 >
[1] Break Point
(gdb) b * 0x080483e9 함수가 진행하다가 해당 지점을 만나면 실행하지 않고 멈춘다
[2] Run
(gdb) run 프로그램을 실행한다
=> 브레이크 포인트를 정해주고 프로세스를 실행했더니 해당지점에서 멈춘 것을 확인할 수 있습니다
=> (gdb) continue or c 명령어를 통해서 계속 실행시킬 수 있습니다
왜 하필 <main+89> 지점에 브레이크포인트를 주었을까?
(1) 그림 12.6에서 <printf> 전 부분을 보시면 <gets>함수가 실행되어 사용자로부터 문자열을 입력받습니다
(2) 입력받은 문자열은 'str' 변수에 저장되고 'str' 변수는 ebp레지스터에 저장되고 eax로 주소가 변경됩니다( lea )
(3) printf함수의 인자로 push되기 전에 eax레지스터를 열어보면 해당 데이터들의 구조를 파악할수 있습니다 ! ㅡ그림 12.7
( esp 레지스터는 스택에서 최상위에 존재하는 레지스터이므로 esp를 열어봐도 확인할 수 있습니다 )
< 그림 12.7 >
[ 그림 12.7 설명 ]
지역변수의 공간은 264byte => str(256byte) + dummy(8byte) 이므로 Stack의 구조는 다음과 같다
Stack구조
주소 : [ str( 0xbffff5d0 ) ] - [ dummy ] - [ SFP( 0xbffff6d8 ) ] - [ RET( 0xbffff6dc ) ]
값 : [ 입력한값 (256byte) ] - [ 쓰레기값 (8byte) ] - [ 0xbffff6f8 (4byte) ] - [ 0x42015574 ]
지역변수의 공간은 264byte 할당되어있으므로 해당 공간을 모두 보려면 16진수주소는 4byte이기 때문에 최소 66개가 필요
넉넉하게 " x/80xw $eax " 명령어를 통해서 eax레지스터를 80개의 16진수로 출력
[1]
주소 " 0xbffff5d0 " 에서 함수를 실행했을 때 입력했던 AAAA 가 보입니다 ( \x41414141 )
=> " 0xbffff5d0 " 은 str 변수의 시작주소임을 알 수 있습니다
[2]
그러면 str변수의 시작주소에 264byte를 더해주면 SFP의 주소가 나올 것입니다
=> SFP주소 : " 0xbffff6d8 "
=> (gdb) x/x 0xbffff6d8 => ' 0xbffff6f8 ' : SFP주소안에 있는 값
[3]
SFP주소에 4byte를 더한 값이 RET주소 입니다 " 0xbffff6dc "
=> (gdb) x/x 0xbffff6dc => ' 0x42015574'
=> RET주소에 있는 값 ' 0x42015574' 를 쉘 실행코드가 있는 메모리 주소로 변질시키면 공격이 성공한다
[4]
변질시키는 방법은 간단합니다
gets()함수에 대해서 256byte + 8byte + 4byte = 268byte만큼 데이터를 입력해서 오버플로우 시킨다
그 다음 RET주소에 쉘 실행코드가 있는 메모리 주소를 입력하면 끝이다
그 전에 오버플로우 공격이 잘 먹히는지 확인해보도록 합시다. python 문법을 이용하겠습니다
< 그림 12.8 >
printf("str") 함수까지 정상적으로 실행되었다
하지만 마지막으로 함수를 호출한 지점으로 리턴하려고 했지만 리턴주소가 변질되어서 Segmentation fault 오류를 출력한다
272byte만큼의 문자 "A"를 입력하여서 RET주소에 들어있던 값이 "0x41414141" 로 변질되었다
"0x41414141"이라는 주소는 존재하지 않아서 오류가 발생한다 !
미리 메모리에 쉘 코드를 올려두고 이 쉘 코드의 주소를 리턴 주소를 덮어씌운다면 level13 권한의 쉘이 떨어질 것이다
* 아직 배시 쉘 코드 작성법을 모릅니다.. 작성법 공부후 작성 하겠습니다
* 의문점 : 0xbffff6d8 은 SFP의 주소인데 왜 str 공간에 해당 주소와 동일한 값이 있는 걸까 ?
===================================================================
드디어 쉘을 생성하는 코드를 리턴 주소에 올려놓는 법을 공부했습니다 !
에그쉘을 이용해 환경변수에 쉘생성코드를 저장시키고 환경변수가 저장된 주소를 리턴 주소에 덮어 씌우면 됩니다 !
( 에그쉘에 관한 내용은 여기 를 클릭하시면 상세히 설명되어 있습니다 )
< 그림 12.9 >
문자 "A"를 268byte만큼 버퍼오버플로우 시켜서 SFP주소까지 A로 덮어씌웠습니다
그 다음 RET(4byte) 공간에 에그쉘을 이용해서 얻어낸 환경변수 EGG의 주소를 덮어씌웠습니다
level13의 권한으로 쉘이 떨어진 것을 확인 할 수 있습니다 ( setreuid(3093,3093)때문 )
'SystemHacking > FTZ' 카테고리의 다른 글
FTZ level14 분기루틴 1 (0) | 2017.05.23 |
---|---|
FTZ level13 스택 가드 ( Stack Guard ) (0) | 2017.05.23 |
FTZ level11포맷 스트링 버그를 이용한 풀이 (0) | 2017.05.23 |
FTZ level10 공유메모리 (0) | 2017.05.23 |
FTZ level9 Buffer OverFlow (0) | 2017.05.23 |