ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Scapy 中的 LDAP 协议实现与 LDAP_Client 实战指南:从 LDAPv2/v3 报文构造到认证绑定与查询

2026/9/23 13:52:25 拓冰建站 浏览量
Scapy 中的 LDAP 协议实现与 LDAP_Client 实战指南:从 LDAPv2/v3 报文构造到认证绑定与查询 网络网络安全【免费下载链接】scapyScapy: the Python-based interactive packet manipulation program library.项目地址https://gitcode.com/gh_mirrors/sc/scapy点击查看免费下载本篇技术指南以 doc/scapy/layers/ldap.rst 为骨架深入讲解 Scapy 对 LDAPv2/LDAPv3 消息的完整实现以及内置LDAP_Client类的连接、绑定NONE / SIMPLE / SICILY / SASL_GSSAPI / SASL_GSS_SPNEGO、签名加密、搜索与属性修改全流程并辅以 scapy/layers/ldap.py 源码与 test/scapy/layers/ldap.uts 测试用例佐证实现细节。读完本文你将能够使用 Scapy 以纯 Python 方式对 LDAP 服务器包括 Windows Active Directory进行抓包级报文构造、协议调试与自动化操作并会使用图形化封装 LDAPHero。概述Scapy 的 LDAP 支持范围Scapy 在 scapy/layers/ldap.py 中实现了 LDAP 协议栈依据的标准包括RFC 1777LDAP v2RFC 4511LDAP v3Bind、Search、Modify、Add、Delete、ModifyDN、Abandon、Extended 等完整操作RFC 2254LDAP 过滤字符串语法LDAP_Filter.from_rfc2254_string[MS-ADTS]微软 Active Directory 技术规范中的扩展包括 Sicily 绑定NTLM、分页搜索结果控件、安全描述符相关控件等。除报文层的完整实现外还附带一个功能够用但基础的LDAP_Client类以及基于它的图形化 LDAP 浏览器 LDAPHero。从源码头部注释可以看到一个实用提示要模拟微软 Windows LDAP 报文的编码风格需设置conf.ASN1_default_long_size 4测试 test/scapy/layers/ldap.uts 中的 Microsoft LDAP tests 一节正是用这一开关验证了与 Windows 报文逐字节一致的构建与重新解包。LDAP主类本身是一个 ASN.1BER 编码包其顶层结构由messageIDASN1_INTEGER、protocolOpASN1F_CHOICE覆盖 BindRequest / SearchRequest / ModifyRequest / AddRequest / DelRequest / ModifyDNRequest / AbandonRequest / ExtendedResponse 等全部操作以及可选的 LDAP v3Controls序列构成见 scapy/layers/ldap.py#L1042-L1071。它同时提供了tcp_reassemble重组逻辑用于在 TCP 流上正确拼接被分片的 LDAP 响应以及answers()按 messageID 匹配请求/响应的机制。在传输层绑定上源码通过bind_bottom_up(TCP, LDAP, dport389)、dport3268全局编录以及 UDP 389 上的 CLDAP 完成自动解包挂接因此用rdpcap或sniff抓到的 LDAP 流量可以直接被识别为LDAP/CLDAP层。LDAP_Client 使用总览LDAP_Client定义于 scapy/layers/ldap.py#L1762的使用思路非常直接遵循连接 → 绑定 → 操作三步走实例化LDAP_Client()调用connect(ip)——在这里指定是否使用 SSL调用bind(...)——在这里指定认证机制SSP以及是否需要签名/加密调用search(...)查询数据调用modify(...)编辑属性此外还提供add()、modifydn()、close()等方法。最简单的无认证演示摘自原文档并保留其真实输出格式 client LDAP_Client() client.connect(192.168.0.100) client.bind(LDAP_BIND_MECHS.NONE) client.sr1(LDAP_SearchRequest()).show() ┃ Connecting to 192.168.0.100 on port 389... └ Connected from (192.168.0.102, 40228) NONE bind succeeded ! LDAP_SearchRequest LDAP_SearchResponseEntry ###[ LDAP ]### messageID 0x1 ASN1_INTEGER[1] \protocolOp\ |###[ LDAP_SearchResponseEntry ]### | objectName ASN1_STRING[b] | \attributes\ | |###[ LDAP_PartialAttribute ]### | | type ASN1_STRING[bdomainFunctionality] | | \values \ | | |###[ LDAP_AttributeValue ]### | | | value ASN1_STRING[b7] | |###[ LDAP_PartialAttribute ]### | | type ASN1_STRING[bforestFunctionality] | | \values \ | | |###[ LDAP_AttributeValue ]### | | | value ASN1_STRING[b7] | |###[ LDAP_PartialAttribute ]### | | type ASN1_STRING[bdomainControllerFunctionality] | | \values \ | | |###[ LDAP_AttributeValue ]### | | | value ASN1_STRING[b7] [...]可见sr1()发送的LDAP_SearchRequest拿到了服务器的 root DSE根 DSA 信息应答结果被结构化为LDAP_SearchResponseEntry每个属性为LDAP_PartialAttribute属性值包装在LDAP_AttributeValue中。这就是 LDAP_Client 交互时的典型报文视图非常适合协议教学与调试。若客户端输出过于啰嗦可在实例化时传入verbFalse关闭连接/收发过程的打印LDAP_Client(verbFalse)对应源码__init__(self, verbTrue)的默认参数。连接服务器普通连接与 LDAPS默认端口 389 的连接client LDAP_Client() client.connect(192.168.0.100)connect()的完整签名scapy/layers/ldap.py#L1836为def connect(self, host, portNone, use_sslFalse, sslcontextNone, sniNone, no_check_certificateFalse, timeout5):host目标 IP 或主机名port默认 389非 SSL或 636SSL可用port属性覆盖use_ssl是否启用 LDAPSsslcontext自定义ssl.SSLContext用于严格校验证书sniLDAPS 时的 SNI缺省使用 hostno_check_certificateSSL 下跳过证书检查timeout连接超时默认 5 秒。底层实现会创建 TCP socket开启SO_KEEPALIVE按StreamSocket(sock, LDAP)封装为 Scapy 流式套接字。使用 TLSLDAPS连接client LDAP_Client() client.connect(192.168.0.100, use_sslTrue)使用 TLS 时默认端口变为 636。重要安全提示默认情况下此模式不会校验服务器证书因为目标服务器证书很可能是自签名的。要实现真正安全的 TLS应传入sslcontextimport ssl client LDAP_Client() sslcontext ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) sslcontext.load_verify_locations(path/to/ca.crt) client.connect(192.168.0.100, use_sslTrue, sspcontextsslcontext)注意文档示例中该参数名为sspcontext而源码connect()中实际参数名为sslcontext按当前仓库源码为准传sslcontext即可no_check_certificateTrue也可用于测试环境跳过校验。从源码看SSL 分支还会额外执行两步一是用GssChannelBindings.fromssl(ChannelBindingType.TLS_SERVER_END_POINT, ...)计算 TLS 通道绑定令牌CBT供后续 GSSAPI/SASL 认证使用二是将 socket 封装为SSLStreamSocket(sock, LDAP)。绑定五种认证机制详解绑定必须显式指定机制类型其枚举LDAP_BIND_MECHSscapy/layers/ldap.py#L1661定义如下枚举成员字符串值含义与安全属性NONEANONYMOUS匿名绑定不携带任何凭据SIMPLESIMPLE简单绑定机制凭据明文传输务必配合 LDAPS 使用SICILYSICILYWindows 特有机制[MS-ADTS]仅支持 NTLMSASL_GSSAPIGSSAPISASL 认证机制RFC 4422通常配合 KerberosSASL_GSS_SPNEGOGSS-SPNEGOSPNEGO 认证机制[MS-SPNG]NTLM / Kerberos 皆可Windows 场景常用SASL_EXTERNALEXTERNALSASL EXTERNAL基于外部凭据如客户端证书SASL_DIGEST_MD5DIGEST-MD5SASL DIGEST-MD5需要说明SASL_EXTERNAL与SASL_DIGEST_MD5已出现在枚举中但bind()的校验分支scapy/layers/ldap.py#L2023-L2061目前仅实现了 NONE、SIMPLE、SICILY、SASL_GSSAPI、SASL_GSS_SPNEGO其余机制会抛出Mech ... is still unimplemented !。可用性取决于服务器不同服务器支持的机制不同SICILY与SASL_GSS_SPNEGO绝大多数情况下只在 WindowsActive Directory环境中可用其它标准服务器通常只支持SIMPLE与SASL_GSSAPI。NONE匿名client.bind(LDAP_BIND_MECHS.NONE)SIMPLE简单绑定client.bind( LDAP_BIND_MECHS.SIMPLE, simple_usernameAdministrator, simple_passwordPassword1!, )底层发送LDAP_BindRequestbind_name为用户名authentication使用LDAP_Authentication_simple上下文标签0x80携带明文密码若返回的resultCode ! 0非 success抛出LDAP_Exception。SICILY — NTLMssp NTLMSSP(UPNAdministrator, PASSWORDPassword1!) client.bind( LDAP_BIND_MECHS.SICILY, sspssp, )SICILY 是 [MS-ADTS] 定义的 Windows 专有绑定源码实现为三步握手scapy/layers/ldap.py#L2090-L2149先是Package DiscoveryLDAP_Authentication_sicilyPackageDiscovery再是NegotiateLDAP_Authentication_sicilyNegotiate携带 NTLMSSP NEGOTIATE 令牌最后是ResponseLDAP_Authentication_sicilyResponse携带 NTLM AUTHENTICATE 令牌。中间还会用target_nameldap/ self.host和通道绑定调用GSS_Init_sec_context。SASL_GSSAPI — Kerberosssp KerberosSSP(UPNAdministratordomain.local, PASSWORDPassword1!, SPNldap/dc1.domain.local) client.bind( LDAP_BIND_MECHS.SASL_GSSAPI, sspssp, )SASL_GSSAPI 分支要求 SSP 必须是KerberosSSP实例否则抛ValueError。它的握手比 SICILY 更复杂除标准的 SASL 凭据交换外还需按 RFC 2222 第 7.2.1/7.2.2 节进行安全层协商——解析服务器返回的LDAP_SASL_GSSAPI_SsfCap包含supported_security_layers的 INTEGRITY / CONFIDENTIALITY 标志与max_output_token_size再回发客户端支持的安全层标志scapy/layers/ldap.py#L2207-L2264。SASL_GSS_SPNEGO — NTLM / Kerberosssp SPNEGOSSP([ NTLMSSP(UPNAdministrator, PASSWORDPassword1!), KerberosSSP(UPNAdministratordomain.local, PASSWORDPassword1!, SPNldap/dc1.domain.local), ]) client.bind( LDAP_BIND_MECHS.SASL_GSS_SPNEGO, sspssp, )SPNEGO 允许将多个 SSPNTLM Kerberos打包在一个SPNEGOSSP中由双方协商最终选用的机制在 Windows 上还支持通过 WinSSP 使用隐式凭据。值得注意的是若最终协商结果为 NTLMSSP源码会将self.sign强制置为 Falsescapy/layers/ldap.py#L2267-L2271因为 Windows 的 NTLM-over-LDAP 不支持只签名不加密。bind() 的默认行为与校验bind()有两个值得注意的默认值scapy/layers/ldap.py#L2010-L2014未显式指定encrypt时SICILY 默认加密未显式指定sign且未加密时除 NONE / SIMPLE 外默认签名。同时它做了严格的组合校验SICILY 要求 SSP 为NTLMSSP且不允许只签名不加密SASL_GSS_SPNEGO 要求 SSP 为SPNEGOSSPWindows 下可为WinSSPNONE / SIMPLE 不允许携带 SSP也不允许使用sign/encrypt。绑定成功后若启用了签名或加密套接字会被切换为StreamSocket(sock, LDAP_SASL_Buffer)后续所有 LDAP 报文都经 GSS_Wrap / GSS_Unwrap 保护scapy/layers/ldap.py#L2273-L2277。签名 / 加密SASL 层保护在不使用 LDAPS的前提下可以通过bind()的sign与encrypt参数为 LDAP 流量启用签名或加密这两者借助 GSSAPI 的 INTEGRITY / CONFIDENTIALITY 标志实现。使用时必须注意几个限制NONE 与 SIMPLE 模式不能使用这两个标志在 SICILY 或 SASL_GSS_SPNEGO 中使用NTLMSSP时无法只开sign而不开encrypt——因为 Windows 没有实现该组合SICILY 分支会直接抛出ValueError。GSSAPIKerberos模式下若请求签名而服务器不支持 INTEGRITY或请求加密而服务器不支持 CONFIDENTIALITY绑定会在安全层协商阶段抛出RuntimeError。关于响应保护test/scapy/layers/ldap.uts 中的 Protected LDAP responses retain the requested message ID 一节验证了签名/加密响应解包后messageID 必须与请求一致否则抛ValueError(LDAP response message ID does not match request !)。查询数据sr1 与 search绑定完成后即可发起请求。最简单的用法是直接发送报文并展示结果client.sr1(LDAP_SearchRequest()).show()使用 search() 高阶接口更常用的是search()它接受 base DN、RFC 2254 过滤字符串与 scoperesp client.search( CNUsers,DCdomain,DClocal, (objectCategoryperson), [objectClass, name, description, canonicalName], scope1, # children ) resp.show()search()的完整签名与默认值scapy/layers/ldap.py#L2286-L2297def search(self, baseObject, filter, scope0, derefAliases0, sizeLimit300000, timeLimit3000, attrsOnly0, attributes[], controls[]):scope 取值范围值名称语义0baseObject只查询 base DN 自身的属性1singleLevel查询 base DN 的直接子条目2wholeSubtree查询 base DN 下整个子树其它参数的语义与默认行为derefAliases别名解引用方式0neverDerefAliases、1derefInSearching、2derefFindingBaseObj、3derefAlways默认 0sizeLimit/timeLimit服务端返回条数上限与处理时间上限默认分别 300000 / 3000毫秒attrsOnly是否只取属性名不取值默认 FalsebaseObject传rootDSE时会自动转为空 DN即查询根 DSAfilter为空时默认使用(objectClass*)LDAP_FilterPresent绑定成功后会自动追加分页控件OID1.2.840.113556.1.4.319每页 100 条通过 cookie 循环取完所有结果最终返回{dn: {attribute: [values]}}形式的字典。search()的返回值是字典键为条目的 DN值为属性名到值列表的映射。OpenLDAP 集成测试 test/scapy/layers/ldapopenldap.uts 演示了完整流程与返回结构cli LDAP_Client() cli.connect(127.0.0.1) cli.bind(LDAP_BIND_MECHS.SIMPLE, simple_usernamecnadmin,dcscapy,dcnet, simple_passwordBonjour1) res cli.search(dcscapy,dcnet, ((givenNameAnother)(snTest)), scope2) cli.close() # res {uidanother,ouPeople,dcscapy,dcnet: { # objectClass: [top, person, inetOrgPerson], # cn: [Another Test], uid: [another], sn: [Test], # givenName: [Another], userPassword: [testing]}}RFC 2254 过滤字符串解析search()内部通过LDAP_Filter.from_rfc2254_string(filter)scapy/layers/ldap.py#L603-L740把字符串过滤条件解析为对应的 ASN.1 过滤器树。该解析器支持组合操作符LDAP_FilterAnd、|LDAP_FilterOr、!LDAP_FilterNot比较操作符EqualityMatch、GreaterOrEqual、LessOrEqual、~ApproxMatch子串过滤attributeinitial*any*final对应 LDAP_SubstringFilter 的 initial / any / final 三段存在性过滤attribute*LDAP_FilterPresent。已知限制文档明确警告当前 RFC 2254 解析器不支持 Extensible Match扩展匹配attr:rule:value遇到:会抛NotImplementedError(Extensible not implemented.)但过滤器结构类LDAP_FilterExtensibleMatch本身已在源码中定义可手工构造报文使用。等价的底层报文构造为了透彻理解search()做了什么原文档给出了完全等价的手工构造写法——search()的上述调用等价于以下sr1()调用resp client.sr1( LDAP_SearchRequest( filterLDAP_Filter( filterLDAP_FilterEqual( attributeTypeASN1_STRING(bobjectCategory), attributeValueASN1_STRING(bperson) ) ), attributes[ LDAP_SearchRequestAttribute(typeASN1_STRING(bobjectClass)), LDAP_SearchRequestAttribute(typeASN1_STRING(bname)), LDAP_SearchRequestAttribute(typeASN1_STRING(bdescription)), LDAP_SearchRequestAttribute(typeASN1_STRING(bcanonicalName)) ], baseObjectASN1_STRING(bCNUsers,DCdomain,DClocal), scopeASN1_ENUMERATED(1), derefAliasesASN1_ENUMERATED(0), sizeLimitASN1_INTEGER(1000), timeLimitASN1_INTEGER(60), attrsOnlyASN1_BOOLEAN(0) ) )LDAP_SearchRequest的字段布局scapy/layers/ldap.py#L748-L771为baseObject、scopeENUMERATED0/1/2、derefAliases、sizeLimit、timeLimit、attrsOnly、filterLDAP_Filter、attributesSEQUENCE OF LDAP_SearchRequestAttribute。这也是直接在报文物化层上定制搜索的唯一正确入口。修改属性modify / add / modifydnmodify()用于更改对象的属性典型场景是替换displayName并新增servicePrincipalNameclient.modify( CNUser1,CNUsers,DCdomain,DClocal, changes[ LDAP_ModifyRequestChange( operationreplace, modificationLDAP_PartialAttribute( typedisplayName, values[ LDAP_AttributeValue(valueLord User the 1st) ] ) ), LDAP_ModifyRequestChange( operationadd, modificationLDAP_PartialAttribute( typeservicePrincipalName, values[ LDAP_AttributeValue(valuehttp/lorduser) ] ) ) ] )LDAP_ModifyRequestChange.operation是 ASN1_ENUMERATED取值见 scapy/layers/ldap.py#L822-L8350add、1delete、2replace直接传字符串名即可。modify()内部发送LDAP_ModifyRequestobjectchanges序列若响应不是LDAP_ModifyResponse或resultCode ! 0则抛LDAP_Exception。LDAP_Client还提供另外两个写操作add(entry, attributes, controls[])新增条目attributes支持两种格式——LDAP_Attribute或LDAP_PartialAttribute列表或{属性名: [值列表]}字典源码会自动转换为LDAP_Attributemodifydn(entry, newdn, deleteoldrdnTrue, controls[])重命名/移动条目自动从新旧 DN 推导 relative DN 与 newSuperior若父级未变则省略 newSuperior 字段遵循 RFC 4511 第 4.9 节。所有写操作失败时都会抛出带resultCode与diagnosticMessage属性的LDAP_Exception当诊断消息形如8009030C: LdapErr: ... data 52e ...Windows 域控错误时异常还会自动用 scapy/layers/windows/erref.py 中的STATUS_ERREF表把十六进制错误码翻译成可读文本。LDAPHero图形化 LDAP 浏览器LDAPHero法语昵称 LDAPéro源码见 scapy/modules/ldaphero.py是基于LDAP_Client的跨平台图形化封装tkinter GUI适用于希望可视化浏览目录树的场景。启动方式 load_module(ticketer) LDAPHero()也可以在构造时传入 SSP点击界面上的 Bind 按钮时就会使用它认证例如 SICILY / NTLM LDAPHero(mechLDAP_BIND_MECHS.SICILY, ... sspNTLMSSP(UPNAdministratordomain.local, PASSWORDtest))SSP 的用法与前面bind()章节完全一致。还可以传入连接参数例如指定目标主机 LDAPHero(host192.168.0.100)从 scapy/modules/ldaphero.py#L153-L219 看LDAPHero的构造参数还包括mech默认SASL_GSS_SPNEGO、sign默认 True、encrypt默认 False、port、ssl以及一组认证参数UPN、password、kerberos_required、HashNt、HashAes256Sha96、HashAes128Sha96、use_krb5ccname、use_winssp当传了host且未提供 SSP 时会自动用这些参数构造SPNEGOSSP。GUI 提供目录树浏览双击展开子树、搜索、新增子条目、修改属性、修改 DN、查看并编辑安全描述符通过 LDAP 控件1.2.840.113556.1.4.801读取nTSecurityDescriptor并借助 DCE/RPCdrsuapi接口解析 SID等能力需要python3-tkDebian 系apt install python3-tk。深入协议细节与周边工具报文物化层与操作码ASN1_Class_LDAPscapy/layers/ldap.py#L195-L218定义了全部 LDAP 操作的应用标签BindRequest0x60、BindResponse0x61、SearchRequest0x63、SearchResultEntry0x64、SearchResultDone0x65、ModifyRequest0x66、ModifyResponse0x67、AddRequest0x68、DelRequest0x4A、ModifyDNRequest0x6C、CompareRequest0x6E、AbandonRequest0x50、ExtendedResponse0x78等这些标签正是 BER 编码中识别操作类型的依据。LDAPResult元组则把 resultCode 数值映射为语义0success、49invalidCredentials、53unwillingToPerform、32noSuchObject 等并包含matchedDN、diagnosticMessage及可选 referral 列表。LDAP 控件ControlsLDAP_Control由controlTypeOID、可选的criticality与controlValue组成。源码维护了一张_LDAP_CONTROLS注册表scapy/layers/ldap.py#L975-L989可按 OID 自动把 controlValue 解包为专用结构目前注册了两个1.2.840.113556.1.4.319LDAP_realSearchControlValueRFC 2696 分页结果控件sizecookiesearch()的分页功能即依赖它1.2.840.113556.1.4.801LDAP_serverSDFlagsControl[MS-ADTS]OWNER/GROUP/DACL/SACL标志LDAPHero 读取安全描述符时使用。任何未注册 OID 的控件值都会以原始 ASN1_STRING 形式保留不影响报文往返。其它相关能力CLDAP 与 DC 定位UDP 389 上的 CLDAPRFC 1798同样实现了并内置LdapPing_am应答机ldappingd以及dclocator命令按 [MS-ADTS] 6.3.6 执行域控定位支持nocheck/connect/ldap三种探测模式源码 scapy/layers/ldap.py#L1479-L1653Windows 兼容性LDAP_ExtendedResponse.do_dissect专门兼容了 Windows 构造的长度字段不含可选字段的畸形包scapy/layers/ldap.py#L959-L970对微软报文整体使用conf.ASN1_default_long_size 4可还原其定长编码风格测试test/scapy/layers/ldap.uts 覆盖了 BindRequest / BindResponse / SearchRequest / SearchResponse / UnbindRequest 的字节级解包-重建、TCP 会话重组、CLDAP、Microsoft LDAP 编码、SASL 保护响应等场景可作为理解协议细节的补充阅读。小结Scapy 的 LDAP 支持覆盖了从报文层到客户端再到图形界面三个层次底层是完整的 LDAPv2/v3 CLDAP 的 ASN.1/BER 报文实现与自动 TCP/UDP 端口解包中间层是LDAP_Client提供 connect / bind匿名、简单、NTLM Sicily、Kerberos GSSAPI、SPNEGO/ search含分页与 RFC 2254 过滤解析/ modify / add / modifydn / close 全套操作并支持 SASL 签名加密与 LDAPS上层则是面向交互式渗透测试与域环境管理的 LDAPHero 图形浏览器。无论是抓包调试 LDAP 协议、编写针对 Active Directory 的自动化脚本还是验证自建 OpenLDAP 服务scapy/layers/ldap.py 与本文所述的用法都是直接可用的起点。赞分享网络网络安全【免费下载链接】scapyScapy: the Python-based interactive packet manipulation program library.项目地址https://gitcode.com/gh_mirrors/sc/scapy点击查看免费下载相关推荐Scapy SCTP 协议层实战指南报文构造、Chunk 解析与 Linux 内核扩展能力启用Scapy SCTP 协议层实战指南报文构造、Chunk 解析与 Linux 内核扩展能力启用 SCTPStream Control Transmissio网络网络安全终极指南如何在Blazor应用中快速集成GraphQL实现高效数据绑定终极指南如何在Blazor应用中快速集成GraphQL实现高效数据绑定 Blazor是一个基于.NET和WebAssembly的现代Web框架让开发者可以使前端Web框架CANN Runtime Context 查询实战从创建、绑定到默认 Stream 与线程资源限制查询CANN Runtime Context 查询实战从创建、绑定到默认 Stream 与线程资源限制查询 本篇基于 CANN runtime 仓库中的样例 0_CANNAscend人工智能性能剖析系统编程上一篇全国人口及GDP空间数据资源助力多领域精准分析下一篇探秘UDP组播调试利器C编写的高效调试工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考