HackThisSite - Extended Basic Mission 13
Challenge
The script's filename is vrfy.php. Make the script reply 1. Use the relative path. You don't know any users or emails.
关卡给出校验脚本
vrfy.php,要求给出一段相对路径的 URL(可带 query),让脚本输出1;没有任何已知的用户或邮箱。
关卡页把脚本正文放在 <code> 块里:
1 |
|
Solution
name 先过 mysql_real_escape_string() 作为
WHERE 条件里的单引号字符串字面量。这个函数会转义
\ ' " \n \r \0 和
Ctrl-Z,所以引号无法闭合,name 侧没有 SQL 注入。同时
email 也被转义后才参与比较,但它不进入
SQL:它只和查询结果做相等判断。
也就是说注入方向被堵死,能动的只有查询结果长什么样和比较表达式怎么算。
SELECT \email` FROM `members` WHERE name =
'$user'没有LIMIT,但脚本用mysql_fetch_assoc()只取**第一行**。关键在一行都取不到时它的返回值:布尔false`。
此时 $result 为 false,脚本却直接读
$result['email']。布尔值上的下标访问在 PHP 5 里静默求值为
null(PHP 8 会补一条
Trying to access array offset on value of type bool
警告,结果仍是 null)。于是只要让 name
匹配不到任何成员,比较的右端就固定是 null。
if ($email == $result['email']) 用的是松散比较
==,null 与字符串比较时按空串处理,于是:
1 | var_dump("" == null); // bool(true) |
所以 $email
必须是空字符串,配合一个匹配不到的
name,$reply 就变成 true,脚本回
1。email 换成
0、x、0e0
都不行:isset($_GET['email']) 对空串仍为
true,email= 不会被前一个 isset
分支挡掉。
PHP 8 已经移除 mysql_* 扩展,于是把三个 DB
调用按关卡语义打桩:mysql_query()
返回空结果集(表中不存在所发送的
name),mysql_fetch_assoc() 对空集返回
false,与线上行为一致。
1 |
|
工作区里用已安装的 PHP 8.5 跑了几组 query string:
1 | $ php replica_vrfy.php 'name=&email=' 2>/dev/null |
空 email 且 name 匹配不到 →
1;非空 email → 0;只给
name 不给 email(isset 为假)→
0。与上面的推导一致。(2>/dev/null 只是滤掉
PHP 8 那条布尔下标警告;线上 PHP 5 不产生警告,返回同样是
null。)
Key points
name走mysql_real_escape_string()后拼进单引号字符串,引号闭合不了,SQL 注入不通;email根本不进 SQL,只参与相等判断。mysql_fetch_assoc()在零行时返回false而非空数组,脚本却直接读$result['email'],布尔下标求值为null。- 松比较
==下"" == null为真、任何非空字符串(含"0")为假,所以email必须留空;isset($_GET['email'])对空串为真,email=能通过前置检查。 name只要匹配不到成员即可(本关没有任何已知用户),无需猜邮箱。- playit 提交纪律:
formkey每次加载都变,必须取页面 → 立刻提交;缺少Referer: <关卡页>会被Invalid Referer拒掉且不计分。 - 修正方式:用
===做严格比较,并在使用结果前判断mysql_fetch_assoc()是否为false(更彻底地用 PDO/mysqli 预处理替换已废弃的mysql_*)。