FTZ란 Free Training Zone의 약자로 hackerschool에서 배포하는 워게임입니다.
총 20문제로 구성되어 있으며 각 문제를 격파하여 다음 레벨의 패스워드를 얻는 형식입니다.
문제를 격파하고 다음 레벨의 bash를 획득하였다면 my-pass로 패스워드를 확인할 수 있습니다.
사용 linux 명령어
ls - 파일 내역 출력
cat - 파일 내용 확인
cp - 파일 또는 디렉터리 복사 명령
gdb - 디버깅 도구
objdump - objdump는 ELF 파일을 어셈블리어로 보여주는 역어셈블러로 사용될 수 있다.
egrep - 파일에서 특정한 패턴을 찾는 명령어
send me email if you have any questions.
FTZ Level 19
ID : level19
PASSWD : swimming in pink
hint를 보기 전 attackme의 gid가 level19임을 확인하였다.
이는 level20의 권한으로 attackme를 실행할 수 없다는 뜻이고, 무언가의 방법으로 attackme에 level20의 set-uid를 걸어야 한다.
이전 문제들에서 봐왔듯이 setreuid(pm1,pm2); 형태의 set-uid를 설정하는 함수가 있고 이를 이용해보자.
1
2
3
4
5
6
main()
{ char buf[20];
gets(buf);
printf("%s\n",buf);
}
level11부터는 attackme라는 파일이 존재하며 hint는 attackme의 소스가 제공된다.
소스분석
별다른 특징없이 buf에 buffer over-flow를 시도할 수 있다.
하지만 level20의 권한이 없으므로 RTL공격을 이용해 main의 ret에 setreuid 주소를 덮고 setreuid의 ret에 system함수의 주소를 덮어 /bin/bash를 실행하도록 해보자.
RTL?
Return To Library 즉 라이브러리로 돌아가라! 라는 의미를 가지고 있다.
main의 ret에 Library 함수의 주소를 덮는 방법으로 코드에서 사용하지 않는 함수를 실행시킬 수 있으며 인자전달 또한 가능하다.
DEP 도는 NX-bit라는 메모리 기법에 의하여 방어가 가능하다. (메모리 보호기법 종류)
RTL Chaining?
RTL기법을 응용하여 라이브러리 함수의 호출을 연계하는것으로 RTL기법에서 하나의 함수를 호출했다면, RTL Chaining에서는 여러 함수를 연계하여 호출한다.
원리 : RTL에서 페이로드중 RET뒤에 4byte의 dummy값을 채웠지만 이부분에 pop ret과 같은 명령어의 주소를 넣어 스택의 포인터를 다음 함수로 위치시키는 것
ftz에는 ASLR(메모리 보호기법)이 걸려있다.
ASLR은 프로그램을 실행할 때 마다 메모리 주소가 변경되는 메모리 보호기법
권한문제가 없도록 tmp디렉터리에 attackme를 복사하고 attackme의 메모리 구조를 파악하기 위해 gdb를 사용하여 intel방식으로 디스어셈블 한다.
어셈블리 분석
먼저 level20의 id값을 확인한다. -> 3100
main+3 line을 보아 스택에는 40Byte만큼의 공간이 할당되었다.
main+24 line을 보아 buf의 주소는 ebp-40임을 알 수 있다.
따라서 buf~sfp까지는 44Byte이므로 ret에 접근하기 위해 필요한 dummy값의 크기를 알 수 있다.
p는 print명령으로 library함수의 주소를 확인할 수 있다.
No Symbol error이 발생할 경우 main에 break point를 걸어주고 run명령으로 실행시켜준 뒤에 정상적으로 확인이 가능하다.
attackme에 set-uid를 걸어주기 위해 lib함수인 setreuid의 주소를 확인한다.
setreuid의 ret에 덮을 system함수의 주소를 확인한다.
print명령으로 RTL Chaining에 필요한 setreuid와 system함수의 주소를 확인한다.
이 setreuid의 주소를 main의 ret에 덮고 setreuid의 ret에 system함수의 주소를 덮어 함수를 연쇄적으로 호출 할 것이다.
No Symbol error가 발생할 경우 main에 break를 걸고 한번 실행한 뒤 다시 시도해보자.
일반적인 함수호출과 비정상적인 함수호출의 차이점
일반적으로 push eip, jmp “함수 주소”의 형태로 함수호출이 진행된다.
하지만 ret에 함수의 주소를 덮는 형태로 비정상적인 함수 호출을 하게 되면 push eip의 명령 없이 함수의 주소로 jump하게 된다.
EIP(Instruction Pointer)는 다음 명령어의 위치를 저장하는 레지스터이다.
push eip없이 해당 함수로 jump하면 sfp위의 ret에 되돌아갈 주소가 저장되지 않는다.
why?
함수의 에필로그는 leave와 ret으로 끝난다.
즉, leave : mov esp, ebp + pop ebp, pop eip순서로 명령이 실행된다.
함수의 프롤로그는 push ebp, mov esp, ebp으로 실행된다.
그렇다면 스택을 생각해 보았을 때 setreuid를 비정상적으로 호출하면 함수의 프롤로그가 진행될 때 eip가 없다. 즉 sfp위에 ret이 존재하지 않는다.
정상적인 함수(main) |
---|
인자1 |
인자2 |
ret |
sfp |
변수 |
… |
비정상적인 호출(system 함수) 후 |
---|
인자1 |
인자2 |
sfp |
ret(system함수주소) |
sfp(이전 ebp) |
버퍼 |
변수 |
… |
비정상적 호출 함수의 에필로그가 진행될 때 leave가 끝난 시점 esp가 인자2를 포인팅하게 된다.
-> mov esp, ebp로 esp와 ebp가 sfp를 가리키지만 pop ebp로 esp+4가 진행되어 인자 2를 포인팅.
따라서 ret이 진행되면서 인자2의 값을 pop해버린다.
RTL을 이용해 buffer over-flow공격을 진행하려면 ret(system함수 주소) + dummy + /bin/bash의 형태로 페이로드를 작성한다.
RTL Chaining을 하려면?
setreuid와 system함수를 연계하여 호출하면 페이로드는
[buffer] + [sfp] + [setreuid함수 주소] + [system함수 주소] + [“3100”] + [“3100”]이다.
하지만 의도와는 달리 system함수가 호출될 때 esp+8의 3100이 system함수의 인자로 들어가게 된다.
따라서 system함수에 /bin/bash를 넣기 위해 가젯(gadget)이라는 것을 이용한다.
gadget은 ret으로 끝나는 연속된 명령을 의미
함수의 인자들을 정리하기 위해 gadget을 사용하는데 자세히 말하자면 우리가 입력한 next return address를 가리키기 위해 사용했던 인자들을 pop하고 esp를 next return address가 있는 스택에 맞추어 리턴하기 위해 사용한다.
[buffer] + [sfp] + [setreuid함수 주소] + [gadget] + [“3100”] + [“3100”] + [system함수 주소] + [dummy] + [/bin/bash]로
페이로드를 구성하면 setreuid가 끝나고 esp는 gadget을 가리키게 된다.
gadget을 pop pop ret을 가젯으로 사용하면 setreuid에 사용한 인자가 pop되며 esp는 system함수의 주소를 포인팅하며 ret(pop eip)를 하고,
system함수에 /bin/bash를 인자로 넣게 된다.
문제를 해결하기 위해 필요한 정보들
buf~sfp끝 거리 -> 44Byte
setreuid 주소 -> 0x420d7920
system 주소 -> 0x4203f2c0
setreuid의 인자 1,2 -> 3100 -> 0x00000c1c
ppr(pop pop ret) 주소 -> ?
/bin/bash의 주소 -> ?
objdump -d 옵션은 디스어셈블 옵션이다.
attackme를 디스어셈블하여 pop|ret 패턴을 찾아 pop + pop + ret으로 이루어진 부분의 주소를 파악한다.
-> 804849d
위의 소스는 system함수의 내부에 들어있는 “/bin/sh”/의 주소를 얻는 프로그램이다.
system함수의 시작주소부터 메모리 주소를 1씩 증가시켜 “/bin/sh”를 찾을 때 까지 반복하는 소스.
위에서 작성한 소스를 실행하여 /bin/bash의 주소를 획득한다.
-> 0x42127ea4
RTL Chaining을 위해 필요한 모든 정보를 수집하였기 때문에 바로 공격을 진행하여 level20의 bash를 탈취하고, passwd를 획득한다.
페이로드
[buffer] + [sfp] + [setreuid함수 주소] + [gadget] + [“3100”] + [“3100”] + [system함수 주소] + [dummy] + [/bin/bash]
(python -c “print ‘\x90’ * 44 + ‘\x20\x79\x0d\x42’ + ‘\x9d\x84\x04\x08’ + ‘\x1c\x0c\x00\x00’ + ‘\x1c\x0c\x00\x00’ + ‘\xc0\xf2\x03\x42’ + ‘\x90’*4 + ‘\xa4\x7e\x12\x42’“;cat) | ./attackme