题目代码
<?php
$output = "";
if (isset($_GET['code'])) {
$content = file_get_contents(__FILE__);
$content = preg_replace('/FLAG\-[0-9a-zA-Z_?!.,]+/i', 'FLAG-XXXXXXXXXXXXXXXXXXXXXXX', $content);
echo '<div class="code-highlight">';
highlight_string($content);
echo '</div>';
}
if (isset($_GET['pass'])) {
if(!preg_match('/^[^\W_]+$/', $_GET['pass'])) { //限制输入只能是字母或数字
$output = "Don't hack me please :(";
} else {
$pass = md5("admin1674227342");
//输入不能与MD5哈希相同但是必须相等(在发生类型混合后) ==只比较数值,!==既比较数值又比较类型
if ((((((((($_GET['pass'] == $pass)))) && (((($pass !== $_GET['pass']))))) || ((((($pass == $_GET['pass'])))) && ((($_GET['pass'] !== $pass)))))))) {
if (strlen($pass) == strlen($_GET['pass'])) { //输入值必须等于MD5哈希的大小-32个字符(128位)
$output = "<div class='alert alert-success'>FLAG-XXXXXXXXXXXXXXXXXXXXXXX</div>";
} else {
$output = "<div class='alert alert-danger'>Wrong password</div>";
}
} else {
$output = "<div class='alert alert-danger'>Wrong password</div>";
}
}
}
?>
本题正常运行是要在一定的环境中,在这里我找到了这个题目的出处:ringzer0team挑战web254-PHP Fairy
在本地复现失败的师傅可以找到在题目出处练习一下这个题目。
首先,我们看到的题目页面页面应该是这样的
题目分析:
这个题目的要求就是输入一串字符串作为密码,然后内部代码会检查这个密码是否合乎限制。
- 要求输入的字符串仅包含字母和数字字符
- 在输入和生成的MD5哈希之间进行几次比较。也就是说,输入不能与pass的MD5哈希相同,但是必须相等。
- 输入值必须等于哈希的大小,md5(“admin1674227342”)等于
0e463854177790028825434984462555(32个字符,128位)
本题考查的知识点:
-
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。
该字符串的开始部分决定了它的值。如果该字符串以合法的数值开始,则使用该数值。否则其值为 0(零)。合法数值由可选的正负号,后面跟着一个或多个数字(可能有小数点),再跟着可选的指数部分。指数部分由 ‘e’ 或 ‘E’ 后面跟着一个或多个数字构成。
-
==
只比较数值,!==
和===
既比较数值又比较类型
详细解释为:
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行(也就是第一条所说的)。此规则也适用于 switch 语句。当用 === 或 !== 进行比较时则不进行类型转换,因为此时类型和数值都要比对。
解决问题:
构造一个符合上文题目分析出来的字符串
0e509367213418206700842008763514
0e509367213418206700842008763514与0e463854177790028825434984462555进行==比较时,都是0,0等于0。显然,0e509367213418206700842008763514与0e463854177790028825434984462555并不相同。0e509367213418206700842008763514满足32个字符。
也就是说,只需构造一个以0e开头的只含有数字和字母的总数为32个字符的字符串即可。