목표 : SQL, 필터링 우회
클릭해보기 - 쿠키 값 보기 - 페이지 소스값 보기
auth 클릭 시
쿠키는 oldzombie 값 한 개
viewsource 클릭 시 확인 가능한 소스
<?php
include "../../config.php";
if($_GET['view_source']) view_source();
?><html>
<head>
<title>Challenge 7</title>
</head>
<body>
<?php
$go=$_GET['val'];
if(!$go) { echo("<meta http-equiv=refresh content=0;url=index.php?val=1>"); }
echo("<html><head><title>admin page</title></head><body bgcolor='black'><font size=2 color=gray><b><h3>Admin page</h3></b><p>");
if(preg_match("/2|-|\+|from|_|=|\\s|\*|\//i",$go)) exit("Access Denied!");
$db = dbconnect();
$rand=rand(1,5);
if($rand==1){
$result=mysqli_query($db,"select lv from chall7 where lv=($go)") or die("nice try!");
}
if($rand==2){
$result=mysqli_query($db,"select lv from chall7 where lv=(($go))") or die("nice try!");
}
if($rand==3){
$result=mysqli_query($db,"select lv from chall7 where lv=((($go)))") or die("nice try!");
}
if($rand==4){
$result=mysqli_query($db,"select lv from chall7 where lv=(((($go))))") or die("nice try!");
}
if($rand==5){
$result=mysqli_query($db,"select lv from chall7 where lv=((((($go)))))") or die("nice try!");
}
$data=mysqli_fetch_array($result);
if(!$data[0]) { echo("query error"); exit(); }
if($data[0]==1){
echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Access_Denied!')\"><p>");
}
elseif($data[0]==2){
echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Hello admin')\"><p>");
solve(7);
}
?>
<a href=./?view_source=1>view-source</a>
</body>
</html>
PHP
복사
$data[0] 에 값 2를 넣게 되면 성공
$go=$_GET['val'];
PHP
복사
$go에는 뒤에 오는 값을 GET, 즉 URL에서 이렇게 받아온다.
if(preg_match("/2|-|\+|from|_|=|\\s|\*|\//i",$go)) exit("Access Denied!");
PHP
복사
정규식 매칭
2 - + from _ = \s * \ 공백 이 들어오면 접속 금지. 필터링 시스템
$db = dbconnect();
$rand=rand(1,5);
if($rand==1){
$result=mysqli_query($db,"select lv from chall7 where lv=($go)") or die("nice try!");
}
if($rand==2){
$result=mysqli_query($db,"select lv from chall7 where lv=(($go))") or die("nice try!");
}
if($rand==3){
$result=mysqli_query($db,"select lv from chall7 where lv=((($go)))") or die("nice try!");
}
if($rand==4){
$result=mysqli_query($db,"select lv from chall7 where lv=(((($go))))") or die("nice try!");
}
if($rand==5){
$result=mysqli_query($db,"select lv from chall7 where lv=((((($go)))))") or die("nice try!");
}
$data=mysqli_fetch_array($result);
if(!$data[0]) { echo("query error"); exit(); }
if($data[0]==1){
echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Access_Denied!')\"><p>");
}
elseif($data[0]==2){
echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Hello admin')\"><p>");
solve(7);
}
PHP
복사
DB에 연결 후 랜덤 값 생성 rand 에 1~5 사이 값 저장
SQL에 ‘select lv from chall7 where lv=((($go)))’ 과 같은 쿼리를 날린다.
5개 정도면 하나만 나온다 생각하고 여러번 시도하는게 속이 편하긴하다.
rand 가 1일 때를 확인해보면
$result=mysqli_query($db,"select lv from chall7 where lv=($go)") or die("nice try!");
PHP
복사
직독직해하자면 chall7 DB에서 lv 값이 $go인 lv을 고른다.
결국 $go에 들어가는 값이 data[0]의 값이 되는 구조
url 뒤 val 값에 여러 방법으로 우회해 2라는 값만 넣으면 된다.
필터링 문자 목록
2 - + from _ = \s * \ 공백
MySQL은 정말 다양한 연산자를 지원한다.
/ 나 \/ 는 불가능하니 % 와 같이 나머지를 만들거나 Shift 연산자 >> 를 사용해보자
(블랙리스트 기반이 아닌 화이트리스트 기반으로 해야하는 이유)
5%3 = 2
4>>1 = 2
Access Deny 가 아닌 Query error
SQL이나 DB를 조금 다뤄보지 않았으면 이해가 안될 수 있는 답변이다.
쿼리에 lv=2 라는 값이 없다는 이야기다
DB lv 컬럼에 2 값을 넣어줘야 한다.
select lv from chall7 where lv=val
val에 들어가는 값을 통해 lv=2인 컬럼을 만들어야 하니 컬럼을 반환하여 그 결과를 합치는 union select 문을 활용한다.
나온 결과물
12345 union select 2
공백을 필터링하니
(12345)union(select(2))
2를 필터링하니
(12345)union(select(4>>1))
쿼리문 구조를 확인해보면
select lv from chall7 where lv=($go)
select lv from chall7 where lv=(12345)union(select(4>>1))
PHP
복사
로 앞에 (를 빼야하고, rand 1일 때만 성립되므로 새로고침을 반복한다.
왜 안되지? 싶어 다시 확인했다.
놀랍게도 12345라는 값이 있나보다..
999로 바꾸고 새로고침을 수행하니까 풀렸다.
→ 다시 확인하니 12345에 2가 있어서 필터링됐다.