一个用 Golang 编写的 ADWS 实用客户端。
Sopa 实现了 ADWS 协议栈(MS-NNS + MC-NMF + SOAP),提供了以下命令行功能:
query: 通过 WS-Enumeration 的 Enumerate + Pull 循环运行 LDAP 过滤器搜索,支持属性投影、作用域控制(Base/OneLevel/Subtree)和分页get: 通过 WS-Transfer Get 根据 DN 获取单个对象create: 通过 WS-Transfer ResourceFactory 创建对象(内置类型:user, computer, group, OU, container;或通过 IMDA AddRequest 使用 YAML 模板创建自定义对象)delete: 通过 WS-Transfer Delete 根据 DN 删除对象attr: 通过 WS-Transfer Put 添加、替换或删除现有对象上的单个属性值set-password: 通过 MS-ADCAP SetPassword 设置账户密码change-password: 通过 MS-ADCAP ChangePassword 更改账户密码(需要旧密码)translate-name: 通过 TranslateName 在 DN 和规范名称格式之间转换groups: 通过 GetADPrincipalGroupMembership / GetADPrincipalAuthorizationGroup 列出主体的组成员身份或授权组members: 通过 GetADGroupMember 枚举组成员(可选递归)optfeature: 通过 ChangeOptionalFeature 切换可选 AD 功能(例如回收站)info: 通过 GetVersion、GetADDomain、GetADForest、GetADDomainControllers 检索拓扑元数据(版本、域、林、DC 列表)mex: 通过未认证的 WS-MetadataExchange 请求获取 ADWS 服务端点元数据$ go install github.com/Macmod/sopa/cmd/sopa@latest
# Auth flags (-u, -p, -d, -k, -H, -c, ...) are omitted for brevity - see Authentication section.
# Search objects by LDAP filter
$ sopa [auth_flags] query --dc <DC> --filter '(objectClass=*)'
# Fetch a single object by DN
$ sopa [auth_flags] get --dc <DC> --dn '<DN>'
# Delete an object by DN
$ sopa [auth_flags] delete --dc <DC> --dn '<DN>'
# Edit attribute values
$ sopa [auth_flags] attr add --dc <DC> --dn '<DN>' --attr <ATTR> --value <VALUE>
$ sopa [auth_flags] attr replace --dc <DC> --dn '<DN>' --attr <ATTR> --value <VALUE>
$ sopa [auth_flags] attr delete --dc <DC> --dn '<DN>' --attr <ATTR>
# Create objects
$ sopa [auth_flags] create user --dc <DC> --name <CN> --pass <INITIAL_PASS>
$ sopa [auth_flags] create computer --dc <DC> --name <CN>
$ sopa [auth_flags] create group --dc <DC> --name <CN> --type GlobalSecurity
$ sopa [auth_flags] create ou --dc <DC> --name <CN>
$ sopa [auth_flags] create container --dc <DC> --name <CN>
$ sopa [auth_flags] create custom --dc <DC> --template <TEMPLATE.yaml>
# Set / change account passwords (MS-ADCAP)
$ sopa [auth_flags] set-password --dc <DC> --dn '<DN>' --new <NEW_PASS>
$ sopa [auth_flags] change-password --dc <DC> --dn '<DN>' --old <OLD_PASS> --new <NEW_PASS>
# Translate DN <-> canonical name (MS-ADCAP)
# (this call is mostly useless but kept for completeness 😄)
$ sopa [auth_flags] translate-name --dc <DC> --offered DistinguishedName --desired CanonicalName '<DN>'
# Principal group memberships (MS-ADCAP)
$ sopa [auth_flags] groups --dc <DC> --dn '<DN>' --membership --authz
# Group members (MS-ADCAP)
$ sopa [auth_flags] members --dc <DC> --dn '<GROUP_DN>' --recursive
# Toggle optional AD feature, e.g. Recycle Bin (MS-ADCAP)
$ sopa [auth_flags] optfeature --dc <DC> --feature-id <FEATURE_GUID> --enable
# Topology info (MS-ADCAP)
$ sopa [auth_flags] info version --dc <DC>
$ sopa [auth_flags] info domain --dc <DC>
$ sopa [auth_flags] info forest --dc <DC>
$ sopa [auth_flags] info dcs --dc <DC>
# ADWS service endpoint metadata (unauthenticated - auth flags not needed)
$ sopa mex --dc <DC>
运行 sopa 时不带子命令以打开交互式 shell。它会为所有命令复用单个连接,并提供 Tab 补全。
$ sopa --dc <DC> -u <USER> -p <PASS> -d <DOMAIN>
sopa v1.1.0
Connected dc.corp.local domain=corp.local user=Administrator
Type 'help' for commands or 'exit' to quit.
[corp.local]> query --filter '(objectClass=user)' --attrs sAMAccountName
[corp.local]> get --dn 'CN=Administrator,CN=Users,DC=corp,DC=local'
[corp.local]> exit
使用 exit、quit 或 Ctrl-D 退出 shell。
示例模板:examples/custom-create.example.yaml
模板架构(YAML):
parentDN (字符串,必需):容器 DNrdn (字符串,必需):新对象的相对 DN(例如 CN=Foo)attributes (列表,必需):每个项包含:
name (字符串,必需):属性名称(cn 或 addata:cn)type (字符串,可选):string|int|bool|base64|hex(或显式 xsd:*),默认 stringvalue (字符串) 要么 values (字符串列表)注意:
ad:relativeDistinguishedName 或 ad:container-hierarchy-parent(它们会自动注入)。hex 值会转换为 xsd:base64Binary。value: ""。--dc 接受一个 FQDN、一个 IP 地址,或者可以省略。
由于 DC 的主机名有时无法从网络的默认 DNS 获取,强烈建议始终传递 --dns <DC-IP>,以便 sopa 使用 DC 自己的 DNS 服务器进行所有查询:
# Option 1: let sopa resolve everything through the DC's DNS
$ sopa query --dns 192.168.1.10 -d corp.local -u user -p pass --filter '(objectClass=user)'
当省略 --dc 且提供了 --domain 时,sopa 通过查询 SRV 记录自动发现 DC:
_ldap._tcp.<domain> (tried first)
_kerberos._tcp.<domain> (fallback)
目标为优先级最高的记录。这要求 --dns 指向的 DNS 服务器能够回答这些 SRV 查询——DC 自带的集成 DNS 服务器(如果存在)应该可以做到。
# Option 2: provide DC explicitly without Kerberos
$ sopa info version --dc 192.168.1.10 --domain corp.local -u user -p pass
当为 --dc 提供 IP 时,该 IP 总是通过反向 PTR 查询解析为 FQDN——ADWS 端点的 WCF 地址过滤器要求在 wsa:To 标头中使用 FQDN,无论采用哪种身份验证方法。
此 PTR 查找也经过 --dns,因此 DC 上需要有一个正确配置的反向区域:
# Option 3: IP input - PTR lookup resolves 192.168.1.10 -> dc.corp.local
$ sopa info version --dc 192.168.1.10 --dns 192.168.1.10 --domain corp.local -u user -p pass
栈中的所有 DNS 操作——DC 发现、PTR 解析、ADWS TCP 拨号和 Kerberos KDC 连接——都使用从 --dns / --dns-tcp 构建的同一个解析器。
| 标志 | 描述 |
|---|---|
--dns <主机[:端口]> | 用于所有查询(SRV、PTR、正向)的自定义 DNS 服务器。默认为端口 53。 |
--dns-tcp | 强制 DNS 查询通过 TCP 而不是 UDP。当 UDP 被阻塞或 SRV 响应过大时有用。 |
-x / --socks5 <主机:端口> | 将所有 TCP 连接通过 SOCKS5 代理路由。除非设置了 --no-proxy-dns,否则 DNS 查询也会通过代理隧道传输。 |
--no-proxy-dns | 当 --socks5 激活时,通过本地系统解析器解析 DNS,而不是通过代理。 |
--dns-timeout <持续时间> | DNS 操作超时(默认 10s)。 |
--tcp-timeout <持续时间> | TCP 拨号和 ADWS 协议操作超时(默认 30s)。 |
sopa 支持以下凭据模式:
# Password
$ sopa --dc <DC> -d <DOMAIN> -u <USER> -p <PASS> <subcommand> [...]
# NT hash
$ sopa --dc <DC> -d <DOMAIN> -u <USER> -H <NT_HASH> <subcommand> [...]
# AES session key (Kerberos is implied)
# 32 hex chars = AES-128, 64 hex chars = AES-256
$ sopa --dc <DC> -d <DOMAIN> -u <USER> --aes-key <HEX_KEY> <subcommand> [...]
# Kerberos ccache (Kerberos is implied)
$ sopa --dc <DC> -d <DOMAIN> -u <USER> -c <CCACHE_PATH> <subcommand> [...]
# PFX certificate (Kerberos is implied / via PKINIT)
$ sopa --dc <DC> -d <DOMAIN> -u <USER> --pfx <CERT.pfx> --pfx-password <PFX_PASS> <subcommand> [...]
# PEM certificate (Kerberos is implied / via PKINIT)
$ sopa --dc <DC> -d <DOMAIN> -u <USER> --cert <CERT.pem> --key <KEY.pem> <subcommand> [...]
所有 TCP 连接(ADWS、Kerberos、DNS)都可以通过 -x / --socks5 经由 SOCKS5 代理隧道传输: