[하루한줄] CVE-2026-58222 : Samba AD DC에서 발생하는 LDAP Comapre 필터 Injection 및 접근 제어 우회 취약점
URL
Target
- 4.24.0 ≤ Samba Actvie Directory Domain Controller < 4.24.5
- 4.23.0 ≤ Samba Actvie Directory Domain Controller < 4.23.10
- 4.22.0 ≤ Samba Actvie Directory Domain Controller < 4.22.11
Explain
Overview
CVE-2026-58222는 Samba Active Directory Domain Controller의 LDAP Compare 요청 처리 과정에서 발생하는 LDAP Injection 및 접근 제어 우회 취약점입니다.
취약한 Samba 서버는 LDAP Compare 요청을 처리할 때 클라이언트가 전달한 속성 이름과 비교 값을 검증하지 않은 상태로 LDAP 검색 필터에 삽입합니다. 이 과정에서 공격자는 LDAP 비교 연산자를 조작하여 정상적인 동등 비교(=)를 범위 비교(>=)로 변경할 수 있습니다.
이후 해당 요청을 내부 데이터베이스 검색으로 변환하면서 일반 사용자 요청이라는 사실을 표시하지 않기 때문에 내부 검색 과정에서 기밀 속성에 대한 접근 제어 검사가 정상적으로 적용되지 않을 수 있습니다.
따라서 공격자는 일반 도메인 사용자 권한만으로 LDAP Compare 요청을 반복하여 원래 열람할 수 없는 기밀 속성을 추론할 수 있게 됩니다. 주 공격 대상은 Group Key Distribution Service에서 사용하는 루트 키가 저장되는 msKds-RootKeyData 속성이며, 공격자가 해당 값을 복원할 경우 gMSA(Group Managed Service Account)의 암호를 오프라인에서 크래킹할 수 있습니다.
높은 권한을 보유한 gMSA가 존재한다면 도메인 장악까지의 체인까지 구성할 수 있게 됩니다.
이 취약점은 CVSSv3.1 기준 8.8(High)로 평가되었으며 2026년 7월 28일 보안 업데이트가 공개되었습니다.
Background
- Samba Acive Directory Domain Controller
Samba는 Linux 및 Unix 환경에서 SMB 파일 공유, 인증 및 도메인 서비스를 제공하는 오픈소스 소프트웨어로 SMB 파일 서버뿐만 아니라 Active Directory Domain Controller 기능도 구현되어 있습니다. 이 기능을 통해 Windows Server의 Active Directory와 유사하게 도메인 사용자, 그룹, 컴퓨터 계정, 그룹 정책 등의 정보를 관리할 수 있습니다.
Active Directory의 디렉터리 정보는 LDAP 프로토콜을 통해 조회하거나 수정할 수 있으며, Samba는 내부적으로 LDAP 유사 임베디드 데이터베이스인 LDB(TDB 백엔드)를 사용하여 데이터를 저장합니다. 모든 디렉터리 객체는 nTSecurityDescriptor 속성에 보안 설명자를 가지며, DACL의 ACE는 객체 단위뿐 아니라 개별 속성 단위로도 권한을 정의합니다.
따라서 LDAP 요청을 처리할 때 Samba는 요청자의 보안 토큰(사용자 및 그룹 SID)과 대상 객체의 DACL을 대조하여 해당 객체와 속성에 대한 읽기/쓰기 허용 여부를 판단해야 합니다. 인증된 저권한 도메인 사용자가 ACL이 허용하지 않은 객체나 속성을 읽거나 수정할 수 있다면 이는 보안 경계 위반이 됩니다.
- LDAP Compare Operation
LDAP 프로토콜에는 디렉터리 정보를 조회하는 Search 요청 외에도 특정 객체의 속성이 지정한 값과 일치하는지 확인하는 Compare 요청이 존재합니다.
LDAP Compare 요청의 구조
CompareRequest ::= [APPLICATION 14] SEQUENCE {
entry LDAPDN, // 비교할 LDAP 객체의 DN (Distinguished Name)
ava AttributeValueAssertion
}
AttributeValueAssertion ::= SEQUENCE {
attributeDesc AttributeDescription, // 비교 대상 속성 이름
assertionValue AssertionValue // 비교에 사용할 값
}
정상적인 Compare 요청에서는 결과가 다음과 같습니다.
compareTrue (6): 속성 값 중 비교 값과 일치하는 것이 존재compareFalse (5): 일치하는 값 없음
LDAP Compare 요청이 속성 값을 직접 반환하지 않더라도 비교 결과를 통해 해당 값에 대한 오라클을 제공하기 때문에 기밀 속성에 대한 Compare 요청 역시 일반적인 LDAP Search 요청과 마찬가지로 접근 제어 검사가 필요합니다. (값 공간이 작거나 예측 가능한 속성이라면 반복 질의만으로 실제 값을 확정)
- LDAP Filter
LDAP Search 요청은 필터를 사용하여 검색 대상 객체를 지정합니다. 대표적인 필터 연산자는 표 내용과 같습니다.
| 연산자 | 예시 | 의미 |
|---|---|---|
= |
(uid=alice) |
일치 |
>= |
(uid>=alice) |
크거나 같음 |
<= |
(uid<=alice) |
작거나 같음 |
=* |
(uid=*) |
속성 존재 여부 |
LDAP Search 필터와 LDAP Compare 요청은 서로 다른 연산입니다. LDAP Compare의 attributeDesc는 기본적으로 속성 이름을 나타내야 하며, 검색 필터의 비교 연산자를 포함해서는 안 됩니다.
그러나 애플리케이션이 Compare 요청의 속성 이름과 비교 값을 문자열로 연결하여 검색 필터를 구성할 경우 LDAP Injection이 발생할 수 있습니다.
- gMSA / KDS Root Key
gMSA(Group Managed Service Account)는 서비스에서 사용할 수 있도록 Active Directory가 암호를 자동으로 관리하는 계정입니다.
일반적인 서비스 계정은 관리자가 암호를 생성하고 교체해야 하지만, gMSA는 도메인에서 암호를 자동으로 생성하고 갱신할 수 있습니다. gMSA 암호 생성에는 Group Key Distribution Service의 키 파생 메커니즘이 사용됩니다.
관련된 주요 속성은 다음과 같습니다.
msKds-RootKeyData: 암호 도출에 사용되는 KDS Root KeymsDS-ManagedPasswordID: gMSA 암호 도출에 필요한 키 식별 정보msDS-ManagedPassword: 관리되는 암호 정보를 포함하는 속성
KDS Root Key와 필요한 파생 매개변수를 확보하면 해당 키에 연관된 gMSA 암호를 계산할 수 있기 때문에 msKds-RootKeyData는 일반 도메인 사용자가 임의로 읽을 수 없어야 하는 민감 정보입니다.
Root Cause
취약점은 두 가지 문제점이 결합되어 발생하였습니다.
- LDAP Compare 요청의 속성 이름과 비교 값을 검증하지 않아 LDAP 검색 필터를 조작할 수 있었음
- 내부 검색 요청이 신뢰된 요청으로 처리되어 기밀 속성에 대한 접근 제어 검사가 우회됨
따라서 공격자는 접근이 제한된 속성을 비교 결과를 통해 복원할 수 있게 됩니다.
- LDAP Filter Injection
취약한 Samba의 LDAP Compare 요청 처리 코드는 클라이언트가 전달한 속성 이름과 비교 값을 이용하여 내부 LDB 검색 필터를 생성합니다.
ldap_server/ldap_backend.c:146x
static NTSTATUS ldapsrv_CompareRequest(struct ldapsrv_call *call)
{
...
// 두 값을 문자열로 결합하여 검색 필터 생성
filter = talloc_asprintf(
local_ctx,
"(%s=%*s)",
req->attribute, // 클라이언트가 전달한 속성 이름
(int)req->value.length,
req->value.data); // 클라이언트가 전달한 비교 값
...
ldb_ret = ldb_search(samdb, local_ctx, &res,
dn, LDB_SCOPE_BASE, attrs, "%s", filter);
위 코드에서 개발자는 내부 필터가 항상 (attribute=value) 형태가 될 것으로 가정했기 때문에 req->attribute에 올바른 LDAP 속성 이름만 들어오는지 확인하는 검증 과정이 존재하지 않았습니다. 또한 req->value.data도 LDAP 필터 문자열에 맞게 이스케이프되지 않았습니다.
정상적인 요청에서는 다음과 같이 필터가 생성됩니다.
req->attribute = "uid"
req->value.data = "alice"
결과: (uid=alice)
하지만 속성 이름에 > 문자가 포함될 경우 결과가 달라집니다.
req->attribute = "uid>"
req->value.data = "alice"
결과: (uid>=alice)
속성 이름 뒤에 붙은 >로 인해 내부 검색 필터의 연산자가 >=로 변경되어 Compare 요청이 크기 비교 연산을 수행할 수 있게 됩니다.
이 과정으로 인해 공격자는 서버에 저장된 실제 값을 알지 못하더라도 특정 값보다 크거나 작은지 반복적으로 확인할 수 있는 primitive를 확보하게 됩니다.
- Trusted Search Context
LDAP Injection만으로는 기밀 속성 값을 알아낼 수 없습니다. 정상적인 LDAP 서버라면 필터에 등장하는 속성에 대해 요청자의 읽기 권한을 먼저 검사하고, 권한이 없는 속성은 필터 평가에서 제외해야 하기 때문입니다.
하지만 취약점이 존재하는 버전에서는 Compare 처리 경로에서 접근 제어 검사가 존재하지 않았습니다.
Samba AD DC에서는 LDB 모듈 스택의 acl_read 모듈이 LDAP 검색에 대한 읽기 접근 제어를 담당합니다. 이 모듈은 요청에 설정된 untrusted 표시를 확인하여 외부 사용자 요청과 내부의 신뢰된 요청을 구분합니다.
dsdb/samdb/ldb_modules/acl_read.c
static int aclread_search(struct ldb_module *module, struct ldb_request *req)
{
bool is_untrusted = ldb_req_is_untrusted(req);
...
/* skip access checks if we are system or system control is supplied
* or this is not LDAP server request */
if (!p || !p->enabled || am_system || !is_untrusted) {
return ldb_next_request(module, req);
}
위 코드에서 ldb_req_is_untrusted()는 요청이 신뢰되지 않은 외부 요청으로 표시되었는지 확인합니다. is_untrusted가 false인 경우에는 읽기 접근 제어 검사 경로를 건너뛰고 ldb_next_request()를 통해 다음 모듈로 요청을 전달합니다.
일반적인 LDAP Search 요청은 내부 검색 요청을 생성할 때 ldb_req_mark_untrusted()를 통해 해당 표시를 설정합니다. 따라서 acl_read 모듈에서 요청자의 권한에 따른 접근 제어 검사가 수행됩니다.
취약한 ldapsrv_CompareRequest()는 LDAP Compare 요청을 BASE 스코프의 내부 검색으로 변환하면서 ldb_search()를 직접 호출하였기 때문에 문제가 되었습니다.
ldap_server/ldap_backend.c
filter = talloc_asprintf(
local_ctx,
"(%s=%*s)",
req->attribute,
(int)req->value.length,
req->value.data
);
...
ldb_ret = ldb_search(
samdb,
local_ctx,
&res,
dn,
LDB_SCOPE_BASE,
attrs,
"%s",
filter
);
ldb_search()를 호출하는 과정에서 요청에 untrusted 표시가 설정되지 않았기 때문에 외부 사용자가 보낸 LDAP Compare 요청임에도 acl_read 모듈에서는 is_untrusted가 false로 평가되어 읽기 접근 제어 검사 경로를 건너뛰게 됩니다.
요청 처리 흐름 비교
ldapsrv_SearchRequest()
-> untrusted 표시 설정
-> acl_read: is_untrusted == true
-> 사용자 권한에 따른 읽기 접근 제어 검사
-> 검색 수행
ldapsrv_CompareRequest()
-> ldb_search() 호출
-> untrusted 표시 누락
-> acl_read: is_untrusted == false
-> 읽기 접근 제어 검사 생략
-> 기밀 속성에 대한 필터 평가
-> compareTrue / compareFalse 반환
결과적으로 일반 도메인 사용자의 LDAP Compare 요청이 내부의 신뢰된 검색 요청으로 취급되면서, 원래 읽기 권한이 없는 msKds-RootKeyData 등의 기밀 속성도 검색 필터 평가에 사용될 수 있었습니다.
- Boolean Oracle을 이용한 정보 복원
1번과 2번의 문제를 결합하면 공격자는 LDAP Compare를 이용한 Boolean Oracle을 구성할 수 있습니다.
stored_value >= candidate ?
위 필터식처럼 공격자는 candidate 값을 변경하면서 비교 결과를 관찰할 수 있습니다.
0x70값을 알아내는 예시
candidate = 0x40 -> True
candidate = 0x80 -> False
candidate = 0x60 -> True
candidate = 0x70 -> True
candidate = 0x78 -> False
- gMSA 암호 도출
공격자가 msKds-RootKeyData를 복원하면 KDS Root Key를 확보하게 됩니다. 이후 MS-GKDI의 키 파생 과정을 구현하여 gMSA 암호를 계산할 수 있습니다.
KDS Root Key 하나로 도메인의 모든 계정 암호를 얻는 것은 아니며, 공격 대상 gMSA와 연결된 키 및 관련 파생 정보를 확보해야 합니다. 따라서 도메인 장악까지 이어지는지 여부는 해당 gMSA가 가진 권한에 따라 달라집니다.
PoC
공개된 PoC를 사용하여 취약점을 악용하여 msKds-RootKeyData를 복원하는 과정을 실습해볼 수 있습니다.
# PoC clone
$ git clone https://github.com/abdelaaziz0/KDSExtract.git
$ cd KDSExtract
# 환경 설정
$ python -m venv .venv
$ source .venv/bin/activate
$ pip install .
$ cd lab
# 실행
$ ./run-demo.sh
위 환경에서는 취약한 Samba 4.24.4와 Samba 4.24.5가 각각 구성되며 KDS Root Key와 일반 사용자 lowpriv가 생성되어 ldb에 적재됩니다.
kdsextract 도구를 사용하기 전, kdsextract/ldap.py에서 OID_STARTTLS 값을 1.3.6.1.4.1.1466.20037로 변경해줘야 TLS 관련 에러를 피할 수 있습니다.

먼저 모의침투에서 사용되는 ldapsearch 도구를 통해 lovpriv 사용자가 msKds-RootKeyData 속성에 접근할 수 있는지 확인해보겠습니다.
$ LDAPTLS_REQCERT=never ldapsearch -LLL -x -ZZ \
-H ldap://127.0.0.1:1389 \
-D 'CN=lowpriv,CN=Users,DC=lab,DC=local' \
-w 'Lowpriv123!' \
-b 'CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,DC=lab,DC=local' \
-s sub \
'(objectClass=*)' \
msKds-RootKeyData
결과를 확인하면 객체에 대한 열거(DN)는 가능하지만 기밀 속성에 해당하는 msKds-RootKeyData을 읽을 권한이 없어 값을 확인할 수 없음을 알 수 있습니다.

확인된 Root Key 객체의 GUID는 각각 아래와 같습니다.
54a0d969-a657-da93-e6a9-0ee896864158bc6c2334-daac-ab16-ffc4-5a091cf52c72
해당 GUID에 대해 kdsextract 도구를 사용하면 이전과는 달리 취약점을 악용하여 성공적으로 msKds-RootKeyData 64바이트 값을 얻어낼 수 있게 됩니다.
$ kdsextract recover \
--host 127.0.0.1 \
--port 1389 \
--user lowpriv@lab.local \
--key <GUID>

Patch
커밋 07786681889b972b2b78ccb854f23443e57cb87c에서 문제가 되었던 코드들에 대한 검증 로직들을 추가하여 패치되었습니다.

Reference
- https://nvd.nist.gov/vuln/detail/cve-2026-58222
- https://github.com/abdelaaziz0/KDSExtract
- https://learn.microsoft.com/ko-kr/windows-server/identity/ad-ds/manage/group-managed-service-accounts/group-managed-service-accounts/manage-group-managed-service-accounts?tabs=aduc
- https://rzip.samba.org/samba/security/CVE-2026-58222.html
- https://www.rfc-editor.org/info/rfc4511/#section-4.10
- https://www.rfc-editor.org/info/rfc4515/
- https://bugzilla.samba.org/show_bug.cgi?id=16148
- https://github.com/samba-team/samba/commit/07786681889b972b2b78ccb854f23443e57cb87c
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.