
Golang으로 작성된 ADWS 실용 클라이언트.
Golang으로 작성된 ADWS 실용 클라이언트입니다.
Sopa는 ADWS 프로토콜 스택(MS-NNS + MC-NMF + SOAP)을 구현하며, 다음 명령줄 기능을 제공합니다:
query: LDAP 필터 검색을 WS-Enumeration Enumerate + Pull 루프를 통해 실행하며, 속성 프로젝션, 범위 제어(Base/OneLevel/Subtree), 페이지네이션 지원get: DN으로 단일 객체를 WS-Transfer Get을 통해 가져옴create: WS-Transfer ResourceFactory를 통해 객체 생성 (내장 유형: user, computer, group, OU, container; 또는 IMDA AddRequest를 통한 YAML 템플릿 기반 사용자 정의 객체)delete: DN으로 객체를 WS-Transfer Delete를 통해 삭제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를 실행하면 인터랙티브 셸이 열립니다. 모든 명령에 대해 단일 연결을 재사용하며 탭 완성을 지원합니다.
$ 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를 사용하여 셸을 종료합니다.
예제 템플릿: 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> (먼저 시도)
_kerberos._tcp.<domain> (대체)
가장 높은 우선순위의 레코드 대상이 사용됩니다. 이를 위해서는 --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 <host[:port]> | 모든 조회(SRV, PTR, 정방향)에 대한 사용자 정의 DNS 서버. 기본 포트는 53입니다. |
--dns-tcp | UDP 대신 TCP를 통해 DNS 쿼리를 강제 실행합니다. UDP가 차단되거나 SRV 응답이 큰 경우 유용합니다. |
-x / --socks5 <host:port> | 모든 TCP 연결을 SOCKS5 프록시를 통해 라우팅합니다. --no-proxy-dns가 설정되지 않은 경우 DNS 쿼리도 프록시를 통해 터널링됩니다. |
--no-proxy-dns | --socks5가 활성화된 경우, 프록시 대신 로컬 시스템 리졸버를 통해 DNS를 확인합니다. |
--dns-timeout <duration> | DNS 작업의 제한 시간 (기본값 10초). |
--tcp-timeout <duration> | TCP 연결 및 ADWS 프로토콜 작업의 제한 시간 (기본값 30초). |
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> [...]