어쨌든 5번 문제의 화면은 요렇다.
한숨만 나온다.
소스를 보기로 하자.
<html> <head> <title>Challenge 5</title> </head> <body bgcolor=black> <center> <font color=black> .<p> .<p> .<p> .<p> .<p> .<p> .<p> </font> <input type=button value='Login' style=border:0;width:100;background=black;color=green onmouseover=this.focus(); onclick=move('login');> <input type=button value='Join' style=border:0;width:100;background=black;color=blue onmouseover=this.focus(); onclick=no();> <script> function no() { alert('Access_Denied'); } function move(page) { if(page=='login') { location.href='mem/login.php'; } } </script> </center> </body> </html>
일단, join은 누르기만 하면 무조건 no를 출력하게 되어 있는 쓸모없는 버튼이다.
login은 누르면 mem/login.php로 이동되게 되어 있으니 이동해보자.
로그인 화면이 하나 나오긴하는데 아무거나 입력해보면 이렇게 나온다.
admin 이라는 이름의 아이디로 로그인할 수 있다는 것이겠다.
하지만 우린 가진게 없다. 간단한 인젝션을 시도해봤으나 일단은 다 막혀있는 것 같다.
다시 메인으로 돌아가서, 왜 join을 굳이 no로 처리해두었을까?
혹시 mem/join.php가 존재하진 않을까? 빙고.
하지만 화면엔 아무것도 없다.
소스보기를 하면 다음과 같은 소스가 나온다.
<html> <title>Challenge 5</title></head><body bgcolor=black><center> <script> l='a';ll='b';lll='c';llll='d';lllll='e';llllll='f';lllllll='g';llllllll='h';lllllllll='i';llllllllll='j';lllllllllll='k';llllllllllll='l';lllllllllllll='m';llllllllllllll='n';lllllllllllllll='o';llllllllllllllll='p';lllllllllllllllll='q';llllllllllllllllll='r';lllllllllllllllllll='s';llllllllllllllllllll='t';lllllllllllllllllllll='u';llllllllllllllllllllll='v';lllllllllllllllllllllll='w';llllllllllllllllllllllll='x';lllllllllllllllllllllllll='y';llllllllllllllllllllllllll='z';I='1';II='2';III='3';IIII='4';IIIII='5';IIIIII='6';IIIIIII='7';IIIIIIII='8';IIIIIIIII='9';IIIIIIIIII='0';li='.';ii='<';iii='>';lIllIllIllIllIllIllIllIllIllIl=lllllllllllllll+llllllllllll+llll+llllllllllllllllllllllllll+lllllllllllllll+lllllllllllll+ll+lllllllll+lllll; lIIIIIIIIIIIIIIIIIIl=llll+lllllllllllllll+lll+lllllllllllllllllllll+lllllllllllll+lllll+llllllllllllll+llllllllllllllllllll+li+lll+lllllllllllllll+lllllllllllllll+lllllllllll+lllllllll+lllll;if(eval(lIIIIIIIIIIIIIIIIIIl).indexOf(lIllIllIllIllIllIllIllIllIllIl)==-1) { bye; }if(eval(llll+lllllllllllllll+lll+lllllllllllllllllllll+lllllllllllll+lllll+llllllllllllll+llllllllllllllllllll+li+'U'+'R'+'L').indexOf(lllllllllllll+lllllllllllllll+llll+lllll+'='+I)==-1){alert('access_denied');history.go(-1);}else{document.write('<font size=2 color=white>Join</font><p>');document.write('.<p>.<p>.<p>.<p>.<p>');document.write('<form method=post action='+llllllllll+lllllllllllllll+lllllllll+llllllllllllll+li+llllllllllllllll+llllllll+llllllllllllllll +'>');document.write('<table border=1><tr><td><font color=gray>id</font></td><td><input type=text name='+lllllllll+llll+' maxlength=5></td></tr>');document.write('<tr><td><font color=gray>pass</font></td><td><input type=text name='+llllllllllllllll+lllllllllllllllllllllll+' maxlength=10></td></tr>');document.write('<tr align=center><td colspan=2><input type=submit></td></tr></form></table>');} </script> </body> </html>
난독화된 코드가 반겨준다. 무엇을 의미하나?
deobfuscation를 찾아보자. 나는 firefox addon을 설치해서 돌려봤다.
l = 'a'; ll = 'b'; lll = 'c'; llll = 'd'; lllll = 'e'; llllll = 'f'; lllllll = 'g'; llllllll = 'h'; lllllllll = 'i'; llllllllll = 'j'; lllllllllll = 'k'; llllllllllll = 'l'; lllllllllllll = 'm'; llllllllllllll = 'n'; lllllllllllllll = 'o'; llllllllllllllll = 'p'; lllllllllllllllll = 'q'; llllllllllllllllll = 'r'; lllllllllllllllllll = 's'; llllllllllllllllllll = 't'; lllllllllllllllllllll = 'u'; llllllllllllllllllllll = 'v'; lllllllllllllllllllllll = 'w'; llllllllllllllllllllllll = 'x'; lllllllllllllllllllllllll = 'y'; llllllllllllllllllllllllll = 'z'; I = '1'; II = '2'; III = '3'; IIII = '4'; IIIII = '5'; IIIIII = '6'; IIIIIII = '7'; IIIIIIII = '8'; IIIIIIIII = '9'; IIIIIIIIII = '0'; li = '.'; ii = '<'; iii = '>'; lIllIllIllIllIllIllIllIllIllIl = lllllllllllllll + llllllllllll + llll + llllllllllllllllllllllllll + lllllllllllllll + lllllllllllll + ll + lllllllll + lllll; lIIIIIIIIIIIIIIIIIIl = llll + lllllllllllllll + lll + lllllllllllllllllllll + lllllllllllll + lllll + llllllllllllll + llllllllllllllllllll + li + lll + lllllllllllllll + lllllllllllllll + lllllllllll + lllllllll + lllll; if (eval(lIIIIIIIIIIIIIIIIIIl).indexOf(lIllIllIllIllIllIllIllIllIllIl) == -1) { bye; } if (eval(llll + lllllllllllllll + lll + lllllllllllllllllllll + lllllllllllll + lllll + llllllllllllll + llllllllllllllllllll + li + 'U' + 'R' + 'L').indexOf(lllllllllllll + lllllllllllllll + llll + lllll + '=' + I) == -1) { alert('access_denied'); history.go(-1); } else { document.write('<font size=2 color=white>Join</font><p>'); document.write('.<p>.<p>.<p>.<p>.<p>'); document.write('<form method=post action=' + llllllllll + lllllllllllllll + lllllllll + llllllllllllll + li + llllllllllllllll + llllllll + llllllllllllllll + '>'); document.write('<table border=1><tr><td><font color=gray>id</font></td><td><input type=text name=' + lllllllll + llll + ' maxlength=5></td></tr>'); document.write('<tr><td><font color=gray>pass</font></td><td><input type=text name=' + llllllllllllllll + lllllllllllllllllllllll + ' maxlength=10></td></tr>'); document.write('<tr align=center><td colspan=2><input type=submit></td></tr></form></table>'); }
변수들은 설명되어 있는데 사실 브라우저에서 console.log를 찍어보면된다.
수정해서 다시 코드를 보자
lIllIllIllIllIllIllIllIllIllIl = 'o'+ 'l' + 'd' + 'z' + 'o' + 'm' + 'b' + 'i' + 'e'; lIIIIIIIIIIIIIIIIIIl = 'd' + 'o' + 'c' + 'u' + 'm' + 'e' + 'n' + 't' + '.' + 'c' + 'o' + 'o' + 'k' + 'i' + 'e'; if (eval(lIIIIIIIIIIIIIIIIIIl).indexOf(lIllIllIllIllIllIllIllIllIllIl) == -1) { bye; } if (eval(document.URL).indexOf(mode=1) == -1) { alert('access_denied'); history.go(-1); } else { document.write('<font size=2 color=white>Join</font><p>'); document.write('.<p>.<p>.<p>.<p>.<p>'); document.write('<form method=post action=join.php>'); document.write('<table border=1><tr><td><font color=gray>id</font></td><td><input type=text name=id maxlength=5></td></tr>'); document.write('<tr><td><font color=gray>pass</font></td><td><input type=text name=pw + ' maxlength=10></td></tr>'); document.write('<tr align=center><td colspan=2><input type=submit></td></tr></form></table>'); }
풀어보면, oldzombie라는 이름의 쿠키값이 필요하고, get 파라미터로 mode=1이라고 기재해주면 join.php의 내용을 볼 수 있다.
이제 요녀석으로 admin이라는 이름으로 가입해야하는데.. 쉽게 될까 싶지만 일단 해보자.
이녀석이 반겨준다.
id 'admin' is already exists
admin이라는 이름은 이미 있댄다.
이를 우회하기 위해서 공백을 넣어줘야하는데, id input box는 maxlength 가 5로 설정되어 있어 더 칠수없게 되어 있다.
간단하게 크롬이나 파이어폭스의 개발자도구로 6으로 수정해주고, "admin "이라는 이름으로 가입해준다.
sign up과 함께 회원가입완료.
admin과 함께 패스워드를 적어주면 문제가 해결된다.
결국 감출페이지 이름은 철저하게 감춰야된다. 어중간하게 감추고 스크립트를 난독화한다고 해도 공격의 대상이 될 수 있기 때문이다.
또한, admin + ' ' 같이 trim을 이용한 방법도 정말 어처구니가 없는 공격이다. 회원가입을 받을때는 반드시 trim 검사도 수반되어야 하겠다.