剪贴板变身C2通道:GoClipC2在VDI/RDP环境中的隐蔽通信

本文详细介绍了GoClipC2工具,这是一种基于Windows剪贴板的隐蔽C2通信通道,专门针对VDI/RDP环境设计。通过加密Base64消息传递绕过网络监控,支持文件传输、命令执行等多种功能。

Expanding on ChunkyIngress - Clippy Goes Rogue (GoClipC2)

GoClipC2:一种隐蔽的基于Windows剪贴板的C2通道,适用于VDI/RDP环境。通过加密的Base64消息传递绕过网络监控。

背景起源

一年前,我在基于VDI的实验室环境中进行实验时编写了一个工具。我发现的一个问题是剪贴板已启用,但无法将二进制文件或脚本复制粘贴到环境中。然而,你可以将其从浏览器粘贴为文本,这让我思考:如何将所需内容转换为文本传入环境?Base64解决了这个问题。

我的初始版本是直接传输并希望复制正确,但考虑到人为错误和可能出现的乐趣,我决定根据剪贴板缓冲区的大小将其自动分块。

解析ChunkyIngress

我一年前编写ChunkyIngress时就开始写这篇博客文章。它通过将输入文件分块为选定大小的块,并将这些块复制到剪贴板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
if ($mode -eq 'encode') {
    $fileBytes = [IO.File]::ReadAllBytes($inputPath)
    $base64String = [Convert]::ToBase64String($fileBytes)
    $chunkSize = 68KB  # 根据需要修改块大小
    $chunkCount = [math]::Ceiling($base64String.Length / $chunkSize)
    
    Write-Output "准备接收 ${chunkCount} 个数据块"
    Write-Output "将每个块粘贴到环境后,按回车键复制下一个块"

    for ($i = 0; $i -lt $chunkCount; $i++) {
        $startIndex = $i * $chunkSize
        $chunk = $base64String.Substring($startIndex, [math]::Min($chunkSize, $base64String.Length - $startIndex))
        Set-Clipboard -Value $chunk
        Write-Output "块 $i 已复制到剪贴板。按Enter复制下一个块或按Ctrl+C退出。"
        Read-Host
    }

    Write-Output "创建的总块数:$chunkCount"
}

在示例和默认代码中,它会将文件分成68KB的块,计算所需的块数,然后将块复制到剪贴板,以便在另一侧使用类似以下代码解码回文件:

1
2
$BuildMahFile = [Convert]::FromBase64String("")
[IO.File]::WriteAllBytes('ChunkyIngress.7z', $BuildMahFile)

