组策略启动脚本工作过程以及通过劫持写任意启动项
2023-5-5 17:34:8 Author: 安全的矛与盾(查看原文) 阅读量:15 收藏

前言

之前逆向分析过一个能够过360启动项监控的远控木马的工作原理(具体细节可以参考https://articles.zsxq.com/id_w2ue4o2bs2ju.html),关注到系统的组策略中的脚本(启动/关机)是一个很奇怪的点,360的启动项列举并不会列出这一项,所以这是一个很好的权限维持的点。

之前分析的木马是通过模拟消息的方式进行填写,这种方式太过于笨重,一直希望可以有一种简单的方式去操作这里。直到我翻遍了windows关于组策略操作相关文档(https://learn.microsoft.com/en-us/windows/win32/api/gpedit/nn-gpedit-igrouppolicyobject),也没有找到相关内容。网上有人给出了一些相关的回答(https://simplecodesoftware.com/articles/how-to-set-up-group-policy-scripts-programmatically),但是具体的操作办法都是需要先写文件,然后再去更新组策略。

那么问题来了,相关的组策略实际上到底存储在哪里?为什么更新需要这么复杂?相关的过程能不能被利用?本文就上述问题展开解答。

根据网络上历史相关的信息以及代码(https://gist.github.com/rajbos/49f70f4e2b9765da05f0526225de2450),我们可以知道与组策略中启动脚本相关的路径和注册表项主要有如下:

路径:C:\Windows\System32\GroupPolicy\Machine\Scripts

注册表项1: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts

注册表项2: \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State

通过分析发现 C:\Windows\System32\GroupPolicy\Machine\Scripts 路径下的内容仅仅只控制界面显示。

而删除scripts.ini文件后,界面上就看不到相关的表项了。

但是注册表相关的内容是依然存在的。

此时运行gpscript.exe /startup,会发现此配置是依然可以执行的。

结论: 文件内容部分只负责界面展示,注册表项只负责执行

利用这里进行权限维持的困境是:1. 直接写文件会被拦截,2. 直接写注册表也肯定会被拦截。结合此困境,我们就特别想去搞清楚如下问题: 谁负责写的文件;谁更新的注册表;更新注册表和写文件的是否是同一个程序,如果不是同一个,他们之间是怎样通信的?

首先attack到mmc.exe进程上去,在 zwWriteFile 上下断点,保存在界面上保存相关的配置,就可以跟踪到 scrptadm.dll:CScriptsSnapIn::ScriptDlgProc 函数,这是一个消息处理函数,他会调用CScriptsSnapIn::OnApplyNotify 进行保存按钮消息的处理。

这里可以清楚的看到调用 CScriptsSnapIn::SaveToIniFile 将相关的配置保存在 scripts.ini 文件中。

但是此时还没有进行注册表写入操作,我们继续跟踪,发现调用了 gpedit.dll:CGroupPolicyObject::Save函数进行了信息的保存,在其中调用了 GPAPI.dll:RefreshPolicyForPrincipal

继续分析 GPAPI.dll:RefreshPolicyForPrincipal,发现最后会进行一个rpc远程过程调用,之后结束全部的流程。

这里的RPC发送的请求包就不再具体分析了,也没有必要。因为GPAPI.dll:RefreshPolicyForPrincipal是一个导出函数,我们是可以直接进行调用的,不需要自己手工构造RPC请求。

但是到此依然没有找到写注册表的元凶,那我们接下来监控注册表写入事件,看具体哪个服务处理了此RPC请求,经过监控发现对应的进程是 C:\Windows\system32\svchost.exe -k netsvcs -p -s gpsvc,对应的服务是gpsvc;

继续分析一下gpsvc.dll, 看到函数ProcessLocalGPO中对文件路径进行了拼接,然后读取相关的文件内容:

最后调用了相关的函数进行了注册表更新。

至此组策略更新的相关的工作过程已经基本上分析完整了,下面用一个图简单的描述一下。

想写入组策略的启动脚本,我们至少有两个地方可以进行操作,写入文件或者写入注册表,但是这两个位置都毫无可能性,因为都被拦截的死死的。

通过上述分析,如果我们可以向gpsvc服务发起RPC请求,就可以实现注册表的写入,这个注册表写入肯定不会被拦截,因为hips并不清楚RPC请求的发起者是谁,它只能看到服务写入了一个注册表。

但是文件路径是被写死的,由于无法控制文件内容,依然无法实现写入任意的启动项,这里我感觉其实是无解的

但是如果有了system权限,问题就解决了。这个路径是存储在gpsvc.dll的.rdata节上的,只需要patch这个路径,就可以控制注册表写入了。

我们接下来使用 CE 来进行这个操作,首先找到进程,然后搜索字符串%SystemRoot%\System32\GroupPolicy,并将其内容修改为%ProgramData%\test

然后复制%SystemRoot%\System32\GroupPolicy目录下的文件到%ProgramData%\test 目录,并修改scripts.ini 文件内容为要执行的程序,之后执行命令行程序 gpupdate /force ,就会看到更新成功了。

然后重启也是可以成功运行的。

这里测试的时候发现只执行gpupdate是无法成功的,进行跟踪调试发现区别是调用RefreshPolicyForPrincipal的时候参数是不同的,普通情况下传入的参数是0,这里传入的是1。

由于此函数未公开,只能写死这几个参数。

下面就自己编写代码来实现整个过程,首先要找到gpsvc.dll的基址

之后读取.rdata的内存,找到字符串的位置。

然后修改内存属性,修改内存。

最后调用组策略刷新函数,强制组策略刷新。

注意这里写内存的操作会被360的核晶拦截,识别为进程注入。不开核晶是不会拦截的,毕竟只是写内存操作,没有创建线程等行为。

https://articles.zsxq.com/id_w2ue4o2bs2ju.html

https://gist.github.com/rajbos/49f70f4e2b9765da05f0526225de2450

https://simplecodesoftware.com/articles/how-to-set-up-group-policy-scripts-programmatically

我们的知识星球"安全的矛与盾"是一个既讲攻击也讲防御,开放的、前沿的安全技术分享社区。在这里你不仅可以学习到最新的攻击方法与逃避检测的技术,也可以学到最全面的安全防御体系,了解入侵检测、攻击防护系统的原理与实践。站在攻与防不同的视角看问题,提高自己对安全的理解和深度,做到: 知攻、知守、知进退;有矛、有盾、有安全。

更多的干货内容,更深入的技术交流,尽在知识星球“安全的矛与盾”,欢迎大家扫码加入!有问题请咨询微信 Manliness_man


文章来源: http://mp.weixin.qq.com/s?__biz=Mzg5MDc4OTUyNg==&mid=2247484204&idx=1&sn=2342d1e012b9813e53a24d348582a9c9&chksm=cfd608d5f8a181c31d22b5979c81ef3c9d0d69c04ff42c3e12e65cb8e02e9c7c24bce886a4c2#rd
如有侵权请联系:admin#unsafe.sh