18번째 문제인 nightmare다.
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)|#|-/i', $_GET[pw])) exit("No Hack ~_~");
if(strlen($_GET[pw])>6) exit("No Hack ~_~");
$query = "select id from prob_nightmare where pw=('{$_GET[pw]}') and id!='admin'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id']) solve("nightmare");
highlight_file(__FILE__);
?>
이번 문제에서는 마침표('.'), 괄호 이어 쓰기('()'), 주석 문자('#', '-')가 필터링되고 있다. 그런데 더 색다른 제한은 pw 파라미터로 전달될 수 있는 문자열을 6글자로 제한하고 있다는 것이다. 게다가 쿼리에서 pw 파라미터는 where 절의 중간에 삽입되고 있기 때문에 주석 문자가 없다면 원하는 코드를 삽입하기가 더욱 어렵다. 이렇게 6글자로 제한해둔 것은 무슨 의도일까? 이번 문제는 blind나 union, error based 같은 injection이 아니라 약간의 트릭을 요하는 문제였다.
먼저 mysql에서 참, 거짓은 true, false 뿐 아니라 1과 0으로 표현될 수 있다. 그 예가 위의 쿼리로 WHERE 절에 1을 전달했을 경우 값이 참이 돼서 "CORRECT"가 출력되었으며 0을 전달했을 경우 출력되지 않았다. 이것을 지금 쿼리에서 어떻게 응용할 수 있을까? 이는 대입 연산자가 중복으로 사용되었을 때 어떻게 처리되는지 알아볼 필요가 있다.
위의 쿼리에서 demo 테이블에는 id, name, hint 컬럼이 있으며 id 값은 1에서 6까지밖에 존재하지 않는다. 그런데 id가 9999인 조건이 0과 동일한지 1과 동일한지 비교하는 것에 따라 쿼리 결과가 달라진다. 현재 테이블에는 id가 9999인 데이터가 없는데 왜 쿼리에 해당되는 결과가 있는 것일까? 이는 WHERE 절에서 사용된 조건이 'id가 9999인가?'가 아닌 'id가 9999인가 라는 질문에 대한 답은 참/거짓인가?'를 의미하기 때문이다.
이를 프로그래밍 언어처럼 알아보기 쉽게 표현하면 위와 같을 것이다. SQL에서 동일 비교 연산자는 '=='가 아닌 '='기 때문에 헷갈릴 수 있지만 위처럼 동작하며 왼쪽의 예시에서 보듯이 결과적으로 id가 9999인 것은 없기 때문에 false에 해당된다. 그래서 WHERE 절이 참이 되어 모든 id에 대해 "CORRECT"가 출력되는 것을 볼 수 있다.
그렇다면 이런 특징을 위의 쿼리에 적용해보면 pw 파라미터로 ')=0을 넘겨줘서 pw가 ('')인지 판단한 결과가 0(false)인지 확인하도록 WHERE 절을 조작할 수 있다. 비밀번호 컬럼이 비어있지는 않을 것이기 때문에 이는 거짓이 되어 0과 동일하게 된다. 하지만 이후 쿼리가 문제가 되기 때문에 이를 지워줘야 하는데 '#', '-'가 필터링된 지금 어떻게 주석 문자를 사용할 수 있을까? 이는 잘 정리된 포스트에서 찾은 ';%00' 이라는 키워드를 사용할 수 있었다.
정확히는 주석이 아니라 세미콜론과 널 문자를 사용하면 뒤의 쿼리가 무시되는 듯한데 아마 null-termination string이나 세미콜론으로 statement를 끝내는 특징과 관련이 있지 않을까 싶다. 그래서 결과적으로 pw 파라미터에 ')=0;%00처럼 6글자(%00은 디코딩됨)를 전달해주면 풀 수 있다.
이번 문제는 풀다가 도저히 모르겠어서 오픈채팅방에 질문을 올리고 1분 만에 풀려서 꽤나 황당하였던 문제였다. 채팅방에서 해답을 알려준 건 아니고 질문을 올리고도 계속 이것저것 시도해보다가 갑자기 풀려서 이게 왜 풀리지 란 생각이 들었던 기억이 있다.
처음 보는 형태라서 익숙하지 않았지만 옛날에 프로그래밍 언어를 배울 때 대입 연산자가 여러 개 있다면 일정 순서대로 처리된다는 것을 기억해서 비슷한 쿼리를 몇 번 작성해 보면서 이해할 수 있었다.
'챌린지 > los.rubiya.kr' 카테고리의 다른 글
Lord of SQLInjection - dragon (0) | 2021.01.31 |
---|---|
Lord of SQLInjection - xavis (0) | 2021.01.15 |
Lord of SQLInjection - zombie_assassin (0) | 2021.01.11 |
Lord of SQLInjection - succubus (0) | 2021.01.06 |
Lord of SQLInjection - assassin (0) | 2021.01.05 |