본문 바로가기
정보보안/wargame

[webhacking.kr] challenge(old). old-05

by 민-Zero 2020. 2. 13.

문제에 처음 접근하면 검은색 화면에 Login과 Join 버튼이 보인다.

Login 버튼을 통해 로그인 화면에 접근하게 되면 아이디와 비밀번호를 통해 로그인을 할 수 있는 화면이 제공된다.

임의로 아무 아이디와 비밀번호를 입력하면 Wrong password라는 문구와 함께 다시 로그인 창이 등장한다.

Join 버튼을 통해 회원가입을 진행하려고 하면 Access_Denied라는 알림창과 함께 아무런 변화도 일어나지 않는다.

따라서 우선 접근이 되는 Login 페이지에 대한 소스를 확인해 봤다. id와 password를 사용해 로그인하면 화면에 대한 정의만 있고 별다른 것은 발견할 수 없다.

어떻게 문제에 접근해야 할지 한참 고민하다가 url을 보니까 Login페이지는 login.php로 동작하고 있는 것을 확인할 수 있었다. 그럼 Join페이지는 join.php로 존재하지 않을까 생각했다.

따라서 url의 login.php부분을 join.php로 바꿔서 접근해 보았더니 Access_Denied 대신 bye라는 문구가 적혀있는 알림 창이 등장하고

아무것도 존재하지 않는 검은 페이지만 존재한다.

따라서 해당 페이지의 소스를 확인해보니 드디어 문제를 해결할 만한 실마리를 발견할 수 있었다. 소스코드는 알아보기 힘들게 난독화가 되어있고 중간에 직접 확인할 수 있었던 alert('bye')나 alert('Access_Denied') 뿐만 아니라 pass에 관한 부분도 존재함을 확인할 수 있었다. 따라서 우선 난독화를 해제해 어떤 코드인지를 확인해야 한다.

 

간단하게 해당 스크립트에만 적용되는 난독화 해제 코드를 작성했다.

난독화를 순서를 나누어 진행했다. 난독화 코드의 script부분을 복사해 txt파일로 Obfuscation이라는 이름으로 저장하고 자동으로 줄 바꿈을 진행한 뒤 Obfuscation_ver2.txt로 새로 저장하였다. 

우선 코드가 변수 선언이나 조건문에 대해 줄 바꿈이 안되어있어 가독성이 너무 떨어져 보기가 더 힘드므로 우선 줄 바꿈부터 진행하였다. 우선  모든 코드의 종료를 뜻하는 ';'을 구분자로 사용하여 줄 바꿈을 진행하고 조건문이나 반복문을 좀 더 보기 쉽게 하려고 '{'와 '}'에 대해서도 줄 바꿈을 진행하였다. '\n\n'부분은 기존의 \n으로 인해 한번 더 줄 바꿈이 일어나는 부분을 제거하기 위해 작성했다.

 

훨씬 보기 편하게 변경되었다. 다음으로 I, l 등으로 변수명이 알아보기 힘들게 되어있는 부분을 변수에 저장된 문자를 통해 어떤 문자인지 알아볼 수 있도록 변경하면 된다.

 

알아보기 힘든 변수명에 저장된 문자를 key, value형태로 사용하기 위해 딕셔너리 변수에 저장하기 위한 부분이다.

 

앞에서 저장한 딕셔너리에 난독화된 변수명을 key값으로 입력하여 스크립트에 문자열 대신 변수명으로 사용된 부분을 value값으로 변경해 알아보기 편하게 변경하여 _ver3로 저장하였다.

따라서 Obfuscation_ver3에서 위와 같은 원문을 얻을 수 있다. 몇 군대 잘못 해독되는 곳이 있어 약간의 코드 수정이 필요하다. 그냥 크롬의 개발자 도구를 사용해서 번역해도 되지만 코드가 엄청 길어질 경우를 가정해 한번 연습해 보았다.

 

코드를 해석해 보면

if(eval(document.cookie).indexOf(oldzombie)==-1) {
    alert('bye');
    throw "stop";
}

eval은 python이나 java에서도 존재하는 함수로 입력받은 문자열이 실행 가능한 코드라면 실행해버리는 함수이다.

indexOf는 입력된 문자열을 찾는 함수로 존재하지 않는다면 -1을 존재한다면 문자의 위치를 반환한다.

따라서 페이지에 oldzombie라는 쿠키값이 없으면 bye라는 알림창과 함께 stop이라는 에러를 발생시킨다. 

 

if(eval(d+o+c+u+m+e+n+t+.+'U'+'R'+'L')).indexOf(m+o+d+e+'='+1)==-1){
    alert('access_denied');
    throw "stop";
}

페이지 URL에 mode=1이라는 문자열이 없으면 access_denied라는 알림창과 함께 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='+j+o+i+n+.+p+h+p+'>');
    document.write('<table border=1><tr><td><font color=gray>id</font></td><td><input type=text  name='+i+d+' maxlength=20></td></tr>');
    document.write('<tr><td><font color=gray>pass</font></td><td><input type=text name='+p+w+'></td></tr>');
    document.write('<tr align=center><td colspan=2><input type=submit></td></tr></form></table>');
}

위 두 조건문이 실행되지 않는다면 Join이라는 id와 pw를 입력받는 창이 생성된다. 즉, oldzombie라는 쿠키값을 생성하고 URL에 mem=1을 추가하여 실행하면 가입할 수 있는 창이 생성되고 아이디를 생성해 login창에서 로그인을 할 수 있게 된다.

 

따라서 위와 같이 oldzombie라는 쿠키값과 join.php?mode=1 로 php에 mode=1을 입력하여 전송했더니 join페이지가 등장했다.

 

min이라는 id와 pass에 1234를 넣어 제출했더니 성공적으로 가입이 되었다는 문구가 출력되었다.

가입한 아이디로 로그인을 했더니 위와 같은 문구와 함께 admin으로 로그인하라는 문구가 생성되었다.

그래서 admin으로 가입하려고 했더니 이미 존재하는 id라는 문구와 함께 등록되지 않는다. 그럼 admin의 비밀번호를 찾아내야 하는 줄 알고 한참을 고민했다. 그러다 문득 로그인한 아이디를 보여주는 문구를 보았고 id에 특수문자나 공백을 입력할 경우 어떤 결과를 출력하는지 확인해 보았다.

db에서 주석으로 사용하는 #을 이용하면 되지 않을까 했지만 특수문자는 인식하여 가입한 id를 그대로 출력했다.

하지만 min앞에 공백을 추가하여 id를 생성한 경우 그냥 min으로 가입한 것과 똑같이 min으로 인식하는 것을 확인할 수 있었다.

따라서 이미 존재하는 admin이 아닌 앞에 공백을 추가한 admin으로 로그인한 결과 그냥 admin으로 인식하여 문제를 해결할 수 있었다.

댓글