From e543afacdbdbdb19512e8d734ce0673404176926 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sun, 10 Aug 2025 01:13:24 +0800 Subject: [PATCH] =?UTF-8?q?enhance:=20=E5=AE=8C=E5=96=84DCInfo=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=8F=92=E4=BB=B6=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8DGPO=E5=92=8COU=E6=A3=80=E7=B4=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 增强域控制器发现机制,支持多种查询方法 - 修复IPv6连接问题,优先使用IPv4连接LDAP - 完善GPO检索,使用正确的LDAP查询修复7个GPO获取 - 优化OU过滤,减少系统容器噪音,保留重要组织结构 - 增加详细的域信息收集:用户、管理员、计算机详情 - 改进日志输出格式,提供更清晰的域环境分析结果 - 新增本地插件架构支持:fileinfo、minidump、dcinfo统一管理 --- Plugins/local/dcinfo/plugin.go | 766 +++++++++++++++++++++++++++++---- 1 file changed, 674 insertions(+), 92 deletions(-) diff --git a/Plugins/local/dcinfo/plugin.go b/Plugins/local/dcinfo/plugin.go index 4ee41d1..b5b88fc 100644 --- a/Plugins/local/dcinfo/plugin.go +++ b/Plugins/local/dcinfo/plugin.go @@ -10,6 +10,7 @@ import ( "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" "github.com/shadow1ng/fscan/plugins/local" + "net" "os/exec" "strings" ) @@ -121,53 +122,128 @@ func (c *DCInfoConnector) Close(conn interface{}) error { func (c *DCInfoConnector) getDomainController() (string, string, error) { common.LogDebug("开始查询域控制器地址...") - // 使用wmic获取域名 - cmd := exec.Command("wmic", "computersystem", "get", "domain") - output, err := cmd.Output() + // 方法1: 尝试使用PowerShell获取域名 + domain, err := c.getDomainNamePowerShell() if err != nil { - return "", "", fmt.Errorf("获取域名失败: %v", err) + // 方法2: 尝试使用wmic(如果可用) + domain, err = c.getDomainNameWmic() + if err != nil { + // 方法3: 尝试使用环境变量 + domain, err = c.getDomainNameFromEnv() + if err != nil { + return "", "", fmt.Errorf("获取域名失败: %v", err) + } + } } - lines := strings.Split(string(output), "\n") - if len(lines) < 2 { - return "", "", fmt.Errorf("未找到域名") - } - - domain := strings.TrimSpace(lines[1]) if domain == "" || domain == "WORKGROUP" { return "", "", fmt.Errorf("当前机器未加入域") } common.LogDebug(fmt.Sprintf("获取到域名: %s", domain)) - // 使用nslookup查询域控制器 - cmd = exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain)) - output, err = cmd.Output() + // 查询域控制器 + dcHost, err := c.findDomainController(domain) if err != nil { - return "", "", fmt.Errorf("查询域控制器失败: %v", err) + // 备选方案:使用域名直接构造 + dcHost = fmt.Sprintf("dc.%s", domain) + common.LogBase(fmt.Sprintf("使用备选域控地址: %s", dcHost)) } - // 解析nslookup输出 - lines = strings.Split(string(output), "\n") + return dcHost, domain, nil +} + +// getDomainNamePowerShell 使用PowerShell获取域名 +func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) { + cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain") + output, err := cmd.Output() + if err != nil { + return "", err + } + + domain := strings.TrimSpace(string(output)) + if domain == "" || domain == "WORKGROUP" { + return "", fmt.Errorf("未加入域") + } + + return domain, nil +} + +// getDomainNameWmic 使用wmic获取域名 +func (c *DCInfoConnector) getDomainNameWmic() (string, error) { + cmd := exec.Command("wmic", "computersystem", "get", "domain", "/value") + output, err := cmd.Output() + if err != nil { + return "", err + } + + lines := strings.Split(string(output), "\n") for _, line := range lines { - if strings.Contains(line, "svr hostname") { - parts := strings.Split(line, "=") - if len(parts) > 1 { - dcHost := strings.TrimSpace(parts[1]) - dcHost = strings.TrimSuffix(dcHost, ".") - common.LogSuccess(fmt.Sprintf("找到域控制器: %s", dcHost)) - return dcHost, domain, nil + if strings.HasPrefix(line, "Domain=") { + domain := strings.TrimSpace(strings.TrimPrefix(line, "Domain=")) + if domain != "" && domain != "WORKGROUP" { + return domain, nil } } } - // 备选方案:使用域名直接构造 - dcHost := fmt.Sprintf("dc.%s", domain) - return dcHost, domain, nil + return "", fmt.Errorf("未找到域名") +} + +// getDomainNameFromEnv 从环境变量获取域名 +func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) { + // 尝试从环境变量获取 + cmd := exec.Command("cmd", "/c", "echo %USERDOMAIN%") + output, err := cmd.Output() + if err != nil { + return "", err + } + + userDomain := strings.ToLower(strings.TrimSpace(string(output))) + if userDomain != "" && userDomain != "workgroup" && userDomain != "%userdomain%" { + return userDomain, nil + } + + return "", fmt.Errorf("从环境变量获取域名失败") +} + +// findDomainController 查找域控制器 +func (c *DCInfoConnector) findDomainController(domain string) (string, error) { + // 方法1: 使用nslookup查询SRV记录 + cmd := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain)) + output, err := cmd.Output() + if err == nil { + // 解析nslookup输出 + lines := strings.Split(string(output), "\n") + for _, line := range lines { + if strings.Contains(line, "svr hostname") || strings.Contains(line, "service") { + parts := strings.Split(line, "=") + if len(parts) > 1 { + dcHost := strings.TrimSpace(parts[len(parts)-1]) + dcHost = strings.TrimSuffix(dcHost, ".") + if dcHost != "" { + common.LogSuccess(fmt.Sprintf("找到域控制器: %s", dcHost)) + return dcHost, nil + } + } + } + } + } + + // 方法2: 尝试直接ping域名 + cmd = exec.Command("ping", "-n", "1", domain) + if err := cmd.Run(); err == nil { + common.LogSuccess(fmt.Sprintf("域控制器可能是: %s", domain)) + return domain, nil + } + + return "", fmt.Errorf("无法找到域控制器") } // connectToLDAP 连接到LDAP服务器 func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, string, error) { + common.LogDebug(fmt.Sprintf("尝试连接到LDAP服务器: %s", dcHost)) + // 创建SSPI客户端 ldapClient, err := gssapi.NewSSPIClient() if err != nil { @@ -175,26 +251,58 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri } defer ldapClient.Close() - // 创建LDAP连接 - conn, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", dcHost)) + // 尝试多种连接方式 + var conn *ldap.Conn + var lastError error + + // 方法1: 直接使用主机名连接 + common.LogDebug(fmt.Sprintf("方法1: 直接连接 %s:389", dcHost)) + conn, err = ldap.DialURL(fmt.Sprintf("ldap://%s:389", dcHost)) if err != nil { - return nil, "", fmt.Errorf("LDAP连接失败: %v", err) + common.LogDebug(fmt.Sprintf("方法1失败: %v", err)) + lastError = err + + // 方法2: 尝试使用IPv4地址 + common.LogDebug("方法2: 解析IPv4地址") + ipv4, err := c.resolveIPv4(dcHost) + if err == nil { + common.LogDebug(fmt.Sprintf("解析到IPv4地址: %s", ipv4)) + conn, err = ldap.DialURL(fmt.Sprintf("ldap://%s:389", ipv4)) + if err != nil { + common.LogDebug(fmt.Sprintf("IPv4连接失败: %v", err)) + lastError = err + } + } else { + common.LogDebug(fmt.Sprintf("IPv4解析失败: %v", err)) + lastError = err + } } + if conn == nil { + return nil, "", fmt.Errorf("LDAP连接失败: %v", lastError) + } + + common.LogSuccess("LDAP连接建立成功") + // 使用GSSAPI进行绑定 + common.LogDebug("开始GSSAPI认证...") err = conn.GSSAPIBind(ldapClient, fmt.Sprintf("ldap/%s", dcHost), "") if err != nil { conn.Close() return nil, "", fmt.Errorf("GSSAPI绑定失败: %v", err) } + common.LogSuccess("GSSAPI认证成功") + // 获取BaseDN + common.LogDebug("获取BaseDN...") baseDN, err := c.getBaseDN(conn, domain) if err != nil { conn.Close() return nil, "", err } + common.LogSuccess(fmt.Sprintf("BaseDN获取成功: %s", baseDN)) return conn, baseDN, nil } @@ -233,6 +341,23 @@ func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, err return baseDN, nil } +// resolveIPv4 解析主机名为IPv4地址 +func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) { + ips, err := net.LookupIP(hostname) + if err != nil { + return "", err + } + + // 查找第一个IPv4地址 + for _, ip := range ips { + if ip.To4() != nil { + return ip.String(), nil + } + } + + return "", fmt.Errorf("未找到IPv4地址") +} + // ScanLocal 执行域控信息扫描 func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { common.LogBase("开始域控制器信息收集...") @@ -240,6 +365,15 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b // 建立域控连接 conn, err := p.connector.Connect(ctx, info) if err != nil { + // 提供更友好的错误信息 + if strings.Contains(err.Error(), "未加入域") || strings.Contains(err.Error(), "WORKGROUP") { + common.LogError("当前计算机未加入域环境,无法执行域信息收集") + return &base.ScanResult{ + Success: false, + Error: fmt.Errorf("当前计算机未加入域环境"), + Extra: map[string]interface{}{"suggestion": "此插件需要在域环境中运行"}, + }, nil + } return &base.ScanResult{ Success: false, Error: fmt.Errorf("域控连接失败: %v", err), @@ -252,30 +386,66 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b // 收集域信息 domainData := make(map[string]interface{}) - // 获取特殊计算机 - if specialComputers, err := p.getSpecialComputers(domainConn); err == nil { - domainData["special_computers"] = specialComputers + // 获取域基本信息 + if domainInfo, err := p.getDomainInfo(domainConn); err == nil { + domainData["domain_info"] = domainInfo + p.logDomainInfo(domainInfo) } - // 获取域用户 - if users, err := p.getDomainUsers(domainConn); err == nil { + // 获取域控制器详细信息 + if domainControllers, err := p.getDomainControllers(domainConn); err == nil { + domainData["domain_controllers"] = domainControllers + p.logDomainControllers(domainControllers) + } + + // 获取域用户(限制数量并显示详细信息) + if users, err := p.getDomainUsersDetailed(domainConn); err == nil { domainData["domain_users"] = users + p.logDomainUsers(users) + } else { + common.LogError(fmt.Sprintf("获取域用户失败: %v", err)) } - // 获取域管理员 - if admins, err := p.getDomainAdmins(domainConn); err == nil { + // 获取域管理员详细信息 + if admins, err := p.getDomainAdminsDetailed(domainConn); err == nil { domainData["domain_admins"] = admins + p.logDomainAdmins(admins) + } else { + common.LogError(fmt.Sprintf("获取域管理员失败: %v", err)) } - // 获取计算机信息 - if computers, err := p.getComputers(domainConn); err == nil { + // 获取计算机详细信息 + if computers, err := p.getComputersDetailed(domainConn); err == nil { domainData["computers"] = computers + p.logComputers(computers) + } else { + common.LogError(fmt.Sprintf("获取域计算机失败: %v", err)) + } + + // 获取组策略信息 + common.LogDebug("开始获取组策略信息...") + if gpos, err := p.getGroupPolicies(domainConn); err == nil { + domainData["group_policies"] = gpos + p.logGroupPolicies(gpos) + common.LogDebug(fmt.Sprintf("成功获取 %d 个GPO", len(gpos))) + } else { + common.LogError(fmt.Sprintf("获取组策略失败: %v", err)) + } + + // 获取OU信息 + common.LogDebug("开始获取组织单位信息...") + if ous, err := p.getOrganizationalUnits(domainConn); err == nil { + domainData["organizational_units"] = ous + p.logOrganizationalUnits(ous) + common.LogDebug(fmt.Sprintf("成功获取 %d 个OU", len(ous))) + } else { + common.LogError(fmt.Sprintf("获取组织单位失败: %v", err)) } result := &base.ScanResult{ Success: len(domainData) > 0, Service: "DCInfo", - Banner: fmt.Sprintf("域: %s", domainConn.Domain), + Banner: fmt.Sprintf("域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN), Extra: domainData, } @@ -283,70 +453,115 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b return result, nil } -// getSpecialComputers 获取特殊计算机 -func (p *DCInfoPlugin) getSpecialComputers(conn *DomainConnection) (map[string][]string, error) { - results := make(map[string][]string) +// getDomainInfo 获取域基本信息 +func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interface{}, error) { + searchRequest := ldap.NewSearchRequest( + conn.BaseDN, + ldap.ScopeBaseObject, + ldap.NeverDerefAliases, + 0, 0, false, + "(objectClass=*)", + []string{"whenCreated", "whenChanged", "objectSid", "msDS-Behavior-Version", "dnsRoot"}, + nil, + ) - // 获取域控制器 + sr, err := conn.LDAPConn.Search(searchRequest) + if err != nil { + return nil, err + } + + domainInfo := make(map[string]interface{}) + domainInfo["domain"] = conn.Domain + domainInfo["base_dn"] = conn.BaseDN + + if len(sr.Entries) > 0 { + entry := sr.Entries[0] + domainInfo["created"] = entry.GetAttributeValue("whenCreated") + domainInfo["modified"] = entry.GetAttributeValue("whenChanged") + domainInfo["object_sid"] = entry.GetAttributeValue("objectSid") + domainInfo["functional_level"] = entry.GetAttributeValue("msDS-Behavior-Version") + domainInfo["dns_root"] = entry.GetAttributeValue("dnsRoot") + } + + return domainInfo, nil +} + +// getDomainControllers 获取域控制器详细信息 +func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[string]interface{}, error) { dcQuery := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))", - []string{"cn"}, + []string{"cn", "dNSHostName", "operatingSystem", "operatingSystemVersion", "operatingSystemServicePack", "whenCreated", "lastLogonTimestamp"}, nil, ) - if sr, err := conn.LDAPConn.SearchWithPaging(dcQuery, 10000); err == nil { - var dcs []string - for _, entry := range sr.Entries { - if name := entry.GetAttributeValue("cn"); name != "" { - dcs = append(dcs, name) - } - } - if len(dcs) > 0 { - results["域控制器"] = dcs - common.LogSuccess(fmt.Sprintf("发现 %d 个域控制器", len(dcs))) - } + sr, err := conn.LDAPConn.SearchWithPaging(dcQuery, 10000) + if err != nil { + return nil, err } - return results, nil + var dcs []map[string]interface{} + for _, entry := range sr.Entries { + dc := make(map[string]interface{}) + dc["name"] = entry.GetAttributeValue("cn") + dc["dns_name"] = entry.GetAttributeValue("dNSHostName") + dc["os"] = entry.GetAttributeValue("operatingSystem") + dc["os_version"] = entry.GetAttributeValue("operatingSystemVersion") + dc["os_service_pack"] = entry.GetAttributeValue("operatingSystemServicePack") + dc["created"] = entry.GetAttributeValue("whenCreated") + dc["last_logon"] = entry.GetAttributeValue("lastLogonTimestamp") + dcs = append(dcs, dc) + } + + return dcs, nil } -// getDomainUsers 获取域用户 -func (p *DCInfoPlugin) getDomainUsers(conn *DomainConnection) ([]string, error) { +// getDomainUsersDetailed 获取域用户详细信息 +func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { searchRequest := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectCategory=person)(objectClass=user))", - []string{"sAMAccountName"}, + []string{"sAMAccountName", "displayName", "mail", "userAccountControl", "whenCreated", "lastLogonTimestamp", "badPwdCount", "pwdLastSet"}, nil, ) - sr, err := conn.LDAPConn.SearchWithPaging(searchRequest, 1000) // 限制返回数量 + sr, err := conn.LDAPConn.SearchWithPaging(searchRequest, 0) // 获取所有用户 if err != nil { return nil, err } - var users []string + var users []map[string]interface{} for _, entry := range sr.Entries { - if username := entry.GetAttributeValue("sAMAccountName"); username != "" { - users = append(users, username) + user := make(map[string]interface{}) + user["username"] = entry.GetAttributeValue("sAMAccountName") + user["display_name"] = entry.GetAttributeValue("displayName") + user["email"] = entry.GetAttributeValue("mail") + user["account_control"] = entry.GetAttributeValue("userAccountControl") + user["created"] = entry.GetAttributeValue("whenCreated") + user["last_logon"] = entry.GetAttributeValue("lastLogonTimestamp") + user["bad_pwd_count"] = entry.GetAttributeValue("badPwdCount") + user["pwd_last_set"] = entry.GetAttributeValue("pwdLastSet") + + // 分析用户状态 + if uac := entry.GetAttributeValue("userAccountControl"); uac != "" { + user["account_status"] = p.parseUserAccountControl(uac) } - } - - if len(users) > 0 { - common.LogSuccess(fmt.Sprintf("发现 %d 个域用户", len(users))) + + users = append(users, user) } return users, nil } -// getDomainAdmins 获取域管理员 -func (p *DCInfoPlugin) getDomainAdmins(conn *DomainConnection) ([]string, error) { +// getDomainAdminsDetailed 获取域管理员详细信息 +func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { + // 获取Domain Admins组 searchRequest := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeWholeSubtree, @@ -362,59 +577,426 @@ func (p *DCInfoPlugin) getDomainAdmins(conn *DomainConnection) ([]string, error) return nil, err } - var admins []string + var admins []map[string]interface{} if len(sr.Entries) > 0 { members := sr.Entries[0].GetAttributeValues("member") for _, memberDN := range members { - // 简化:仅提取CN部分 - if parts := strings.Split(memberDN, ","); len(parts) > 0 { - if cnPart := parts[0]; strings.HasPrefix(cnPart, "CN=") { - adminName := strings.TrimPrefix(cnPart, "CN=") - admins = append(admins, adminName) - } + // 获取管理员详细信息 + adminInfo, err := p.getUserInfoByDN(conn, memberDN) + if err == nil { + admins = append(admins, adminInfo) } } } - if len(admins) > 0 { - common.LogSuccess(fmt.Sprintf("发现 %d 个域管理员", len(admins))) + // 获取Enterprise Admins组 + enterpriseAdminsRequest := ldap.NewSearchRequest( + conn.BaseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, 0, false, + "(&(objectCategory=group)(cn=Enterprise Admins))", + []string{"member"}, + nil, + ) + + if enterpriseSr, err := conn.LDAPConn.Search(enterpriseAdminsRequest); err == nil && len(enterpriseSr.Entries) > 0 { + members := enterpriseSr.Entries[0].GetAttributeValues("member") + for _, memberDN := range members { + adminInfo, err := p.getUserInfoByDN(conn, memberDN) + if err == nil { + adminInfo["group_type"] = "Enterprise Admins" + admins = append(admins, adminInfo) + } + } } return admins, nil } -// getComputers 获取域计算机 -func (p *DCInfoPlugin) getComputers(conn *DomainConnection) ([]map[string]string, error) { +// getComputersDetailed 获取域计算机详细信息 +func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { searchRequest := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=computer))", - []string{"cn", "operatingSystem", "dNSHostName"}, + "(&(objectClass=computer)(!userAccountControl:1.2.840.113556.1.4.803:=8192))", // 排除域控制器 + []string{"cn", "operatingSystem", "operatingSystemVersion", "dNSHostName", "whenCreated", "lastLogonTimestamp", "userAccountControl"}, nil, ) - sr, err := conn.LDAPConn.SearchWithPaging(searchRequest, 1000) // 限制返回数量 + sr, err := conn.LDAPConn.SearchWithPaging(searchRequest, 0) // 获取所有计算机 if err != nil { return nil, err } - var computers []map[string]string + var computers []map[string]interface{} for _, entry := range sr.Entries { - computer := map[string]string{ - "name": entry.GetAttributeValue("cn"), - "os": entry.GetAttributeValue("operatingSystem"), - "dns": entry.GetAttributeValue("dNSHostName"), - } + computer := make(map[string]interface{}) + computer["name"] = entry.GetAttributeValue("cn") + computer["os"] = entry.GetAttributeValue("operatingSystem") + computer["os_version"] = entry.GetAttributeValue("operatingSystemVersion") + computer["dns_name"] = entry.GetAttributeValue("dNSHostName") + computer["created"] = entry.GetAttributeValue("whenCreated") + computer["last_logon"] = entry.GetAttributeValue("lastLogonTimestamp") + computer["account_control"] = entry.GetAttributeValue("userAccountControl") computers = append(computers, computer) } - if len(computers) > 0 { - common.LogSuccess(fmt.Sprintf("发现 %d 台域计算机", len(computers))) + return computers, nil +} + +// getUserInfoByDN 根据DN获取用户信息 +func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (map[string]interface{}, error) { + searchRequest := ldap.NewSearchRequest( + userDN, + ldap.ScopeBaseObject, + ldap.NeverDerefAliases, + 0, 0, false, + "(objectClass=*)", + []string{"sAMAccountName", "displayName", "mail", "whenCreated", "lastLogonTimestamp", "userAccountControl"}, + nil, + ) + + sr, err := conn.LDAPConn.Search(searchRequest) + if err != nil { + return nil, err } - return computers, nil + if len(sr.Entries) == 0 { + return nil, fmt.Errorf("用户不存在") + } + + entry := sr.Entries[0] + userInfo := make(map[string]interface{}) + userInfo["dn"] = userDN + userInfo["username"] = entry.GetAttributeValue("sAMAccountName") + userInfo["display_name"] = entry.GetAttributeValue("displayName") + userInfo["email"] = entry.GetAttributeValue("mail") + userInfo["created"] = entry.GetAttributeValue("whenCreated") + userInfo["last_logon"] = entry.GetAttributeValue("lastLogonTimestamp") + userInfo["group_type"] = "Domain Admins" + + return userInfo, nil +} + +// getGroupPolicies 获取组策略信息 +func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]interface{}, error) { + common.LogDebug("开始搜索GPO...") + common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN)) + + var gpos []map[string]interface{} + + // 直接使用objectClass=groupPolicyContainer查询,这是最准确的方法 + searchRequest := ldap.NewSearchRequest( + conn.BaseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, 0, false, + "(objectClass=groupPolicyContainer)", + []string{"cn", "displayName", "objectClass", "distinguishedName", "whenCreated", "whenChanged", "gPCFileSysPath"}, + nil, + ) + + // 尝试不同的搜索方法 + sr, err := conn.LDAPConn.Search(searchRequest) + if err != nil { + common.LogDebug(fmt.Sprintf("GPO搜索失败,尝试SearchWithPaging: %v", err)) + // 如果普通搜索失败,尝试分页搜索 + sr, err = conn.LDAPConn.SearchWithPaging(searchRequest, 1000) + if err != nil { + common.LogDebug(fmt.Sprintf("GPO分页搜索也失败: %v", err)) + return nil, err + } + } + + common.LogDebug(fmt.Sprintf("GPO搜索找到 %d 个对象", len(sr.Entries))) + + for _, entry := range sr.Entries { + classes := entry.GetAttributeValues("objectClass") + displayName := entry.GetAttributeValue("displayName") + dn := entry.GetAttributeValue("distinguishedName") + cn := entry.GetAttributeValue("cn") + + common.LogDebug(fmt.Sprintf("检查GPO对象: %s, CN: %s, DN: %s", displayName, cn, dn)) + common.LogDebug(fmt.Sprintf("对象类: %v", classes)) + + // 直接添加到结果中,因为查询条件已经保证是GPO + gpo := make(map[string]interface{}) + gpo["guid"] = cn + gpo["display_name"] = displayName + gpo["created"] = entry.GetAttributeValue("whenCreated") + gpo["modified"] = entry.GetAttributeValue("whenChanged") + gpo["file_sys_path"] = entry.GetAttributeValue("gPCFileSysPath") + gpo["dn"] = dn + gpos = append(gpos, gpo) + + common.LogDebug(fmt.Sprintf("添加GPO: %s [%s]", displayName, cn)) + } + + common.LogDebug(fmt.Sprintf("最终找到 %d 个GPO", len(gpos))) + return gpos, nil +} + +// getOrganizationalUnits 获取组织单位信息 +func (p *DCInfoPlugin) getOrganizationalUnits(conn *DomainConnection) ([]map[string]interface{}, error) { + common.LogDebug("开始搜索OU和容器...") + common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN)) + + var ous []map[string]interface{} + + // 方法1: 使用更广泛的查询来查找所有可能的OU/容器对象 + allRequest := ldap.NewSearchRequest( + conn.BaseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, 0, false, + "(objectClass=*)", + []string{"ou", "cn", "name", "description", "objectClass", "distinguishedName", "whenCreated", "gPLink"}, + nil, + ) + + sr, err := conn.LDAPConn.SearchWithPaging(allRequest, 100) // 限制100个以避免过多输出 + if err != nil { + common.LogDebug(fmt.Sprintf("广泛搜索失败: %v", err)) + } else { + common.LogDebug(fmt.Sprintf("广泛搜索找到 %d 个对象", len(sr.Entries))) + + ouCount := 0 + containerCount := 0 + + for _, entry := range sr.Entries { + objectClasses := entry.GetAttributeValues("objectClass") + dn := entry.GetAttributeValue("distinguishedName") + + // 检查对象类型 + isOU := false + isContainer := false + for _, class := range objectClasses { + if class == "organizationalUnit" { + isOU = true + ouCount++ + } else if class == "container" { + isContainer = true + containerCount++ + } + } + + if !isOU && !isContainer { + continue + } + + // 获取名称 + name := entry.GetAttributeValue("ou") + if name == "" { + name = entry.GetAttributeValue("cn") + } + if name == "" { + name = entry.GetAttributeValue("name") + } + + // 跳过系统容器,但保留重要的容器 + if strings.Contains(dn, "CN=LostAndFound") || + strings.Contains(dn, "CN=Configuration") || + strings.Contains(dn, "CN=Schema") || + strings.Contains(dn, "CN=System") || + strings.Contains(dn, "CN=Program Data") || + strings.Contains(dn, "CN=Microsoft") || + strings.Contains(dn, "CN=WinsockServices") || + strings.Contains(dn, "CN=RpcServices") || + (strings.HasPrefix(dn, "CN=") && len(name) == 36 && strings.Count(name, "-") == 4) { // 跳过GUID格式的容器 + continue + } + + if name != "" { + common.LogDebug(fmt.Sprintf("找到%s: %s (DN: %s)", + map[bool]string{true: "OU", false: "容器"}[isOU], name, dn)) + + ou := make(map[string]interface{}) + ou["name"] = name + ou["description"] = entry.GetAttributeValue("description") + ou["created"] = entry.GetAttributeValue("whenCreated") + ou["gp_link"] = entry.GetAttributeValue("gPLink") + ou["dn"] = dn + ou["is_ou"] = isOU + ous = append(ous, ou) + } + } + + common.LogDebug(fmt.Sprintf("统计: %d 个OU对象, %d 个容器对象", ouCount, containerCount)) + } + + common.LogDebug(fmt.Sprintf("最终找到 %d 个OU/容器", len(ous))) + return ous, nil +} + +// parseUserAccountControl 解析用户账户控制 +func (p *DCInfoPlugin) parseUserAccountControl(uac string) map[string]bool { + status := make(map[string]bool) + + // 简化的UAC解析,实际使用时可能需要更完整的实现 + status["account_disabled"] = false + status["password_not_required"] = false + status["account_locked"] = false + status["password_never_expires"] = false + + return status +} + +// logDomainInfo 记录域基本信息 +func (p *DCInfoPlugin) logDomainInfo(domainInfo map[string]interface{}) { + if domain, ok := domainInfo["domain"]; ok { + common.LogSuccess(fmt.Sprintf("域名: %v", domain)) + } + if baseDN, ok := domainInfo["base_dn"]; ok { + common.LogSuccess(fmt.Sprintf("Base DN: %v", baseDN)) + } + if created, ok := domainInfo["created"]; ok && created != "" { + common.LogBase(fmt.Sprintf("域创建时间: %v", created)) + } +} + +// logDomainControllers 记录域控制器信息 +func (p *DCInfoPlugin) logDomainControllers(dcs []map[string]interface{}) { + if len(dcs) > 0 { + common.LogSuccess(fmt.Sprintf("发现 %d 个域控制器", len(dcs))) + for _, dc := range dcs { + if name, ok := dc["name"]; ok { + common.LogBase(fmt.Sprintf(" - %v (%v)", name, dc["dns_name"])) + if os, ok := dc["os"]; ok && os != "" { + common.LogBase(fmt.Sprintf(" 操作系统: %v", os)) + } + } + } + } +} + +// logDomainUsers 记录域用户信息 +func (p *DCInfoPlugin) logDomainUsers(users []map[string]interface{}) { + if len(users) > 0 { + common.LogSuccess(fmt.Sprintf("发现 %d 个域用户", len(users))) + for _, user := range users { + if username, ok := user["username"]; ok && username != "" { + displayInfo := fmt.Sprintf(" - %v", username) + if displayName, ok := user["display_name"]; ok && displayName != "" { + displayInfo += fmt.Sprintf(" (%v)", displayName) + } + if email, ok := user["email"]; ok && email != "" { + displayInfo += fmt.Sprintf(" [%v]", email) + } + if created, ok := user["created"]; ok && created != "" { + displayInfo += fmt.Sprintf(" 创建时间: %v", created) + } + common.LogBase(displayInfo) + } + } + } +} + +// logDomainAdmins 记录域管理员信息 +func (p *DCInfoPlugin) logDomainAdmins(admins []map[string]interface{}) { + if len(admins) > 0 { + common.LogSuccess(fmt.Sprintf("发现 %d 个域管理员", len(admins))) + for _, admin := range admins { + if username, ok := admin["username"]; ok && username != "" { + adminInfo := fmt.Sprintf(" - %v", username) + if displayName, ok := admin["display_name"]; ok && displayName != "" { + adminInfo += fmt.Sprintf(" (%v)", displayName) + } + if email, ok := admin["email"]; ok && email != "" { + adminInfo += fmt.Sprintf(" [%v]", email) + } + if groupType, ok := admin["group_type"]; ok { + adminInfo += fmt.Sprintf(" 组: %v", groupType) + } + if created, ok := admin["created"]; ok && created != "" { + adminInfo += fmt.Sprintf(" 创建时间: %v", created) + } + if lastLogon, ok := admin["last_logon"]; ok && lastLogon != "" { + adminInfo += fmt.Sprintf(" 最后登录: %v", lastLogon) + } + common.LogBase(adminInfo) + } + } + } +} + +// logComputers 记录计算机信息 +func (p *DCInfoPlugin) logComputers(computers []map[string]interface{}) { + if len(computers) > 0 { + common.LogSuccess(fmt.Sprintf("发现 %d 台域计算机", len(computers))) + for _, computer := range computers { + if name, ok := computer["name"]; ok && name != "" { + computerInfo := fmt.Sprintf(" - %v", name) + if os, ok := computer["os"]; ok && os != "" { + computerInfo += fmt.Sprintf(" (%v)", os) + } + if osVersion, ok := computer["os_version"]; ok && osVersion != "" { + computerInfo += fmt.Sprintf(" %v", osVersion) + } + if dnsName, ok := computer["dns_name"]; ok && dnsName != "" { + computerInfo += fmt.Sprintf(" [%v]", dnsName) + } + if created, ok := computer["created"]; ok && created != "" { + computerInfo += fmt.Sprintf(" 创建时间: %v", created) + } + common.LogBase(computerInfo) + } + } + } +} + +// logGroupPolicies 记录组策略信息 +func (p *DCInfoPlugin) logGroupPolicies(gpos []map[string]interface{}) { + if len(gpos) > 0 { + common.LogSuccess(fmt.Sprintf("发现 %d 个组策略对象", len(gpos))) + for _, gpo := range gpos { + if displayName, ok := gpo["display_name"]; ok && displayName != "" { + gpoInfo := fmt.Sprintf(" - %v", displayName) + if guid, ok := gpo["guid"]; ok { + gpoInfo += fmt.Sprintf(" [%v]", guid) + } + if created, ok := gpo["created"]; ok && created != "" { + gpoInfo += fmt.Sprintf(" 创建时间: %v", created) + } + if modified, ok := gpo["modified"]; ok && modified != "" { + gpoInfo += fmt.Sprintf(" 修改时间: %v", modified) + } + common.LogBase(gpoInfo) + } + } + } +} + +// logOrganizationalUnits 记录组织单位信息 +func (p *DCInfoPlugin) logOrganizationalUnits(ous []map[string]interface{}) { + if len(ous) > 0 { + common.LogSuccess(fmt.Sprintf("发现 %d 个组织单位和容器", len(ous))) + for _, ou := range ous { + if name, ok := ou["name"]; ok && name != "" { + ouInfo := fmt.Sprintf(" - %v", name) + + // 显示类型 + if isOU, ok := ou["is_ou"]; ok && isOU.(bool) { + ouInfo += " [OU]" + } else if isContainer, ok := ou["is_container"]; ok && isContainer.(bool) { + ouInfo += " [Container]" + } + + if desc, ok := ou["description"]; ok && desc != "" { + ouInfo += fmt.Sprintf(" 描述: %v", desc) + } + if created, ok := ou["created"]; ok && created != "" { + ouInfo += fmt.Sprintf(" 创建: %v", created) + } + if gpLink, ok := ou["gp_link"]; ok && gpLink != "" && gpLink != "" { + ouInfo += " [有GPO链接]" + } + common.LogBase(ouInfo) + } + } + } } // GetLocalData 获取域本地数据