ftz level11을 풀어보겠습니다.
우선 힌트 조회 입니다.
힌트에는 C언어가 있습니다. strcpy 명령을 보니 버퍼오버플로를 생각할 수 있습니다.
그리고 attackme라고 자신을 공격해 달라는 파일이 떡하니 있습니다. 힌트로 코딩된 파일로 유추할 수 있습니다.
attackme 파일을 실행해보면 뒤에 나오는 인자를 그냥 그대로 출력해 줍니다. 배열의 크기가 256이니 그 보다 큰 인자를 넣어서 버퍼 오버플로를 일으킨다고 생각하면 될거같습니다.
본격적인 작업을 하기전에 디버깅을 위해서 attackme 파일을 tmp 디렉터리 안으로 옮겨줍니다. 저는 attack이라는 파일로 복사하였습니다.
메모리 분석을 위하여 gdb [파일명] 을 사용하였습니다. gdb에 관한 명령어는 아래 URL을 참조하시기 바랍니다.
http://psyoblade.egloos.com/2653919
disas main 을 통하여 main함수를 디스어셈블링하여 진행상황을 자세히 보았습니다.
주목해야 될 부분은 main+3, main+48 입니다.
우선 main +3는 sub $0x108, %esp 라고 나와있는데 이는 16*16+8=264 바이트 배열이 할당됨을 알 수 있습니다.
264중 256은 배열로 할당되었기 때문에 8바이트의 dummy가 있다고 유추할 수 있습니다.
그 밑에 main +9는 sub $0x8, %esp 라고 나와있습니다. 이는 SFP(base pointer)(이전 함수 주소) 4바이트, RET(return address)(다음 실행함수 주소) 4바이트로 할당 됩니다. 기본적으로 메모리 구조와 스택 세그먼트는 알아야 버퍼오버플로우를 진행할 수 있습니다.
main+48 은 strcpy 함수를 호출하는 부분으로 버퍼오버플로의 취약점 구간 이라고 할 수 있습니다.
그렇다면 대충 배열의 스택은 아래와 같이 구성됩니다.
256(배열)+8(dummy)+4sfp(base pointer)+4ret(return address)=272
배열만으로는 자체적으로 함수를 실행할 수 없고 더미는 쓰레기값, sfp는 이전함수 포인터기 때문에 ret에 쉘코드로 가는 주소를 삽입하여 쉘코드를 실행시켜 setuid를 획득하도록 하겠습니다.
먼저 NOP방식의 버퍼오버플로우 입니다.
위와 같이 strcpy 호출 직후에 브레이크를 걸고 실행후에 "A" 264바이트(256배열+8더미)+ "B" 4바이트(sfp) + "C" 4바이트(ret)를 파이썬을 통해 입력 후에 해당 ABC가 입력된 실제주소를 구해 A+B 부분에 NOP 와 쉘 코드 를 심어 C에는 해당 NOP 주소를 심어 쉘코드를 실행시키는 방향으로 가려 했습니다. 아래 그림으로 다시 설명하겠습니다.
쉘을 실행시키는 코드는 인터넷이나 책에서 쉽게 찾을 수 있습니다. 저는 25바이트 크기의 쉘을 쓸것이기 때문에 계산을
NOP(243)+SHELLCODE(25)+returnaddress(4)=총 272 로 하였습니다. 물론 NOP(100)+SHELLCODE(25)+NOP(143)+returnaddress(4) 와 같은 형식도 가능합니다. NOP(\x90)는 쉽게 말해서 실행할 수 있는 인스트럭션이 나올때까지 한바이트씩 넘어가는 코드입니다. 그래서 그림과 같이 return address에 도달하면 (return address에 NOP코드 주소중 하나를 적어놓습니다.) NOP주소중 하나로 이동해 실행 가능한 인스트럭션이 나올때 까지 한바이트씩 이동합니다. 이동하다가 쉘코드를 만나면 해당 쉘을 실행하는 방식입니다.
그러나 매 실행시마다 랜덤 스택으로 할당되기 때문에 주소가 바뀌어서 이 방법은 노가다를 해야된다는 것을 깨달았습니다. 미리 알아놓은 주소로 실행되기를 바라면서 계속 해줘야 하기 때문입니다.
그래서 쉘코드를 환경변수로 만들어 해당 환경변수 주소를 return address에 놓는 방법을 하였습니다.
환경변수는 export 명령어로 만들 수 있습니다.
export [변수]=내용
쉘코드는 아래 25바이트를 올려놓았습니다.
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80
\x90(NOP)는 줘도 되고 안줘도 됩니다. 저는 50바이트 주고 뒤에 쉘코드를 넣었습니다.
*주의할점은 가장 바깥에는 ` 프린트 앞에는 ' 해당코드는 " 입니다. 셋다 다릅니다.
변수를 지정하였으면 export | grep [변수이름] 을 하여 생성 여부를 확인 합니다. 확인 완료 되었으면
만든 변수의 정확한 주소를 알기위한 C 코드를 만들겠습니다. tmp 디렉터리내에 locate.c 를 생성하여 해당 코드를 넣어주었습니다.
#include <stdio.h>
int main(){
printf("%x\n", getenv("GS"));
return 0;
}
getenv 함수는 해당 변수의 정보를 얻어 올 수 있는 함수 입니다.
코드 작성이 완료되었으면 gcc로 컴파일 후에 실행시켜 줍니다.
주소는 bffffe38 이 나왔습니다. 위 NOP 방식처럼 쉘코드를 넣어주지 않아도 되고 NOP도 굳이 필요 없으니 ret 부분을 제외하고 아무거나 넣어 줍니다.(272-4=268바이트)
저는 NOP도 넣어보고 A도 넣어 보았습니다. 268바이트 뒤에는 아까 구한 환경변수의 주소를 넣는데 big endian(bf ff fe 38)과 little endian(38 fe ff bf) 방식중 little endian 방식으로 넣어야 합니다.
최종적으로 level11 디렉터리 이동후에
./attackme `python -c 'print "A"*268+"\x38\xfe\xff\xbf"'`
을 실행하면 쉘권한을 얻을 수 있습니다. 그 다음 my-pass를 입력하면 다음레벨의 패스워드가 나옵니다.
ftz level11을 풀어보았습니다.
'Hack > ftz' 카테고리의 다른 글
ftz 풀이 level13 (0) | 2017.01.02 |
---|---|
ftz 풀이 level12 (0) | 2016.12.31 |
ftz 풀이 level10 (0) | 2016.12.12 |
ftz 풀이 level9 (0) | 2016.12.07 |
ftz 풀이 level8 (0) | 2016.12.01 |
댓글