문제분석 & 풀이
sql injection 문구과 로그인 창이 있다.
써있는 값 그대로 로그인 해보니 guest 계정으로 로그인 됐다.
아마 admin의 계정으로 로그인하는 것이 목표인 문제라고 예상된다.
<?php
if($_GET['id'] && $_GET['pw']){
$db = dbconnect();
$_GET['id'] = addslashes($_GET['id']);
$_GET['pw'] = addslashes($_GET['pw']);
$_GET['id'] = mb_convert_encoding($_GET['id'],'utf-8','euc-kr');
if(preg_match("/admin|select|limit|pw|=|<|>/i",$_GET['id'])) exit();
if(preg_match("/admin|select|limit|pw|=|<|>/i",$_GET['pw'])) exit();
$result = mysqli_fetch_array(mysqli_query($db,"select id from chall45 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')"));
if($result){
echo "hi {$result['id']}";
if($result['id'] == "admin") solve(45);
}
else echo("Wrong");
}
?>
코드 분석
1) id, pw 파라미터 값을 addslashes() 처리
2) id 파라미터 인코딩 방식을 'utf-8'에서 'euc-kr' 로 변경
3) id, pw 파라미터에 admin, select, limit, pw, =, <, > 가 들어가면 exit
4) id, pw 파라미터를 토대로 SQL 쿼리를 실행
5) 쿼리 결과의 id가 "admin" 이면 문제 해결
핵심은 addslashes()다.
addslashes()는 SQL Injection을 방지하기 위해 사용하는 함수로 ' , " , / 등을 이스케이프 시켜서 인젝션을 방지하는 함수다.
○ 이스케이프 예제
ex) select * from [table] where id=$id 라는 쿼리가 있다고 가정하자.
select * from [table] where id='He's good guy'
$id에 위와 같이 입력된다면, 쿼리는 에러가 발생한다. 왜냐하면 id='He' 가 하나로 취급되고 나머지 부분은 ' 로 묶이지 않기 때문에 에러가 발생한다.
그렇다면, He's good guy를 id로 쓰고싶으면 어떻게 할까?
select * from [table] where id='He\'s good guy'
이렇게 ' 앞에 \(역슬래시)를 붙여주면 이스케이프 되면서 \뒤에 붙은 ' 는 $id의 일반문자로 취급받는다. 따라서 쿼리는 에러가 발생하지 않는다.
' 앞에 \(역슬래시)를 붙여주는 것을 이스케이프 처리한다고 한다.
결론적으로 id,pw 모두 이스케이프 되기 때문에 ' , " 문자를 이용한 인젝션은 힘들다.
그러나 mb_convert_encoding()를 통해 id 파라미터에 대한 인코딩 방식을 'utf-8' 에서 'euk-kr' 로 바꿔주고 있고 여기서 취약점이 발생한다.
'euk-kr'는 멀티바이트 환경의 언어셋으로 \(백슬래시) 앞에 %a1~%fe가 붙으면 2개의 문자를 하나의 문자로 취급해서 \를 없어지게 만든다.
정리하면, addslashes()를 통해 이스케이프 되면서 ' , " 문자 앞에 \(역슬래시)가 붙어서 인젝션을 방지하고 있는 상황이지만, mb_convert_encoding()에서 취약점이 발생하므로 \를 없애면서 인젝션을 수행할 수 있다.
예제를 들어보자.
ex) He's good guy 에서 addslashes()를 통해 → He\'s good guy
mb_convert_encoding()로 He\'s good guy의 언어셋이 'euk-kr'로 변경된다.
결국, %a1을 \앞에 붙여주면?
→ He%fe\'s good guy 는 He's good guy 가 된다.
예제에서 %a1 ~ %fe 중 %fe 를 사용한 이유는 위에 있는 'euk-kr'표를 보면 알 수 있듯이 %fe 의 경우 어떤값과 합쳐져도 null이기 때문이다.
취약점을 알았으니 SQL 쿼리를 파악해서 id 파라미터로 인젝션을 시도해보자.
1) select id from chall45 where id='1' or id like admin #' and pw=md5('{$_GET['pw']}')
2) select id from chall45 where id='1%fe%27 or id like 0x61646d696e %23' and pw=md5('{$_GET['pw']}')
2개의 쿼리는 같은 의미지만 이해를 돕고자 1번 쿼리도 작성했다.
2번 쿼리를 보면 mb_convert_encoding 취약점을 이용하기 위해 %fe를 ' 앞에 붙였으며 "admin"은 정규표현식 필터링에 의해 사용할 수 없기 때문에 like 와 연계하여 16진수의 값으로 우회하고 있다.
인젝션 쿼리를 모두 완성시켰으니 값을 직접 전송해보자.