본문 바로가기

챌린지/los.rubiya.kr

Lord of SQLInjection - goblin

세 번째 문제인 고블린이다.

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~"); 
  if(preg_match('/\'|\"|\`/i', $_GET[no])) exit("No Quotes ~_~"); 
  $query = "select id from prob_goblin where id='guest' and no={$_GET[no]}"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("goblin");
  highlight_file(__FILE__); 
?>

이번 문제에서는 ', ", ` 문자를 필터링하고 있다. 이런 quotes를 활용할 수 없는 환경에서는 이전과는 좀 다른 방식으로 injection할 필요가 있다. 그리고 문제를 풀기 위한 조건을 얼핏 보면 지금 쿼리로는 해결할 수 없는 것처럼 보인다. 왜냐면 쿼리 결과의 id 컬럼에 admin이 있어야 하는데 현재 쿼리에서는 id가 guest로 고정되어 있으며 이는 우리가 변경할 수 없기 때문이다. 그렇다면 어떻게 우회할 수 있을까? 여기서 사용하는 방법은 union sql injection이라는 방법이다.

 

SQL injection UNION attacks | Web Security Academy

When an application is vulnerable to SQL injection and the results of the query are returned within the application's responses, the UNION keyword can be ...

portswigger.net

자세한 설명은 위의 portswigger 문서를 참조할 수 있다. 간단히 말하자면 이 union 키워드는 두 개의 쿼리의 실행 결과를 하나로 합칠 수 있다. "쿼리1 union 쿼리2" 처럼 두 쿼리를 이어주는 모습으로 사용되는데 이때 두 쿼리의 수행 결과는 동일한 갯수의 컬럼을 가지고 있어야 한다. 그래야 두 번째 쿼리의 실행 결과를 첫번째 쿼리의 실행 결과에 알맞게 합할 수 있다. 물론 컬럼의 자료형도 호환되어야 한다.

그래서 이를 사용하려면 지금 문제에서 제공되는 "select id from prob_goblin where id='guest' and no={$_GET[no]}" 말고 새로운 쿼리를 구축해야 하는데 현재 쿼리 결과는 id 컬럼밖에 없기 때문에 select로 'admin'이란 값을 출력시키는 쿼리를 union해주면 결과적으로 쿼리 결과의 id 컬럼에 'admin'이 삽입되어 $result['id']는 'admin'이 된다. 이때 $result['id']에는 오직 'admin'만 존재해야 하는데 그렇기 위해서는 첫번째 쿼리('guest')를 거짓으로 만들어서 아무런 데이터도 받아오지 않도록 해야 한다. 간단하게 테스트해본 결과 'guest' 계정은 no 필드가 1일 경우 참이 된다. 그러므로 no에는 1 대신 아무 값이나 전달해주면 될 것이다.

그런데 "111 union select 'admin'" 처럼 쿼리를 작성하면 문제의 5번째 줄에 있는 필터링에 따옴표(')가 걸리게 된다. 그렇다면 어떻게 따옴표를 쓰지 않고 문자열을 생성할 수 있을까? 이때는 mysql의 char() 함수를 사용할 수 있다.

 

MySQL CHAR() function - w3resource

MySQL CHAR() returns the character of the given integer value and it skips the NULL value.

www.w3resource.com

이는 매개변수로 받은 ASCII 값으로 해당하는 문자를 조합하여 문자열을 만들어낸다. 자동적으로 따옴표로 감싸주기 때문에 필터링에 걸릴 일도 없으며 'admin'의 'a', 'd', 'm', 'i', 'n' 각 문자를 인터넷에서 아스키 값으로 변환하여 넘겨주면 된다. 16진수로 변환할 일이 많은데 매번 인터넷 사이트를 열어서 변환시키기도 번거롭기 때문에 따로 프로그램을 만들어서 사용해도 좋다.

Windows Forms로 간단하게 만든 변환기

그래서 쿼리에서 'admin' 대신에 char(97,100,109,105,110)을 넣어주면 무난하게 풀리는 것을 볼 수 있다.

 

 

이번 문제는 처음으로 "' or '1'='1'#" 같은 틀에 박힌 sql injection에서 벗어나서 union sql injection이라는 기법을 활용해볼 수 있는 계기가 되었다. 사실 이 문제부터 좀 막히긴 했지만 위의 Portswigger의 가이드를 뒤져보다 이런 기법을 발견해서 적용해봤더니 풀 수 있어서 다행이었다. Portswigger의 Web Security Academy에도 양질의 자료가 많이 있다고 하는데 이번 기회에 좀 공부해봐야겠다.

'챌린지 > los.rubiya.kr' 카테고리의 다른 글

Lord of SQLInjection - darkelf  (0) 2020.12.27
Lord of SQLInjection - wolfman  (0) 2020.12.22
Lord of SQLInjection - orc  (0) 2020.12.19
Lord of SQLInjection - cobolt  (0) 2020.12.16
Lord of SQLInjection - gremlin  (0) 2020.12.15