文章总结: 本文介绍了一种利用CrystalPalace框架将规避手法直接合并到BOF的技术方案,解决了传统IATHook在动态加载场景下失效及破坏SmartInject的问题。核心方法是通过链接脚本将规避PIC与BOF合并,使BOF自带Hook能力,无需依赖父进程传播。文中提供了详细的代码示例与集成步骤,并展示了如何在现有工具中通过重注册别名实现应用,为红队后渗透阶段的隐蔽性提供了新的解决思路。 综合评分: 88 文章分类: 红队,免杀,内网渗透,安全工具,实战经验
BOF Cocktail:将规避手法直接合并到 BOF 中
Rasta Mouse Rasta Mouse
securitainment
2026年3月12日 10:24 中国香港
| 原文链接 | 作者 | | — | — | | https://rastamouse.me/bof-cocktails/ | Rasta Mouse |
Crystal Palace 是一个 PIC 框架,可用于编写诸如前置 DLL 加载器之类的工具。该项目的核心理念是:在链接阶段将规避手法 (同样以 PIC 形式编写) 应用到能力上。以 DLL 为例,它通过 hook IAT 并将指定 API 的执行流重定向至相应的 hook 函数来实现,规避手法就在这些 hook 函数中落地。这意味着加载器需要同时加载 DLL 和一个独立的 PIC blob,二者在能力运行期间共存于内存中。
对于 DLL (如 Beacon) IAT 中的 API 而言,上述方案工作良好,但对于 BOF 这类后渗透能力就成了问题。BOF 是在运行时动态加载的,其依赖项很可能不在 Beacon 的 IAT 中。因此,Beacon 的 BOF 加载器必须在执行 BOF 之前完成函数解析 (GetModuleHandle/LoadLibrary 与 GetProcAddress)。
从规避的角度看,问题在于 GetProcAddress 返回的是指向原始 API 的指针,而非 hook 函数的指针——也就是说,默认情况下 BOF 的执行是”裸奔”状态 (没有任何规避手法)。这可不行。为了解决这个问题,可以同时对 Beacon 中的 GetProcAddress 进行 IAT hook,迫使它使用 __resolve_hookintrinsic,从而返回 hook 函数的指针。该方案可行,但存在以下缺点:
- 它会破坏 ‘smartinject’ 功能。该功能允许父 Beacon 通过
spawn和inject命令生成新进程或执行 fork & run 后渗透任务,并将 GetModuleHandle 和 GetProcAddress 的指针传递给相应能力的加载器,使其无需遍历 EAT 即可完成 API 解析。然而,如果 IAT 中的 GetProcAddress 地址已被 hook,传递出去的指针实际上指向 hook 函数而非原始 API。该指针在另一个进程的上下文中完全无效,会导致加载器崩溃。如果你不需要 smartinject,这不是问题,但问题本身确实存在。 - 效率低下。在这种方案下,我们的规避 PIC 必须包含我们可能需要运行的每一个 BOF 的 hook 函数。这会使 PIC 膨胀,增大体积和检测面,而且一旦 Beacon 已经在运行,就无法再添加新的 hook。
另一种方案是抛开从 Beacon 传播 hook 的思路,转而将规避手法直接合并到 BOF 中。这同时解决了上述两个缺点——我们不再需要 hook GetProcAddress,并且每个规避 PIC blob 可以优化为仅包含各自能力所需的 hook 函数。
下面通过一个简单的 BOF 实例来看看实际效果:
#include<windows.h>
DECLSPEC_IMPORT int WINAPI USER32$MessageBoxA ( HWND, LPCSTR, LPCSTR, UINT );
voidgo ( )
{
USER32$MessageBoxA ( NULL, "Hello World", "BOF", MB_OK );
}
bof.c
如果要 hook MessageBoxA,则需要另外准备一个 COFF,其中包含 hook 函数以及我们希望实现的新行为:
#include<windows.h>
DECLSPEC_IMPORT int WINAPI USER32$MessageBoxA ( HWND, LPCSTR, LPCSTR, UINT );
int WINAPI _MessageBoxA ( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType )
{
return USER32$MessageBoxA ( hWnd, "Hooked", lpCaption, uType );
}
hooks.c
接下来,要在 Crystal Palace 中将手法 (hook) 与能力 (BOF) 合并,spec 文件可以这样写:
x86:
...
x64:
load "bin/bof.x64.o" # grab the bof
make coff +optimize # turn it into a coff-exporter object
load "bin/hooks.x64.o" # grab the hooks
merge # merge with the bof
attach "USER32$MessageBoxA" "_MessageBoxA" # add the hook
export # export the merged bof
bof.spec
要在 Cobalt Strike 中运行,可以将 Crystal Palace 的 Java API 封装到自定义命令/别名中。例如:
import crystalpalace.spec.* from: crystalpalace.jar;
import java.util.HashMap;
alias test
{
local ( '$bid $arch $spec_path $spec $capability $final' );
$bid = $1;
$arch = barch ( $bid );
$spec_path = getFileProper ( script_resource ( ), "bof.spec" );
$spec = [ LinkSpec Parse: $spec_path ];
$capability = [ Capability None: $arch ];
$final = [ $spec run: $capability, [ new HashMap ] ];
btask ( $bid, "Tasked Beacon to run test BOF" );
beacon_inline_execute ( $bid, $final, "go", $null );
};
bof.cna
这固然很好,但能否将其应用到日常使用的现有 BOF 上呢?遗憾的是,目前没有提供可以拦截对 beacon_inline_execute 任意调用的 hook,因此将这套工作流集成到现有项目中会有些麻烦。
🙋♂️
如果 Cobalt Strike 团队中有人看到这篇文章——请把这当作我的正式需求 😂
下面介绍一个变通方案,以 TrustedSec 的 CS-Situational-Awareness-BOF 中的 netstat命令为例。当加载 SA.cna时,它通过 beacon_command_register 注册自定义命令。
alias netstat {
beacon_inline_execute($1, readbof($1, "netstat", $null, "T1049"), "go", $null);
}
beacon_command_register(
"netstat",
"get local ipv4 udp/tcp listening and connected ports",
"Synopsis: List listening and connected ipv4 udp and tcp connections"
);
SA.cna
无需修改他们的 Aggressor 脚本,你可以使用 alias_clear 清除他们注册的别名,然后在自己的脚本中重新注册一个新的别名。
import crystalpalace.spec.* from: crystalpalace.jar;
import java.util.HashMap;
alias_clear ( "netstat" ); # clear the netstat alias from SA.cna
alias netstat # register a new alias
{
local ( '$bid $arch $spec_path $spec $capability $final' );
$bid = $1;
$arch = barch ( $bid );
$spec_path = getFileProper ( script_resource ( ), "netstat.spec" );
$spec = [ LinkSpec Parse: $spec_path ];
$capability = [ Capability None: $arch ];
$final = [ $spec run: $capability, [ new HashMap ] ];
btask ( $bid, "Tasked Beacon to run netstat", "T1049" );
beacon_inline_execute ( $bid, $final, "go", $null );
};
bof-cocktails.cna
netstat.spec读取 netstat BOF 并合并一个针对 OpenProcess 的 hook (该函数用于获取输出中每个进程的名称)。
x86:
...
x64:
load "../CS-Situational-Awareness-BOF/SA/netstat/netstat.x64.o"
make coff +optimize
load "bin/hooks.x64.o"
merge
attach "KERNEL32$OpenProcess" "_OpenProcess"
export
netstat.spec
额外收获
其他原语如 mergelib同样适用,因此可以将 LibTCG 或其他任何库合并到最终的 BOF 中 (dprintf在调试时非常好用)。你甚至可以在规避 PIC 中调用 Beacon BOF API——只需下载最新的 header file 即可上手。
#include<windows.h>
#include"beacon.h"
DECLSPEC_IMPORT int WINAPI USER32$MessageBoxA ( HWND, LPCSTR, LPCSTR, UINT );
int WINAPI _MessageBoxA ( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType )
{
BeaconPrintf ( CALLBACK_OUTPUT, "Hello from _MessageBoxA" );
return USER32$MessageBoxA ( hWnd, "Hooked", lpCaption, uType );
}
需要注意的是,截至本文撰写时,这需要对 Crystal Palace 做一处小改动,详见 Raffi 的帖子。不过我相信下一个版本将会正式支持此功能。
总结
本文介绍了一种将规避手法应用到 BOF 的实现模式,无需依赖从父植入体传播 hook。目前仍处于早期阶段,工具链还有待完善,效果可能因人而异。
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:securitainment Rasta Mouse Rasta Mouse《BOF Cocktail:将规避手法直接合并到 BOF 中》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论