카테고리 없음

2차적 SQL Injection

진모씨 2014. 1. 13. 12:54

웹 개발을 하다보면 흔히 magic_quote나 mysql_real_escape_string을 로그인 부분, 가입 부분에 썼다고 SQL Injection 공격으로부터 그 부분이 안전하다는 착각을 가질 때가 많은 것 같다. 하지만 예외는 있다.


가령 아래의 코드를 보자.


<?php

 $q = mysql_query("SELECT id, pw FROM users WHERE id='".escape($id)."' AND pw='".escape($pw)."'");

 $l = mysql_fetch_array($q);

 if($l)

  echo 'Login!';

?>


이 코드는 users 테이블에서 $id, $pw 의 id와 pw 를 가진 데이터를 뽑아온다.

로그인 소스라고 가정하면, 회원 테이블에서 아이디와 패스워드를 이용해서 회원을 조회하는 코드이다.

조회한 후 사용자의 id, pw를 $l 에다가 맨 처음 회원을 불러오게 된다. 물론 결과가 없다면 저 변수는 null이 된다.


아직까지는 큰 취약점이 없어보인다.

하지만 아래의 코드가 추가된다면?


...

$_SESSION[id] = $l[id];

$_SESSION[pw] = $l[pw];


$q = mysql_query("SELECT * FROM log WHERE id='$_SESSION[id]'");

while($r = mysql_fetch_array($q)) {

 var_dump($r):

}

...


이 코드는 데이터베이스에서 불러온 아이디를 이용해 logs 테이블에서 id가 $_SESSION['id']와 같은 항목을 모두 불러온다.

이 코드의 허점이 뭘까?


간단히 생각해보자.

mysql_real_escape_string은 쿼리에서 다른 mysql 문법을 사용하기 힘들게, '를 \'로 바꾸고 \x00(널 문자)을 \0 으로 바꾼다.

이렇게 한다면 mysql에서 '와 ' 사이에 이 문자열을 넣었을 때 문법이 아닌 하나의 문자열로 인식하게 된다.


그런데 데이터에는 이 문법이 다시 원래 데이터로 복구되어 들어간다.

그러니까 내가 id에 jinmo123' 을 쳤다고 하자.

로그인 쿼리는 이렇게 될것이다. (pw는 123)

SELECT * FROM users WHERE id='jinmo123\'' and pw='123'


그렇다면 id가 jinmo123' 인것을 제대로 조회한다.

그 다음에 로그 조회 부분은?

$_SESSION[id] = $l[id];


와, $l[id]가 그러고보니 jinmo123\'가 아닌 jinmo123'가 들어있다.

이제 log에서 데이터를 조회할 때 escape가 풀린 문자열을 쓸 수가 있다!

여기서 SQL 인젝션을 하면 된다는 것이다.

log 조회 SQL은 아래와 같을 것이다.

SELECT * FROM log WHERE id='jinmo123''

그렇다면 '의 수가 서로 맞지 않아서 문법 오류가 나타난다.


이런 SQL 인젝션들을 보통 2차적 SQL Injection 이라고 부른다.

그러니까 맨 처음엔 안 일어나다가, 데이터의 결과값을 다시 데이터로 쓸 때 인젝션이 일어나는 것이다.


이러한 사례 말고도, SQL에서 여러 테이블을 조회할 때 쿼리 두개를 쓴다던지 하는 상황이라면 발생하기가 쉽다.

그러니 받은 데이터를 쿼리에 쓸 때도 escape가 되어있어야 된다.