青岑CTF Web 入门 EZMD5系列 详解(write up)
EZMD5

代码审计
include "flag.php";
highlight_file(__FILE__);
包含了flag.php文件并展示源代码
if (isset($_GET['QC'])) {
$qc = (string)$_GET['QC'];
$hp = md5($qc);
要求get传参QC并且设置了一个变量保存这个QC的字符串格式
其后有设置一个变量$hp为其的md5值
if ($hp == $admin_hash) {
echo "<br>Welcome, admin!<br>";
echo $flag;
} else {
echo "<br>Login failed.";
}
最后对比$admin_hash$hp这两个函数且是松散比较(==)
若相等则返回flag的值
这里观察到最开始的变量像是一个科学计数法的表示。而且当使用==比较两个字符串时,如果它们看起来像科学计数法表示的数字,PHP 会尝试将它们转换为数字进行比较。
- '0e830400451993494058024219903391'会被当作科学计数法:0 × 10^830400451993494058024219903391,其计算结果为 0。
- 同理,如果 md5($qc)的结果也是一个以 0e开头、后面全是数字的字符串,它也会被转换为数字 0。
所以只需要找到一个MD5值也为0e开头且后面是数字的字符串
有以下几个:QNKCDZO 240610708 aabg7XSs s878926199a
最终payload:?QC=QNKCDZO

拓展
若这里是强比较(===)的话,那就不可以了
强比较规则:
- 值是否相等
- 类型是否相同
- 对于字符串,会逐字符精确比较
此时就只能进行md5碰撞攻击,也就是知道了md5值求原来的那个字符串,基本上就只能写脚本了(可以让ai帮你写)
php中的逻辑运算符及其优先级,短路求值
逻辑与 (AND)
- 运算符:
&&或and - 说明:当两个操作数都为 true 时,结果才为 true。
逻辑或 (OR)
- 运算符:
||或or - 说明:当至少一个操作数为 true 时,结果就为 true。
逻辑非 (NOT)
- 运算符:
! - 说明:对布尔值取反。
逻辑异或 (XOR)
- 运算符:
xor - 说明:当两个操作数一真一假时,结果才为 true(两者相同则 false)。
优先级与短路求值
- 优先级(从高到低):
!>&&>||>and>xor>or - 短路求值:
- 对于
&&:如果第一个操作数为false,则不会计算第二个操作数(因为结果已确定)。 - 对于
||:如果第一个操作数为true,则不会计算第二个操作数。
- 对于
php中的位运算符
位与 &(Bitwise AND)
- 作用:对两个数字的每一个二进制位进行 AND 操作。
- 规则:两位都为1时,结果位才为1。
位或 |(Bitwise OR)
- 作用:对两个数字的每一个二进制位进行 OR 操作。
- 规则:两位中至少一个为1时,结果位就为1。
特殊场景:在布尔值上使用 &和 |
当操作数是布尔值(或可转换为布尔的值)时,&和 |会先将它们转换为整数(true-> 1, false-> 0),进行位运算,再将结果转换回布尔值。这有时会导致与 &&、||不同的结果。
EZMD5_1

代码审计
if (isset($_GET['a']) && isset($_GET['b'])) {
$a = $_GET['a'];
$b = $_GET['b'];
判断是否get传入了a和b并定义变量保存
if ($a != $b && md5($a) == md5($b)) {
echo "<br>Welcome, admin!<br>";
echo $flag;
这里要求$a不能等于$b,但是它俩的ma5值弱相等(==)
这里很容易想到数组绕过,任何数组的md5值都为Null
这里具体的比较可以去看我的“青岑EZPHP”的博客,总结了几乎所有的情况(这个在第一题的讲解里面)
payload
?a[]=1&b[]=2
这里的数组定义是url上的定义方法,这方面内容也在我那个博客里提及(这个在第二题)
EZMD5_2

代码审计
其他的代码沿用前几个题的,就不多解释了
if (substr($md5_a, 0, 2) === '0e' || substr($md5_b, 0, 2) === '0e') {
echo "<br>0e not allowed!";
这是比上一个题多的要求,意思是不能俩变量的值都不能以“0e”开头
但是对数组绕过没有影响
payload
?a[]=1&b[]=2

拓展
URL上数组的定义方法
1. 基本语法:方括号 []
这是最常用的方法,在参数名后添加 []来告诉 PHP 这是一个数组。
// URL: page.php?colors[]=red&colors[]=green&colors[]=blue // PHP 中获取: $colors = $_GET['colors']; // 数组: ['red', 'green', 'blue'] // 或者明确指定索引(数字): // URL: page.php?colors[0]=red&colors[1]=green&colors[2]=blue2. 关联数组(指定键名)
可以在方括号内指定字符串键名来创建关联数组。
// URL: page.php?user[name]=John&user[age]=25&user[city]=Beijing // PHP 中获取: $user = $_GET['user']; // 数组: ['name' => 'John', 'age' => '25', 'city' => 'Beijing'] // 多维数组: // URL: page.php?product[0][name]=Phone&product[0][price]=999&product[1][name]=Case $product = $_GET['product']; // 二维数组: [0 => ['name'=>'Phone','price'=>'999'], 1 => ['name'=>'Case']]3.URL 编码注意事项
当键名或值包含特殊字符时,需要 URL 编码
// 原始:page.php?filters[sort by]=date&filters[price range]=100-200 // 实际 URL 需要编码: // page.php?filters[sort%20by]=date&filters[price%20range]=100-200 // PHP 会自动解码: $filters = $_GET['filters']; // ['sort by' => 'date', 'price range' => '100-200']以下是必须编码的字符

EZMD5_3

依旧类似前面的,不过==变成了===
还是不影响数组绕过(第一种方法)
数组绕过yyDs
第二种方法:需要找两个不同的字符串但MD5值却相等的

EZMD5_4

代码审计
if (isset($_GET['QC'])) {
$qc = (string)$_GET['QC'];
if(substr(md5($qc),-6,6) ==='d54e23'
核心要求是截取$qc的值从-6位置,截取6位,必须是d54e23
只能暴力破解了,让ai写代码,最后求出payload
EZMD5_5

这道题与前面的异曲同工,只是由MD5变成了sha1
继续数组绕过即可
payload ?a[]=&b[]=2

EZMD5_6

依旧与前面的题一样,依旧数组绕过
payload ?a[]=&b[]=2
