Write-Up
처음 페이지를 보면, Login 과 join 버튼이 있다. 하지만 join 버튼 클릭 시, 엑세스가 거부된다. Login 버튼 클릭 시,
challenge/web-05/mem/login.php
위 URL로 이동되며, 로그인 페이지를 볼 수 있다.
위 그림 1과 같이 다양한 SQL Injection 시도를 했는데, 먹지 않았다. 아래 같은 Time based 도 안된다.
'or 1=1 AND if(1=1,sleep(10),false)#
'or 1=1 AND if (1=1) waitfor delay '0:0:10'#
'or 1=1 AND if 1=1 then BEGIN dbms_lock.sleep(10); END; end if--+
현재 challenge/web-05/mem/login.php 의 URL인데, /mem/ 디렉터리로 디렉터리 인덱싱 취약점이 있는지 확인해본다.
위 그림 2와 같이 디렉터리 인덱싱 취약점이 있어, 디렉터리 내 파일구조를 확인할 수 있다. join.php에 접근해 보았다.
"bye" 라는 alert() 가 발생한다. <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) {
alert('bye');
throw "stop";
}
if (eval(llll + lllllllllllllll + lll + lllllllllllllllllllll + lllllllllllll + lllll + llllllllllllll + llllllllllllllllllll + li + 'U' + 'R' + 'L').indexOf(lllllllllllll + lllllllllllllll + llll + lllll + '=' + I) == -1) {
alert('access_denied');
throw "stop";
} 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=20></td></tr>');
document.write('<tr><td><font color=gray>pass</font></td><td><input type=text name=' + llllllllllllllll + lllllllllllllllllllllll + '></td></tr>');
document.write('<tr align=center><td colspan=2><input type=submit></td></tr></form></table>');
}
"bye" 가 나온 부분을 보도록 한다. name이 oldzombie 이던, value가 oldzombie이던 상관 없이 쿠키를 추가해준다. oldzombie라는 문자열이 쿠키 값내에 존재하면 된다.
eval('document.cookie')
'oldzombie=oldzombie; PHPSESSID=1234455555324234234'
eval('document.cookie').indexOf('oldzombie')
0
<<< 아래는 oldzombie 쿠키 삭제 후 >>>
eval('document.cookie').indexOf('oldzombie')
-1
<<< 쿠키 생성하는데, name=1234, value=oldzombie >>>
eval('document.cookie')
'PHPSESSID=1234455555324234234; 1234=oldzombie'
eval('document.cookie').indexOf('oldzombie')
36
쿠키 값이 해결되니, 이제 "bye" 대신에 access denied 가 출력된다. document.URL 시, mode=1 부분이 있어야 해당 부분을 통과할 수 있으므로, /?mode=1 과 같이 요청해준다.
그럼 아래 그림 3과 같은 join page가 나오고, join을 진행할 수 있다. 필자는 처음에 'happy'로 가입을 진행했는데, 로그인 시, 아래와 같은 메시지가 발생했다.
Hello happy
You have to login as admin .
그래서 admin으로 가입을 진행하려했으나, 이미 존재하는 id라고 하였다. 그래서 앞에 공백을 추가하여, (공백)admin / 1234 와 같이 가입을 진행했다.
가입에 성공할 수 있었고, login 페이지에서 login을 그대로 진행 시 문제를 해결할 수 있다.
아래와 같이 공백 및 %00을 사용해 문제를 해결할 수도 있다. 문제 해결을 위해서 admin이라는 keyword만 들어가있으면 되는 것 같다. mysql 상에서 '(공백)admin' 과 'admin' 비교 시, false 값이 나오기 때문이다. ( 'admin' = 'admin(공백)' 은 true. 아래 기술 블로그 참조 )
[버프슈트 사용]
id=admin%20%00&pw=1
id=%20%20%20admin&pw=1
참고
https://techblog.woowahan.com/2559/
https://ko.javascript.info/cookie
https://ko.w3hmong.com/mysql/func_mysql_trim.htm