当你有PowerShell可用时,这对于渗透/渗出非常有用,但同样,如果我们尝试使用相同的剪贴板作为命令控制通道呢?已经有现有的工具在PowerShell中实现了这一点(https://github.com/inguardians/Invoke-Clipboard),但我喜欢创造东西,同样看到了学习新东西和用Golang玩耍的机会。

从API层面看剪贴板

在深入探讨如何实现之前,了解有关剪贴板的更多信息非常重要,特别是在Windows上,因为这是我的用例。

在基础层面,使用的主要API如下所列,每个API可以获取的不同信息也列出。

打开/关闭:

  • OpenClipboard(hwnd) - 打开剪贴板进行读/写
  • CloseClipboard() - 关闭剪贴板访问
  • EmptyClipboard() - 清除剪贴板内容

数据操作:

  • SetClipboardData(format, handle) - 将数据放置在剪贴板上
  • GetClipboardData(format) - 从剪贴板检索数据
  • IsClipboardFormatAvailable(format) - 检查格式是否存在
  • EnumClipboardFormats(format) - 枚举可用格式

常见格式:

  • CF_TEXT - ANSI文本
  • CF_UNICODETEXT - Unicode文本
  • CF_BITMAP - 位图图像
  • CF_DIB - 设备无关位图

快速操作系统历史课

虽然与博客文章不完全相关,但在深入探索时,不同信息会被无意中发现,所以我想与大家分享。以下是关于Windows及其多年来剪贴板差异的快速历史课。

如果你在blog.zsec.uk以外的地方阅读此内容,并且内容似乎是逐字复制,请注意原始文章由Andy Gill撰写并托管在那里。

Windows XP及更早版本

对于那些记得XP及更早版本奇迹的读者,你在环境中不太可能遇到它,但看到它的机会不为零。由于早期的操作系统缺乏现代技术,在可以做什么方面存在明显的限制。

  • 有限的格式支持:主要是基本格式,如CF_TEXT、CF_BITMAP、CF_DIB - 基本上你不能很好地复制unicode,它确实可以做到,但有时某些事情会失败。
  • 无剪贴板历史记录:与更现代的Windows版本不同,没有剪贴板历史记录,这可能是好事也可能是坏事,你复制一次东西,它就会清除剪贴板,完成。

Windows Vista/7

  • 增强的格式支持:引入更复杂的数据格式,以构建在XP之上,并能够更好地复制不同的文本和unicode格式。
  • UAC集成:剪贴板访问受用户帐户控制影响,引入了限制,以防止在没有UAC干预的情况下在特权级别之间复制粘贴,这有助于提高围绕权限提升控制的门槛。
  • 应用程序隔离:这很有趣,因为我们稍后将深入探讨的C2通道使用用Go编写的应用程序来促进剪贴板访问和通信,W7是MS首次为剪贴板引入应用程序隔离以及限制应用程序之间操作的能力。随着操作系统老化,我们在现代环境中更可能看到Windows 10及更高版本,因此需要考虑这一点。

Windows 8/8.1

Windows 8没有太大区别,它确实带来了称为metro应用程序的新应用程序,这引入了围绕剪贴板的额外更改,但幸运的是,所述metro应用程序随着Windows 10的引入而消亡,所以现在我们不太可能看到太多。但它确实带来了围绕在不同应用程序类型之间复制的更严格控制,以及对应用程序开发人员可以防止从其应用程序复制出去的数据的限制,例如引入控制以防止从GUI文本框使用ctrl+c等进行复制粘贴。

Windows 10

Windows 10可能是剪贴板东西的较大突破之一,它是第一个带来剪贴板历史的,被归类为"重大增强",它增加了保留最多25个最近剪贴板项目的能力。除了历史记录,它还增加了通过MS帐户跨设备同步剪贴板数据的能力,类似于Apple与其统一设备(不确定命名约定)所做的,例如,如果它们共享相同的帐户,你可以在平板电脑上复制并在笔记本电脑上粘贴。

还添加了更多元数据,这对防御者来说很棒,因为它还增加了将时间线集成到剪贴板活动发生并记录在时间线中的能力。蓝队可能意识到的是Windows时间线(Win + Tab),它显示了应用程序使用和文档活动的可视化历史,包括浏览器会话、Office文档和一些剪贴板辅助的工作流程。

Windows 11

基于Windows 10的重大改进,W11同样带来了自己的一些"增强",通过扩展剪贴板历史记录,添加更好的格式检测以及添加搜索剪贴板历史记录的能力,所有这些对用户来说都是很棒的功能,但同样对攻击者来说也是很棒的功能。它还围绕所述新功能的隐私以及复制和粘贴表情符号的能力添加了更细粒度的控制。

感谢你坚持听完历史课,我们学到的是,与Windows 10/11相关的API调用可以完全控制许多变量,但如果你面对的是XP/Vista/7/8,那么存在限制,理解并承认这一点很重要。

C2探索

基于ChunkyIngress的成功和使其工作的能力,我开始思考利用剪贴板作为C2通道的其他途径,Invoke-Clipboard存在并且基于PowerShell,很像ChunkyIngress,但我想要更广泛的东西,以及将磁盘上的二进制文件引入并使其以良好的方式使用"加密"数据 blob 的能力,具有各种功能,允许在系统之间轻松复制并添加附加功能。

进入GoClipC2,我组装的一个相当广泛的概念证明,它充当客户端和服务器设置,将服务器放在任何你想充当控制器的东西上,将客户端二进制文件放在你想要建立C2的VDI/RDP主机上。如果你没有复制粘贴二进制文件的能力,你可以使用ChunkyIngress引入客户端二进制文件,然后交给客户端服务器ClipC2。因为它使用剪贴板并且这些在技术上是本地操作,所以设置没有任何出站端口连接,从而提高了操作安全性,唯一的考虑是剪贴板上的内存工件和磁盘上的二进制文件形式的客户端。

构建工具

我喜欢编写工具并为如何扩展和构建东西制定计划,我一开始是一个简单的PoC,发送命令是可行的,但因为AD8K并且用Go构建PoC既有趣又相对简单,我开始添加特性和功能。

该项目最初是ChunkyIngress形式的基于剪贴板的基本命令执行概念证明,但我很快意识到PowerShell很棒,但Go更通用。该项目已经经历了各个版本的步骤:

v0.0.1:核心通信

我一开始打算使用500毫秒轮询循环监控剪贴板更改以充当C2。

很快意识到500毫秒变得非常嘈杂,所以将其翻转到几秒钟,还为通信添加了加密,因为防御者对在RDP会话和主机之间传递的大B64 blob感到难过,所以我为所有流量添加了AES-GCM包装器,以确保它不容易使用预共享密钥解密。

随着这些事情的发展,大量数据开始流动,我有越来越多的想法要添加,所以我构建了一个基于JSON的消息结构,具有唯一ID,以允许服务器区分不同的客户端和通信。

最后,因为剪贴板始终处于活动状态,但不同的东西可以轮询它,我添加了心跳功能。

v0.0.2:实际C2操作

让核心通信工作,可能花了我最长的时间,在我一个全职Go开发的朋友的帮助下(当然完全不是对抗性的,所以他确实有问题为什么我想做特定的动作!),因为我有一个工作的PoC,但事情不断破裂。Stackoverflow没有所有答案。

所以发布的版本目前支持以下C2操作:

  • VDI/RDP环境检测 - 使用进程的多向量检测,它不是世界上最聪明的,但作为PoC工作。
  • 文件上传/下载 - 具有进度跟踪和错误恢复的分块传输架构
  • 命令队列系统 - 我想测试一次运行多个命令并在端点上显示它们执行,所以我设置了队列。
  • 后台持久性控制 - 这更多是为了在后台运行进程而不是传统的持久性,可以使用persist CLIENTNAME on或off切换,这将显示或隐藏当前会话的窗口。不过,当它处于持久模式时,某些功能不起作用。
  • 睡眠/唤醒功能 - 模拟合法信标操作的睡眠功能,具有可配置的休眠期,以使其不那么嘈杂(它目前大约60%的时间工作,每次)。
  • 进程列表枚举 - 这在底层运行tasklist,但进行了一些修改以隐藏窗口执行:
1
2
3
4
cmd := exec.Command("tasklist", "/fo", "table", "/v")
cmd.SysProcAttr = &syscall.SysProcAttr{
    HideWindow: true,  
}
  • 心跳定制 - 具有健康监控的自适应心跳系统

工作原理

如前所述,C2通道通过Windows剪贴板运行(我一直在研究其他操作系统,并可能更新工具)。下面的ASCII图详细说明了通信流程:

在服务器的核心,有几种消息类型发送到客户端二进制文件,以便在客户端激活特定命令或操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 消息类型
const (
	MSG_HEARTBEAT     = "HB"
	MSG_COMMAND       = "CMD"
	MSG_RESPONSE      = "RESP"
	MSG_DATA          = "DATA"
	MSG_ERROR         = "ERR"
	MSG_SHELL         = "SHELL"
	MSG_SLEEP         = "SLEEP"
	MSG_WAKE          = "WAKE"
	MSG_SET_HEARTBEAT = "SET_HB"
	MSG_STATUS        = "STATUS"
	MSG_QUEUE         = "QUEUE"
	MSG_QUEUE_STATUS  = "QUEUE_STATUS"
	MSG_DOWNLOAD      = "DOWNLOAD"
	MSG_UPLOAD        = "UPLOAD"
	MSG_FILE_CHUNK    = "FILE_CHUNK"
	MSG_FILE_COMPLETE = "FILE_COMPLETE"
	MSG_FILE_ERROR    = "FILE_ERROR"
	MSG_PERSIST       = "PERSIST"
	MSG_ENV_INFO      = "ENV_INFO"
	MSG_SCREENSHOT    = "SCREENSHOT"
	MSG_KEYLOG        = "KEYLOG"
	MSG_PROC_LIST     = "PROC_LIST"
)

C2消息系统围绕处理从基本命令执行到分块文件传输的所有内容的通信模式构建。核心功能消息类型如MSG_HEARTBEAT维持连接,MSG_COMMAND和MSG_RESPONSE处理端点上的命令执行流。文件操作使用分块方法,带有MSG_FILE_CHUNK和MSG_FILE_COMPLETE消息,以确保通过剪贴板介质进行可靠传输。

客户端状态管理通过诸如MSG_SLEEP和MSG_WAKE等消息进行处理以用于操作安全,而MSG_QUEUE启用批处理命令处理。该系统还通过MSG_ENV_INFO和MSG_PROC_LIST支持高级侦察。

所有消息都作为加密的Base64 blob发送,前缀为SYUPD,以与合法的系统剪贴板活动混合。这种编码确保临时剪贴板监控或自动化安全工具不太可能检测到C2流量(除非专门狩猎它!),因为消息显示为良性的系统更新数据,而不是结构化的命令控制通信。这是一个示例块:

1
SYSUPD:cwOXba5E9tldKP8xwdLy7LatBk6IMTs26u/i3JUTK3PanCvGivcvw80CJSCh6jkTymGg+qzzRxsB3E+W9Xh7uJFOSDuIk+AVRmOwt5e+NC2T5HRdz48CjeFDs4r+1RIFNXtu0X1+UsZDhxcEXBr/R2piJIai4x9jfixOavp+W/UD6es4bLJgxXbvwh6DNjTHuaEbbCj2jtjBva+Q09UXENTOwa1ftJKBxt3CpQzb

在我的实验室中运行服务器,服务器在WS01上运行,客户端在DC01上:

执行服务器二进制文件启动服务器,并给客户端一个ID,它将连接进来,如图所示:

使用send blog-demo whoami和send blog-demo dir发送命令在服务器端获得输出:

可以运行其他很好的操作,如envinfo blog-demo以从主机提取环境信息:

检测机会

正如我总是尝试为我的工具和研究包括检测机会,这里有一些Sysmon、Yara和Elastic查询:

用于检测GoClipC2的Sysmon事件ID

  • 事件ID 1(进程创建):

    • 检测初始GoClipC2客户端执行
    • 捕获隐藏窗口执行(HideWindow标志)
    • 识别cmd.exe/powershell.exe子进程的快速生成
    • 监控从临时目录执行的进程
  • 事件ID 7(映像/DLL加载):

    • 检测user32.dll加载以进行剪贴板API访问
    • 识别非GUI进程加载kernel32.dll
    • 捕获可疑可执行文件加载剪贴板相关库
  • 事件ID 10(进程访问):

    • 识别GoClipC2访问其他进程以进行侦察
    • 检测环境检测尝试
    • 监控跨进程剪贴板服务交互
    • 捕获潜在的权限提升活动
  • 事件ID 11(文件创建):

    • 监控通过剪贴板分块的文件传输
    • 检测操作期间创建的临时文件
    • 跟踪带有"downloaded_“前缀的下载文件

除了sysmon,这里还有一些sigma规则(我不是检测工程师,所以如果它们很烂,抱歉!):https://github.com/ZephrFish/GoClipC2/tree/main/detection

未来计划

我有一个半功能的COFFLoader端口,适用于进程内执行,我可能作为GoClipC2的附加组件发布。也可能看看从反取证的角度还能做些什么,以使用更多原生Go函数而不是子进程来提高执行的操作安全性。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计