Write-Up
<?php
if($_GET['lv']){
$db = dbconnect();
$_GET['lv'] = addslashes($_GET['lv']);
$_GET['lv'] = str_replace(" ","",$_GET['lv']);
$_GET['lv'] = str_replace("/","",$_GET['lv']);
$_GET['lv'] = str_replace("*","",$_GET['lv']);
$_GET['lv'] = str_replace("%","",$_GET['lv']);
if(preg_match("/select|0x|limit|cash/i",$_GET['lv'])) exit();
$result = mysqli_fetch_array(mysqli_query($db,"select id,cash from chall46 where lv=$_GET[lv]"));
if($result){
echo("{$result['id']} information<br><br>money : {$result['cash']}");
if($result['id'] == "admin") solve(46);
}
}
?>
위의 필터링을 볼때, 공백, % 가 필터링 되어 있어 어떻게 우회를 할지 고민이 좀 됬었다. 우선, SQL Injection 가능성을 파악해본다. ( 물론, 문제 페이지에 대놓고 SQL Injection이라 적혀있긴 하지만.. 말이다. )
lv 를 보면, 숫자로 이루어진 컬럼이란 것을 알 수 있고, lv = 2-1 과 같이 query를 보냈을 때, lv = 1로 계산되어 query가 동작하게 되어, 값이 정상적으로 출력된다.
이제 공백을 어떻게 우회해야 할지 고민해야 한다. %09,%20등 과 같은 형태는 %가 필터링되어 안된다.('%'가 필터링 되어 안되는 것이 아닌, 공백이 필터링 되어 안됨) 그래서 괄호를 사용하기로 했다. 먼저 아래와 같은 payload를 요청해보았다.
/?lv=(1)or(1=1)
lv = true 가 되어, 값이 출력된다. ( 현 상황에서 뒤에 주석은 필요가 없다. ) 그 다음 아래와 같은 요청을 보내봤다.
?lv=(2)or(id=0b0110000101100100011011010110100101101110)
?lv=(0)or(id=char(97,100,109,105,110))
이때는 값은 정상적으로 출력되었으나, id=2 에 해당하는 출력 값이 나왔다. 아마 admin에 해당 하는 값보다 lv = 2에 해당 하는 값이 DB 상 더 위에 위치해서 그렇지 않나 싶다. ( 아래 그림 1 참조 및 ?lv=(1)or(lv=2) 와 같이 lv에 다른 값들을 넣어보며 테스트 해 볼 수 있다. )
본론으로 돌아와서, 아래와 같이 lv =0 을 넣음으로써, or 앞의 값이 false가 되게 만들어 줬다.
?lv=(0)or(id=0b0110000101100100011011010110100101101110)
문제를 해결할 수 있었다.
위 문제를 풀기위한 주요한 부분은 admin 을 0b0110000101100100011011010110100101101110 와 같이 우회 한 것과, 괄호를 사용한 공백 우회이다.
실습
위 그림 2를 보면, 빨간 박스는 쿼리가 올바르게 작성되지 않아 에러 값을 반환하는 것을 볼 수 있고, 괄호를 사용한 초록 박스는 쿼리가 올바르게 수행된 것을 볼 수 있다.
참고로... 여지껏 괄호가 공백문자 우회 기법이란 것은 알고 있었으나, 사용 법을 잘못알고 있었다.
select * from members where idx=0()or()1=1;
위와 같이 사용되어야 하는 줄 알고 있었다. ( DB에서 테스트 시 당연히 에러가 발생한다. )
추가적으로 or 를 || 를 통해 공백 사용 없이 문제를 해결할 수도 있다.
또한, 아래와 같은 요청도 성공적으로 이루어지는데,
1%0d%26%26id=char(97,100,109,105,110)%09
%가 필터링 되어 url encoding을 아얘 사용하지 못하는 것이 아닌 것을 확인했다. %26 은 & 로 %09나 %0d는 (공백) 이 되기에 공백은 필터링 되어지고, %26%26은 &&로 and와 같은 역할을 하게 된다.