
Go로 ADWS(net.tcp/9389)를 통한 Active Directory 정찰 및 읽기 전용 보안 감사
adit는 단일 바이너리 Active Directory 정찰 및 읽기 전용 보안 감사 도구로, LDAP 대신 ADWS(Active Directory Web Services, TCP/9389) 를 통해 도메인 컨트롤러와 통신합니다.
adit은 광산의 수평 갱도 — 조용한 진입로입니다. LDAP/389 대신 ADWS를 통해 AD를 쿼리하면 트래픽이 WCF net.tcp 프레이밍된 바이너리-XML SOAP으로 흔치 않은 포트를 사용하므로, 기존 LDAP보다 검사·필터링·시그니처 탐지될 가능성이 훨씬 낮으면서도 동일한 디렉터리 데이터를 반환합니다. adit은 전체 ADWS 프로토콜 스택을 Go로 구현했으며 외부 런타임 의존성이 없습니다.
┌────────────┐ net.tcp/9389 ┌──────────────┐
│ adit │ ───────────────▶ │ ADWS on DC │ ──▶ local LDAP
│ (Linux) │ NMF · NNS · │ (port 9389) │
│ │ NBFSE · SOAP └──────────────┘
└────────────┘
adit은 방어/감사 및 승인된 평가 도구입니다. 평가 권한이 있는 디렉터리에만 실행하십시오.
| 기능 | 세부 사항 |
|---|---|
| 전송 | WCF net.tcp(TCP/9389)를 통한 ADWS — MC-NMF 프레이밍, MS-NNS 핸드셰이크, MC-NBFS/NBFSE 바이너리 XML, WS-Enumeration/WS-Transfer SOAP |
| 인증 | NTLMv2(sign + seal) 및 Kerberos(ccache / keytab / password; RFC 4121 sealed GSS) |
| 정찰 수집기 | 31개 모듈(아래 참조) → 모듈별 CSV |
| 보고 | 스타일이 적용된 다중 시트 XLSX 워크북 + 자체 포함 HTML 대시보드(검색/정렬, 심각도 배지, 스크롤 스파이) |
| 보안 감사 | 27개 읽기 전용 규칙(ADCS ESC1–4/9 포함) → 심각도 순 JSON, 대시보드에도 표시됨 |
| 프록시 / 점프 호스트 | HTTP CONNECT, SOCKS5/5h, SOCKS4/4a(프록시 인증 + 원격 DNS 지원) |
| 페이징 | 서버 측 WS-Enumeration Pull 페이징, 페이지 크기 구성 가능 |
| 오프라인 보고 | 이전에 수집한 CSV로부터 XLSX/HTML 재생성, DC 불필요 |
| 의존성 | 순수 Go; LDAP 라이브러리 없음, .NET 없음, 외부 서비스 없음 |
forest, domain, trusts, sites, subnets, schemahistory, dcs,
users, userspns, groups, computers, computerspns, ous, gpos,
gplinks, passwordpolicy, fgpp, dnszones, dnsrecords, kerberoastable,
asreproastable, krbtgt, adcs, cas, laps, protectedgroups,
groupmembers, gmsa, dmsa, printers, bitlocker
ADCS(adcs)는 ESC1–ESC4/ESC9를 표시합니다. laps는 각 비밀번호를 읽을 수 있는 주체를 보고합니다. protectedgroups는 Protected Users를 감사합니다. 현재 목록은 adit -list-modules를 실행하십시오.
-audit는 27개의 읽기 전용 규칙을 실행합니다. 발견 사항은 규칙 ID, 심각도, 영향받는 객체, 증거, 그리고 교정 방안을 포함하며 Adit-Audit.json에 기록됩니다(그리고 -collect와 함께 사용하면 대시보드 최상단에 "Audit Findings" 섹션이 표시됩니다). 엔진은 정찰 수집기가 읽을 수 있음에도 불구하고 비밀 속성 값(LAPS / gMSA / BitLocker)을 절대 읽지 않습니다.
| 규칙 | 심각도 | 표시 대상 |
|---|---|---|
| AD-DOM-010 | Critical | 비-Tier-0 주체가 보유한 DCSync(복제) 권한 |
| AD-ACL-001/005/006/007/011–016 | Critical | 권한 있는 객체에 대한 위험한 제어(GenericAll/Write, WriteDACL/Owner, ForceChangePassword, Write SPN / KeyCredentialLink / member / AllowedToAct) |
| AD-SDH-001/002 | Critical | AdminSDHolder에 대한 예상치 못한 제어 / 소유자 |
| AD-CS-001 | Critical–Medium | ESC1 — 인증서 템플릿이 등록자 제공 주체를 허용 |
| AD-CS-002 | Critical–Medium | ESC2 — Any-Purpose / EKU 없는 인증서 템플릿 |
| AD-CS-003 | High–Medium | ESC3 — Certificate Request Agent 템플릿 |
| AD-CS-004 | High | ESC4 — 인증서 템플릿에 대한 취약한 접근 제어 |
| AD-CS-009 | High | ESC9 — 인증서 템플릿에 보안 확장이 없음 |
| AD-CS-005 | Critical/High | ESC5 — PKI 객체(NTAuthCertificates, CA 저장소, CA 객체)에 대한 위험한 제어 |
| AD-CS-006 | Info | AD CS 존재 — ESC6/ESC7/ESC8은 호스트 수준 검증 필요(ADWS로 관찰 불가) |
| AD-DOM-008 | High | 도메인 전체에서 되돌릴 수 있는 비밀번호 암호화 활성화 |
| AD-USR-001 | High | Kerberos 사전 인증 비활성화(AS-REP roastable) |
| AD-USR-009 | High | 계정이 DES 암호화만 사용 |
| AD-CMP-006 | High | 비-DC에서의 비제약 위임 |
| AD-KCL-001 | High | 권한 있는 계정의 Key Credential(shadow credential) |
| AD-KRB-001 | High | krbtgt 비밀번호가 오래됨 |
| AD-DOM-005 | Medium | Machine Account Quota > 0(모든 사용자가 컴퓨터를 조인 가능) |
| AD-USR-002 | Medium | Kerberoastable 서비스 계정(권한 있는 경우 High) |
| AD-USR-005 | Medium | PASSWD_NOTREQD 설정됨 |
| AD-USR-006 | Medium | 권한 있는 계정 비밀번호가 만료되지 않음 |
| AD-USR-011 | Medium | 권한 있는 계정 비밀번호가 오래됨 |
| AD-CMP-008 | Medium | 리소스 기반 제약 위임 구성됨 |
| AD-CMP-012 | Medium | 레거시 / 지원되지 않는 운영 체제 |
| AD-GRP-009 | Medium | 권한 있는 그룹의 외부 보안 주체 |
| AD-GRP-020 | Medium | primaryGroupID를 통한 숨겨진 권한 있는 멤버십 |
| AD-TRU-001/003 | High/Medium | SID 필터링 없는 트러스트 / SID 기록이 있는 트러스트 |
| AD-DNS-003/004 | Medium/Low | WPAD / ISATAP 레코드 존재 |
| AD-CMP-011 | Low | 오래된 컴퓨터 계정 |
| AD-INT-005 | High | sIDHistory에 권한 있는 SID 포함 |
임계값은 조정 가능합니다: -krbtgt-max-age, -priv-pwd-max-age, -stale-days.
ADCS 참고: ESC1–ESC5 및 ESC9는 ADWS를 통해 AD 객체로부터 평가됩니다. ESC6(
EDITF_ATTRIBUTESUBJECTALTNAME2), ESC7(CA 역할 ACL), ESC8(웹 등록 / NTLM 릴레이)은 CA 호스트 레지스트리/HTTP 엔드포인트에 존재하며 ADWS로 관찰할 수 없습니다 —AD-CS-006은 CA가 존재함을 표시하여 호스트에서 검증할 수 있게 합니다(예: Certipy 사용).
Go 1.26+ 가 필요합니다. 다른 빌드 의존성은 없습니다.
# Build the binary
go build -o adit ./cmd/adit
./adit -version
# …or run without installing
go run ./cmd/adit -h
# Cross-compile release archives for linux/windows/darwin (amd64+arm64) into dist/
bash scripts/build-release.sh
-fqdn은 DC의 실제 FQDN이어야 합니다(IP가 아님). ADWS SOAP To 헤더가 WCF의 AddressFilter에 의해 엔드포인트와 일치되기 때문입니다. 예를 들어 nslookup -type=SRV _ldap._tcp.dc._msdcs.<domain> <dc-ip>로 찾을 수 있습니다.
adit -dc <host> -fqdn <dc-fqdn> -d <DOMAIN> -u <user> -p <pass> [flags]
# RootDSE handshake only (verifies auth + the full protocol stack)
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass'
# Run every collector -> ./report/CSV-Files/*.csv + Adit-Report.{xlsx,html}
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' \
-collect all -o ./report
# A subset of collectors
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' \
-collect users,groups,computers,kerberoastable
# Audit only -> Adit-Audit.json
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' -audit
# Recon + audit: findings lead the XLSX + HTML dashboard
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' \
-collect all -audit -o ./report
# Password (synthesises a krb5.conf from the domain + DC if none is present)
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' -auth kerberos
# Existing ticket cache
KRB5CCNAME=/tmp/krb5cc_1000 adit -dc 10.0.0.10 -fqdn dc01.example.local \
-d EXAMPLE.LOCAL -u user -auth kerberos -ccache /tmp/krb5cc_1000
ADWS는 웹 서비스이지만 HTTP는 아닙니다 — 9389에서 원시 TCP를 사용합니다 — 그럼에도 프록시는 그 TCP 스트림을 터널링할 수 있습니다. 프로토콜이 허용하는 경우(SOCKS5/4a, HTTP CONNECT) DC 이름은 프록시에서 해석되므로 내부 전용 이름도 작동합니다.
# SSH dynamic forward = a local SOCKS5 proxy
ssh -D 1080 -N jump-host &
adit -dc dc01.corp.internal -fqdn dc01.corp.internal -d CORP.INTERNAL \
-u user -p 'pass' -proxy socks5://127.0.0.1:1080 -collect all -audit -o ./report
# Other proxy forms
-proxy socks5://user:[email protected]:1080 # SOCKS5 with username/password auth
-proxy socks4a://10.0.0.5:1080 # SOCKS4a (remote DNS)
-proxy http://user:[email protected]:8080 # HTTP CONNECT (Basic auth)
-proxy https://proxy.corp:8443 # HTTP CONNECT over TLS to the proxy
-auth kerberos + 비밀번호를 사용하면 KDC 교환은 여전히 직접 이루어집니다. 점프 호스트를 통하는 경우 먼저 티켓을 획득하고 -ccache / KRB5CCNAME을 전달하십시오.
# Raw LDAP-filter query over ADWS, selecting attributes (paged automatically)
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' \
-query '(&(objectClass=user)(objectCategory=person))' \
-attrs sAMAccountName,userAccountControl,objectSid
# Rebuild the workbook / dashboard from existing CSVs (no DC needed)
adit -generate-excel-from ./report/CSV-Files -o report.xlsx
adit -generate-html-from ./report/CSV-Files -o report.html
전체 플래그 목록은 adit -h를 실행하십시오.