mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-06-21 10:21:21 +00:00
858 lines
19 KiB
Go
858 lines
19 KiB
Go
package Plugins
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/go-ldap/ldap/v3/gssapi"
|
||
"github.com/shadow1ng/fscan/Common"
|
||
"log"
|
||
"os/exec"
|
||
"strconv"
|
||
"strings"
|
||
"syscall"
|
||
"unsafe"
|
||
|
||
"github.com/go-ldap/ldap/v3"
|
||
)
|
||
|
||
type DomainInfo struct {
|
||
conn *ldap.Conn
|
||
baseDN string
|
||
}
|
||
|
||
func (d *DomainInfo) Close() {
|
||
if d.conn != nil {
|
||
d.conn.Close()
|
||
}
|
||
}
|
||
|
||
func (d *DomainInfo) GetCAComputers() ([]string, error) {
|
||
// 在Configuration容器中查找CA服务器
|
||
searchRequest := ldap.NewSearchRequest(
|
||
"CN=Configuration,"+d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectCategory=pKIEnrollmentService))", // CA服务器的查询条件
|
||
[]string{"cn", "dNSHostName"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var caComputers []string
|
||
for _, entry := range sr.Entries {
|
||
cn := entry.GetAttributeValue("cn")
|
||
if cn != "" {
|
||
caComputers = append(caComputers, cn)
|
||
}
|
||
}
|
||
return caComputers, nil
|
||
}
|
||
|
||
func (d *DomainInfo) GetExchangeServers() ([]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectCategory=group)(cn=Exchange Servers))",
|
||
[]string{"member"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var exchangeServers []string
|
||
for _, entry := range sr.Entries {
|
||
for _, member := range entry.GetAttributeValues("member") {
|
||
if member != "" {
|
||
exchangeServers = append(exchangeServers, member)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 移除第一个条目(如果存在)
|
||
if len(exchangeServers) > 1 {
|
||
exchangeServers = exchangeServers[1:]
|
||
}
|
||
|
||
return exchangeServers, nil
|
||
}
|
||
|
||
func (d *DomainInfo) GetMsSqlServers() ([]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectClass=computer)(servicePrincipalName=MSSQLSvc*))",
|
||
[]string{"name"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var sqlServers []string
|
||
for _, entry := range sr.Entries {
|
||
name := entry.GetAttributeValue("name")
|
||
if name != "" {
|
||
sqlServers = append(sqlServers, name)
|
||
}
|
||
}
|
||
|
||
return sqlServers, nil
|
||
}
|
||
|
||
func (d *DomainInfo) GetSpecialComputers() (map[string][]string, error) {
|
||
results := make(map[string][]string)
|
||
|
||
// 获取SQL Server
|
||
sqlServers, err := d.GetMsSqlServers()
|
||
if err == nil && len(sqlServers) > 0 {
|
||
results["SQL服务器"] = sqlServers
|
||
}
|
||
|
||
// 获取CA服务器
|
||
caComputers, err := d.GetCAComputers()
|
||
if err == nil && len(caComputers) > 0 {
|
||
results["CA服务器"] = caComputers
|
||
}
|
||
|
||
// 获取域控制器
|
||
dcQuery := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))",
|
||
[]string{"cn"},
|
||
nil,
|
||
)
|
||
|
||
if sr, err := d.conn.SearchWithPaging(dcQuery, 10000); err == nil {
|
||
var dcs []string
|
||
for _, entry := range sr.Entries {
|
||
name := entry.GetAttributeValue("cn")
|
||
if name != "" {
|
||
dcs = append(dcs, name)
|
||
}
|
||
}
|
||
if len(dcs) > 0 {
|
||
results["域控制器"] = dcs
|
||
}
|
||
}
|
||
|
||
// 获取Exchange服务器
|
||
exchangeServers, err := d.GetExchangeServers()
|
||
if err == nil && len(exchangeServers) > 0 {
|
||
results["Exchange服务器"] = exchangeServers
|
||
}
|
||
|
||
return results, nil
|
||
}
|
||
|
||
// 获取域用户
|
||
func (d *DomainInfo) GetDomainUsers() ([]string, error) {
|
||
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectCategory=person)(objectClass=user))",
|
||
[]string{"sAMAccountName"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var users []string
|
||
for _, entry := range sr.Entries {
|
||
users = append(users, entry.GetAttributeValue("sAMAccountName"))
|
||
}
|
||
|
||
return users, nil
|
||
}
|
||
|
||
// 获取域管理员
|
||
func (d *DomainInfo) GetDomainAdmins() ([]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectCategory=group)(cn=Domain Admins))",
|
||
[]string{"member", "sAMAccountName"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var admins []string
|
||
if len(sr.Entries) > 0 {
|
||
// 获取组成员
|
||
members := sr.Entries[0].GetAttributeValues("member")
|
||
|
||
// 对每个成员DN执行查询以获取其sAMAccountName
|
||
for _, memberDN := range members {
|
||
memberSearch := ldap.NewSearchRequest(
|
||
memberDN,
|
||
ldap.ScopeBaseObject,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(objectClass=*)",
|
||
[]string{"sAMAccountName"},
|
||
nil,
|
||
)
|
||
|
||
memberResult, err := d.conn.Search(memberSearch)
|
||
if err != nil {
|
||
continue // 跳过出错的成员
|
||
}
|
||
|
||
if len(memberResult.Entries) > 0 {
|
||
samAccountName := memberResult.Entries[0].GetAttributeValue("sAMAccountName")
|
||
if samAccountName != "" {
|
||
admins = append(admins, samAccountName)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return admins, nil
|
||
}
|
||
|
||
// 获取组织单位(OU)
|
||
func (d *DomainInfo) GetOUs() ([]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(objectClass=organizationalUnit)",
|
||
[]string{"ou"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var ous []string
|
||
for _, entry := range sr.Entries {
|
||
ou := entry.GetAttributeValue("ou")
|
||
if ou != "" {
|
||
ous = append(ous, ou)
|
||
}
|
||
}
|
||
return ous, nil
|
||
}
|
||
|
||
func (d *DomainInfo) GetComputers() ([]Computer, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectClass=computer))",
|
||
[]string{"cn", "operatingSystem", "dNSHostName"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var computers []Computer
|
||
for _, entry := range sr.Entries {
|
||
computer := Computer{
|
||
Name: entry.GetAttributeValue("cn"),
|
||
OperatingSystem: entry.GetAttributeValue("operatingSystem"),
|
||
DNSHostName: entry.GetAttributeValue("dNSHostName"),
|
||
}
|
||
computers = append(computers, computer)
|
||
}
|
||
return computers, nil
|
||
}
|
||
|
||
// 定义计算机结构体
|
||
type Computer struct {
|
||
Name string
|
||
OperatingSystem string
|
||
DNSHostName string
|
||
}
|
||
|
||
// 获取信任域关系
|
||
func (d *DomainInfo) GetTrustDomains() ([]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectClass=trustedDomain))",
|
||
[]string{"cn", "trustDirection", "trustType"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var trustInfo []string
|
||
for _, entry := range sr.Entries {
|
||
cn := entry.GetAttributeValue("cn")
|
||
if cn != "" {
|
||
trustInfo = append(trustInfo, cn)
|
||
}
|
||
}
|
||
return trustInfo, nil
|
||
}
|
||
|
||
// 获取域管理员组成员
|
||
func (d *DomainInfo) GetAdminGroups() (map[string][]string, error) {
|
||
adminGroups := map[string]string{
|
||
"Domain Admins": "(&(objectClass=group)(cn=Domain Admins))",
|
||
"Enterprise Admins": "(&(objectClass=group)(cn=Enterprise Admins))",
|
||
"Administrators": "(&(objectClass=group)(cn=Administrators))",
|
||
}
|
||
|
||
results := make(map[string][]string)
|
||
|
||
for groupName, filter := range adminGroups {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
filter,
|
||
[]string{"member"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
if len(sr.Entries) > 0 {
|
||
members := sr.Entries[0].GetAttributeValues("member")
|
||
if len(members) > 0 {
|
||
results[groupName] = members
|
||
}
|
||
}
|
||
}
|
||
return results, nil
|
||
}
|
||
|
||
// 获取委派信息
|
||
func (d *DomainInfo) GetDelegation() (map[string][]string, error) {
|
||
delegationQueries := map[string]string{
|
||
"非约束委派": "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))",
|
||
"约束委派": "(msDS-AllowedToDelegateTo=*)",
|
||
"基于资源的约束委派": "(msDS-AllowedToActOnBehalfOfOtherIdentity=*)",
|
||
}
|
||
|
||
results := make(map[string][]string)
|
||
|
||
for delegationType, query := range delegationQueries {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
query,
|
||
[]string{"cn", "distinguishedName"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
var entries []string
|
||
for _, entry := range sr.Entries {
|
||
cn := entry.GetAttributeValue("cn")
|
||
if cn != "" {
|
||
entries = append(entries, cn)
|
||
}
|
||
}
|
||
|
||
if len(entries) > 0 {
|
||
results[delegationType] = entries
|
||
}
|
||
}
|
||
return results, nil
|
||
}
|
||
|
||
// 获取AS-REP Roasting漏洞用户
|
||
func (d *DomainInfo) GetAsrepRoastUsers() ([]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))",
|
||
[]string{"sAMAccountName"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var users []string
|
||
for _, entry := range sr.Entries {
|
||
name := entry.GetAttributeValue("sAMAccountName")
|
||
if name != "" {
|
||
users = append(users, name)
|
||
}
|
||
}
|
||
return users, nil
|
||
}
|
||
|
||
// 获取域密码策略
|
||
func (d *DomainInfo) GetPasswordPolicy() (map[string]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeBaseObject,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(objectClass=*)",
|
||
[]string{
|
||
"maxPwdAge",
|
||
"minPwdAge",
|
||
"minPwdLength",
|
||
"pwdHistoryLength",
|
||
"pwdProperties",
|
||
"lockoutThreshold",
|
||
"lockoutDuration",
|
||
},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.Search(searchRequest)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if len(sr.Entries) == 0 {
|
||
return nil, fmt.Errorf("未找到密码策略信息")
|
||
}
|
||
|
||
policy := make(map[string]string)
|
||
entry := sr.Entries[0]
|
||
|
||
// 转换最大密码期限(负值,以100纳秒为单位)
|
||
if maxAge := entry.GetAttributeValue("maxPwdAge"); maxAge != "" {
|
||
maxAgeInt, _ := strconv.ParseInt(maxAge, 10, 64)
|
||
if maxAgeInt != 0 {
|
||
days := float64(maxAgeInt) * -1 / float64(864000000000)
|
||
policy["最大密码期限"] = fmt.Sprintf("%.0f天", days)
|
||
}
|
||
}
|
||
|
||
if minLength := entry.GetAttributeValue("minPwdLength"); minLength != "" {
|
||
policy["最小密码长度"] = minLength + "个字符"
|
||
}
|
||
|
||
if historyLength := entry.GetAttributeValue("pwdHistoryLength"); historyLength != "" {
|
||
policy["密码历史长度"] = historyLength + "个"
|
||
}
|
||
|
||
if lockoutThreshold := entry.GetAttributeValue("lockoutThreshold"); lockoutThreshold != "" {
|
||
policy["账户锁定阈值"] = lockoutThreshold + "次"
|
||
}
|
||
|
||
return policy, nil
|
||
}
|
||
|
||
// 获取SPN信息
|
||
func (d *DomainInfo) GetSPNs() (map[string][]string, error) {
|
||
searchRequest := ldap.NewSearchRequest(
|
||
d.baseDN,
|
||
ldap.ScopeWholeSubtree,
|
||
ldap.NeverDerefAliases,
|
||
0,
|
||
0,
|
||
false,
|
||
"(servicePrincipalName=*)",
|
||
[]string{"distinguishedName", "servicePrincipalName", "cn"},
|
||
nil,
|
||
)
|
||
|
||
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
spns := make(map[string][]string)
|
||
for _, entry := range sr.Entries {
|
||
dn := entry.GetAttributeValue("distinguishedName")
|
||
_ = entry.GetAttributeValue("cn")
|
||
spnList := entry.GetAttributeValues("servicePrincipalName")
|
||
if len(spnList) > 0 {
|
||
key := fmt.Sprintf("[*] SPN:%s", dn)
|
||
spns[key] = spnList
|
||
}
|
||
}
|
||
return spns, nil
|
||
}
|
||
|
||
// 获取域控制器地址
|
||
func getDomainController() (string, error) {
|
||
// 先尝试使用 wmic 获取当前域名
|
||
cmd := exec.Command("wmic", "computersystem", "get", "domain")
|
||
output, err := cmd.Output()
|
||
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 == "" {
|
||
return "", fmt.Errorf("域名为空")
|
||
}
|
||
|
||
// 使用 nslookup 查询域控制器
|
||
cmd = exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain))
|
||
output, err = cmd.Output()
|
||
if err != nil {
|
||
return "", fmt.Errorf("查询域控制器失败: %v", err)
|
||
}
|
||
|
||
// 解析 nslookup 输出
|
||
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, ".")
|
||
return dcHost, nil
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果上述方法失败,尝试直接使用域名前缀加上 DC 后缀
|
||
domainParts := strings.Split(domain, ".")
|
||
if len(domainParts) > 0 {
|
||
return fmt.Sprintf("dc.%s", domain), nil
|
||
}
|
||
|
||
return "", fmt.Errorf("无法获取域控制器地址")
|
||
}
|
||
|
||
func NewDomainInfo() (*DomainInfo, error) {
|
||
// 获取域控制器地址
|
||
dcHost, err := getDomainController()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("获取域控制器失败: %v", err)
|
||
}
|
||
|
||
// 创建SSPI客户端
|
||
ldapClient, err := gssapi.NewSSPIClient()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建SSPI客户端失败: %v", err)
|
||
}
|
||
defer ldapClient.Close()
|
||
|
||
// 创建LDAP连接
|
||
conn, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", dcHost))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("LDAP连接失败: %v", err)
|
||
}
|
||
|
||
// 使用GSSAPI进行绑定
|
||
err = conn.GSSAPIBind(ldapClient, fmt.Sprintf("ldap/%s", dcHost), "")
|
||
if err != nil {
|
||
conn.Close()
|
||
return nil, fmt.Errorf("GSSAPI绑定失败: %v", err)
|
||
}
|
||
|
||
// 先执行一个根搜索来获取defaultNamingContext
|
||
searchRequest := ldap.NewSearchRequest(
|
||
"",
|
||
ldap.ScopeBaseObject,
|
||
ldap.NeverDerefAliases,
|
||
0, 0, false,
|
||
"(objectClass=*)",
|
||
[]string{"defaultNamingContext"},
|
||
nil,
|
||
)
|
||
|
||
result, err := conn.Search(searchRequest)
|
||
if err != nil {
|
||
conn.Close()
|
||
return nil, fmt.Errorf("获取defaultNamingContext失败: %v", err)
|
||
}
|
||
|
||
if len(result.Entries) == 0 {
|
||
conn.Close()
|
||
return nil, fmt.Errorf("未找到defaultNamingContext")
|
||
}
|
||
|
||
baseDN := result.Entries[0].GetAttributeValue("defaultNamingContext")
|
||
if baseDN == "" {
|
||
baseDN = getDomainDN(dcHost) // 使用备选方法
|
||
}
|
||
|
||
fmt.Printf("Using BaseDN: %s\n", baseDN) // 添加调试输出
|
||
|
||
return &DomainInfo{
|
||
conn: conn,
|
||
baseDN: baseDN,
|
||
}, nil
|
||
}
|
||
|
||
// 检查是否在域环境中
|
||
func IsInDomain() bool {
|
||
// 获取计算机域成员身份信息
|
||
var joinStatus uint32
|
||
var buffer uint32
|
||
|
||
ret, _, _ := syscall.NewLazyDLL("netapi32.dll").NewProc("NetGetJoinInformation").Call(
|
||
0,
|
||
uintptr(unsafe.Pointer(&joinStatus)),
|
||
uintptr(unsafe.Pointer(&buffer)),
|
||
)
|
||
|
||
if ret == 0 {
|
||
// 清理资源
|
||
syscall.NewLazyDLL("netapi32.dll").NewProc("NetApiBufferFree").Call(uintptr(buffer))
|
||
// 检查是否为域成员
|
||
return joinStatus == 3 // 3 = NetSetupDomainName 表示是域成员
|
||
}
|
||
return false
|
||
}
|
||
|
||
func DCInfoScan(info *Common.HostInfo) (err error) {
|
||
if !IsInDomain() {
|
||
return fmt.Errorf("当前系统不在域环境中")
|
||
}
|
||
|
||
// 创建DomainInfo实例,使用当前用户凭据
|
||
di, err := NewDomainInfo()
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
defer di.Close()
|
||
|
||
// 首先获取特殊计算机列表
|
||
specialComputers, err := di.GetSpecialComputers()
|
||
if err != nil {
|
||
log.Printf("获取特殊计算机失败: %v", err)
|
||
} else {
|
||
// 按固定顺序显示结果
|
||
categories := []string{
|
||
"SQL服务器",
|
||
"CA服务器",
|
||
"域控制器",
|
||
"Exchange服务器",
|
||
}
|
||
|
||
for _, category := range categories {
|
||
if computers, ok := specialComputers[category]; ok {
|
||
fmt.Printf("[*] %s:\n", category)
|
||
for _, computer := range computers {
|
||
fmt.Printf("\t%s\n", computer)
|
||
}
|
||
}
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
users, err := di.GetDomainUsers()
|
||
if err != nil {
|
||
log.Printf("获取域用户失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 打印用户信息
|
||
fmt.Println("[*] 域用户:")
|
||
for _, user := range users {
|
||
fmt.Println("\t" + user)
|
||
}
|
||
|
||
// 获取域管理员
|
||
admins, err := di.GetDomainAdmins()
|
||
if err != nil {
|
||
log.Printf("获取域管理员失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 打印域管理员信息
|
||
fmt.Println("[*] 域管理员:")
|
||
for _, admin := range admins {
|
||
fmt.Println("\t" + admin)
|
||
}
|
||
|
||
// 获取组织单位
|
||
ous, err := di.GetOUs()
|
||
if err != nil {
|
||
log.Printf("获取组织单位失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 打印组织单位信息
|
||
fmt.Println("[*] 组织单位:")
|
||
for _, ou := range ous {
|
||
fmt.Println("\t" + ou)
|
||
}
|
||
|
||
// 获取域计算机
|
||
computers, err := di.GetComputers()
|
||
if err != nil {
|
||
log.Printf("获取域计算机失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 打印域计算机信息
|
||
fmt.Println("[*] 域计算机:")
|
||
for _, computer := range computers {
|
||
fmt.Printf("\t%s", computer.Name)
|
||
if computer.OperatingSystem != "" {
|
||
fmt.Printf(" --> %s", computer.OperatingSystem)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// 获取并显示信任域关系
|
||
trustDomains, err := di.GetTrustDomains()
|
||
if err == nil {
|
||
fmt.Println("[*] 信任域关系:")
|
||
for _, domain := range trustDomains {
|
||
fmt.Printf("\t%s\n", domain)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// 获取并显示域管理员组信息
|
||
adminGroups, err := di.GetAdminGroups()
|
||
if err == nil {
|
||
for groupName, members := range adminGroups {
|
||
fmt.Printf("[*] %s成员:\n", groupName)
|
||
for _, member := range members {
|
||
fmt.Printf("\t%s\n", member)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
}
|
||
|
||
// 获取并显示委派信息
|
||
delegations, err := di.GetDelegation()
|
||
if err == nil {
|
||
for delegationType, entries := range delegations {
|
||
fmt.Printf("[*] %s:\n", delegationType)
|
||
for _, entry := range entries {
|
||
fmt.Printf("\t%s\n", entry)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
}
|
||
|
||
// 获取并显示AS-REP Roasting漏洞用户
|
||
asrepUsers, err := di.GetAsrepRoastUsers()
|
||
if err == nil {
|
||
fmt.Println("[*] AS-REP弱口令账户:")
|
||
for _, user := range asrepUsers {
|
||
fmt.Printf("\t%s\n", user)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// 获取并显示域密码策略
|
||
passwordPolicy, err := di.GetPasswordPolicy()
|
||
if err == nil {
|
||
fmt.Println("[*] 域密码策略:")
|
||
for key, value := range passwordPolicy {
|
||
fmt.Printf("\t%s: %s\n", key, value)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// 获取SPN信息
|
||
spns, err := di.GetSPNs()
|
||
if err != nil {
|
||
log.Printf("获取SPN信息失败: %v", err)
|
||
return
|
||
}
|
||
|
||
// 打印SPN信息
|
||
if len(spns) > 0 {
|
||
for dn, spnList := range spns {
|
||
fmt.Println(dn)
|
||
for _, spn := range spnList {
|
||
fmt.Printf("\t%s\n", spn)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
} else {
|
||
fmt.Println("[*] 未发现SPN信息\n")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 辅助函数:从服务器地址获取域DN
|
||
func getDomainDN(server string) string {
|
||
parts := strings.Split(server, ".")
|
||
var dn []string
|
||
for _, part := range parts {
|
||
dn = append(dn, fmt.Sprintf("DC=%s", part))
|
||
}
|
||
return strings.Join(dn, ",")
|
||
}
|