如何编写BOF

什么是BOF

BOF的全称是Beacon Object File。它一个使用C语言编写,运行在Beacon进程空间内,并且调用Beacon API的一段函数代码。其本质上是一个对象文件。

总所周知,从源代码到可执行程序要经历4个阶段。

预编译 -> 编译 -> 汇编 -> 链接

在源代码按照此流程经历过汇编之后,会生成一个共用对象文件格式(COFF)的文件,也就是目标文件或对象文件。由于缺少链接过程,该文件无法直接使用,需要修补程序中调用的Win32 API函数的内存地址等信息,这部分工作由beacon完成。

为什么要用BOF

Cobalt Strike中集成了多种执行自定义功能的方法,如:反射DLL注入,PowerShell和.NET等。通常情况下以上方法会触发向其他进程内存写入数据的行为,这会触发EDR告警。而BOF的执行是插入在beacon自身进程中,从而规避监测。

BOF编写

引用beacon.h

文件地址:beacon.h

在beacon.h中定义了beacon的接口,如:传入参数、显示结果等。而且在使用不同版本的Cobalt Strike时,需要注意使用的Beacon API是否与之适配。

调用Win32 API函数

在调用Win32 API前,需要对函数重新定义,方法如下

1
WINBASEAPI <return_type> WINAPI <dll_name>$<function_name> (<parameters>)

例如我需要在BOF中调用 HeapFree,则需要进行如下定义

1
WINBASEAPI void * WINAPI KERNEL32$HeapFree (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);

调用libc函数

在使用libc函数时也需要定义,方法与调用Win32 API函数大致相同。这里直接通过举例说明

1
WINBASEAPI void * __cdecl MSVCRT$malloc(size_t _Size);

编写代码

通常情况下入口函数为 void go(char * args, int alen)。当然也可以将入口函数修改为其他名字,但是在调用的时候需要指定入口函数名。

编译BOF

编译器使用mingw-w64

macOS夸平台编译
brew install mingw-w64

1
2
i686-w64-mingw32-gcc -c bof.c -o bof.x86.o
x86_64-w64-mingw32-gcc -c bof.c -o bof.x64.o

cna脚本调用

1
2
3
4
5
$handle = openf(script_resource("bof. $+ $barch $+ .o"));
$data = readb($handle, -1);
closef($handle);

beacon_inline_execute($bid, $data, "go", $args);
  • $bid: beacon ID
  • $data: BOF文件
  • "go": 入口函数名
  • $args: BOF参数

最后

在编写BOF时,特别需要BOF的崩溃。由于BOF是插入到beacon中执行,所以BOF的崩溃会直接导致beacon进程中止,且BOF不适用长期运行的功能,如网络扫描、代理等。