- 网络
- 网络安全
【免费下载链接】scapy
Scapy: the Python-based interactive packet manipulation program & library.
本篇技术指南以 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 1777:LDAP v2;
- RFC 4511:LDAP v3(Bind、Search、Modify、Add、Delete、ModifyDN、Abandon、Extended 等完整操作);
- RFC 2254:LDAP 过滤字符串语法(
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.1(BER 编码)包,其顶层结构由messageID(ASN1_INTEGER)、protocolOp(ASN1F_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, dport=389)、dport=3268(全局编录)以及 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[b'domainFunctionality']> | | \values \ | | |###[ LDAP_AttributeValue ]### | | | value = <ASN1_STRING[b'7']> | |###[ LDAP_PartialAttribute ]### | | type = <ASN1_STRING[b'forestFunctionality']> | | \values \ | | |###[ LDAP_AttributeValue ]### | | | value = <ASN1_STRING[b'7']> | |###[ LDAP_PartialAttribute ]### | | type = <ASN1_STRING[b'domainControllerFunctionality']> | | \values \ | | |###[ LDAP_AttributeValue ]### | | | value = <ASN1_STRING[b'7']> [...]可见sr1()发送的LDAP_SearchRequest拿到了服务器的 root DSE(根 DSA 信息)应答,结果被结构化为LDAP_SearchResponseEntry,每个属性为LDAP_PartialAttribute,属性值包装在LDAP_AttributeValue中。这就是 LDAP_Client 交互时的典型报文视图,非常适合协议教学与调试。
若客户端输出过于啰嗦,可在实例化时传入
verb=False关闭连接/收发过程的打印(LDAP_Client(verb=False),对应源码__init__(self, verb=True)的默认参数)。
连接服务器:普通连接与 LDAPS
默认端口 389 的连接
client = LDAP_Client() client.connect("192.168.0.100")connect()的完整签名(scapy/layers/ldap.py#L1836)为:
def connect(self, host, port=None, use_ssl=False, sslcontext=None, sni=None, no_check_certificate=False, timeout=5):host:目标 IP 或主机名;port:默认 389(非 SSL)或 636(SSL),可用port属性覆盖;use_ssl:是否启用 LDAPS;sslcontext:自定义ssl.SSLContext,用于严格校验证书;sni:LDAPS 时的 SNI,缺省使用 host;no_check_certificate:SSL 下跳过证书检查;timeout:连接超时,默认 5 秒。
底层实现会创建 TCP socket(开启SO_KEEPALIVE),按StreamSocket(sock, LDAP)封装为 Scapy 流式套接字。
使用 TLS(LDAPS)连接
client = LDAP_Client() client.connect("192.168.0.100", use_ssl=True)使用 TLS 时默认端口变为 636。重要安全提示:默认情况下此模式不会校验服务器证书,因为目标服务器证书很可能是自签名的。要实现真正安全的 TLS,应传入sslcontext:
import 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_ssl=True, sspcontext=sslcontext)(注意:文档示例中该参数名为sspcontext,而源码connect()中实际参数名为sslcontext,按当前仓库源码为准传sslcontext即可;no_check_certificate=True也可用于测试环境跳过校验。)
从源码看,SSL 分支还会额外执行两步:一是用GssChannelBindings.fromssl(ChannelBindingType.TLS_SERVER_END_POINT, ...)计算 TLS 通道绑定令牌(CBT),供后续 GSSAPI/SASL 认证使用;二是将 socket 封装为SSLStreamSocket(sock, LDAP)。
绑定:五种认证机制详解
绑定必须显式指定机制类型,其枚举LDAP_BIND_MECHS(scapy/layers/ldap.py#L1661)定义如下:
| 枚举成员 | 字符串值 | 含义与安全属性 |
|---|---|---|
NONE | "ANONYMOUS" | 匿名绑定,不携带任何凭据 |
SIMPLE | "SIMPLE" | 简单绑定机制,凭据明文传输,务必配合 LDAPS 使用 |
SICILY | "SICILY" | Windows 特有机制([MS-ADTS]),仅支持 NTLM |
SASL_GSSAPI | "GSSAPI" | SASL 认证机制(RFC 4422),通常配合 Kerberos |
SASL_GSS_SPNEGO | "GSS-SPNEGO" | SPNEGO 认证机制([MS-SPNG]),NTLM / Kerberos 皆可,Windows 场景常用 |
SASL_EXTERNAL | "EXTERNAL" | SASL EXTERNAL(基于外部凭据,如客户端证书) |
SASL_DIGEST_MD5 | "DIGEST-MD5" | SASL 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绝大多数情况下只在 Windows(Active Directory)环境中可用;其它标准服务器通常只支持SIMPLE与SASL_GSSAPI。
NONE(匿名)
client.bind(LDAP_BIND_MECHS.NONE)SIMPLE(简单绑定)
client.bind( LDAP_BIND_MECHS.SIMPLE, simple_username="Administrator", simple_password="Password1!", )底层发送LDAP_BindRequest:bind_name为用户名,authentication使用LDAP_Authentication_simple(上下文标签0x80)携带明文密码;若返回的resultCode != 0(非 success),抛出LDAP_Exception。
SICILY — NTLM
ssp = NTLMSSP(UPN="Administrator", PASSWORD="Password1!") client.bind( LDAP_BIND_MECHS.SICILY, ssp=ssp, )SICILY 是 [MS-ADTS] 定义的 Windows 专有绑定,源码实现为三步握手(scapy/layers/ldap.py#L2090-L2149):先是Package Discovery(LDAP_Authentication_sicilyPackageDiscovery),再是Negotiate(LDAP_Authentication_sicilyNegotiate携带 NTLMSSP NEGOTIATE 令牌),最后是Response(LDAP_Authentication_sicilyResponse携带 NTLM AUTHENTICATE 令牌)。中间还会用target_name="ldap/" + self.host和通道绑定调用GSS_Init_sec_context。
SASL_GSSAPI — Kerberos
ssp = KerberosSSP(UPN="Administrator@domain.local", PASSWORD="Password1!", SPN="ldap/dc1.domain.local") client.bind( LDAP_BIND_MECHS.SASL_GSSAPI, ssp=ssp, )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 / Kerberos
ssp = SPNEGOSSP([ NTLMSSP(UPN="Administrator", PASSWORD="Password1!"), KerberosSSP(UPN="Administrator@domain.local", PASSWORD="Password1!", SPN="ldap/dc1.domain.local"), ]) client.bind( LDAP_BIND_MECHS.SASL_GSS_SPNEGO, ssp=ssp, )SPNEGO 允许将多个 SSP(NTLM + Kerberos)打包在一个SPNEGOSSP中,由双方协商最终选用的机制;在 Windows 上还支持通过 WinSSP 使用隐式凭据。值得注意的是,若最终协商结果为 NTLMSSP,源码会将self.sign强制置为 False(scapy/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 为
SPNEGOSSP(Windows 下可为WinSSP); - NONE / 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)。
GSSAPI(Kerberos)模式下,若请求签名而服务器不支持 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 过滤字符串与 scope:
resp = client.search( "CN=Users,DC=domain,DC=local", "(objectCategory=person)", ["objectClass", "name", "description", "canonicalName"], scope=1, # children ) resp.show()search()的完整签名与默认值(scapy/layers/ldap.py#L2286-L2297):
def search(self, baseObject="", filter="", scope=0, derefAliases=0, sizeLimit=300000, timeLimit=3000, attrsOnly=0, attributes=[], controls=[]):scope 取值范围:
| 值 | 名称 | 语义 |
|---|---|---|
| 0 | baseObject | 只查询 base DN 自身的属性 |
| 1 | singleLevel | 查询 base DN 的直接子条目 |
| 2 | wholeSubtree | 查询 base DN 下整个子树 |
其它参数的语义与默认行为:
derefAliases:别名解引用方式(0=neverDerefAliases、1=derefInSearching、2=derefFindingBaseObj、3=derefAlways),默认 0;sizeLimit/timeLimit:服务端返回条数上限与处理时间上限,默认分别 300000 / 3000(毫秒);attrsOnly:是否只取属性名不取值,默认 False;baseObject传"rootDSE"时会自动转为空 DN"",即查询根 DSA;filter为空时默认使用(objectClass=*)(LDAP_FilterPresent);- 绑定成功后,会自动追加分页控件(OID
1.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_username="cn=admin,dc=scapy,dc=net", simple_password="Bonjour1") res = cli.search("dc=scapy,dc=net", "(&(givenName=Another)(sn=Test))", scope=2) cli.close() # res == {'uid=another,ou=People,dc=scapy,dc=net': { # '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); - 子串过滤:
attribute=initial*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( filter=LDAP_Filter( filter=LDAP_FilterEqual( attributeType=ASN1_STRING(b'objectCategory'), attributeValue=ASN1_STRING(b'person') ) ), attributes=[ LDAP_SearchRequestAttribute(type=ASN1_STRING(b'objectClass')), LDAP_SearchRequestAttribute(type=ASN1_STRING(b'name')), LDAP_SearchRequestAttribute(type=ASN1_STRING(b'description')), LDAP_SearchRequestAttribute(type=ASN1_STRING(b'canonicalName')) ], baseObject=ASN1_STRING(b'CN=Users,DC=domain,DC=local'), scope=ASN1_ENUMERATED(1), derefAliases=ASN1_ENUMERATED(0), sizeLimit=ASN1_INTEGER(1000), timeLimit=ASN1_INTEGER(60), attrsOnly=ASN1_BOOLEAN(0) ) )LDAP_SearchRequest的字段布局(scapy/layers/ldap.py#L748-L771)为:baseObject、scope(ENUMERATED,0/1/2)、derefAliases、sizeLimit、timeLimit、attrsOnly、filter(LDAP_Filter)、attributes(SEQUENCE OF LDAP_SearchRequestAttribute)。这也是直接在报文物化层上定制搜索的唯一正确入口。
修改属性:modify / add / modifydn
modify()用于更改对象的属性,典型场景是替换displayName并新增servicePrincipalName:
client.modify( "CN=User1,CN=Users,DC=domain,DC=local", changes=[ LDAP_ModifyRequestChange( operation="replace", modification=LDAP_PartialAttribute( type="displayName", values=[ LDAP_AttributeValue(value="Lord User the 1st") ] ) ), LDAP_ModifyRequestChange( operation="add", modification=LDAP_PartialAttribute( type="servicePrincipalName", values=[ LDAP_AttributeValue(value="http/lorduser") ] ) ) ] )LDAP_ModifyRequestChange.operation是 ASN1_ENUMERATED,取值见 scapy/layers/ldap.py#L822-L835:0=add、1=delete、2=replace,直接传字符串名即可。modify()内部发送LDAP_ModifyRequest(object+changes序列),若响应不是LDAP_ModifyResponse或resultCode != 0则抛LDAP_Exception。
LDAP_Client还提供另外两个写操作:
add(entry, attributes, controls=[]):新增条目,attributes支持两种格式——LDAP_Attribute(或LDAP_PartialAttribute)列表,或{属性名: [值列表]}字典(源码会自动转换为LDAP_Attribute);modifydn(entry, newdn, deleteoldrdn=True, 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(mech=LDAP_BIND_MECHS.SICILY, ... ssp=NTLMSSP(UPN="Administrator@domain.local", PASSWORD="test"))SSP 的用法与前面bind()章节完全一致。还可以传入连接参数,例如指定目标主机:
>>> LDAPHero(host="192.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-tk(Debian 系apt install python3-tk)。
深入:协议细节与周边工具
报文物化层与操作码
ASN1_Class_LDAP(scapy/layers/ldap.py#L195-L218)定义了全部 LDAP 操作的应用标签:BindRequest0x60、BindResponse0x61、SearchRequest0x63、SearchResultEntry0x64、SearchResultDone0x65、ModifyRequest0x66、ModifyResponse0x67、AddRequest0x68、DelRequest0x4A、ModifyDNRequest0x6C、CompareRequest0x6E、AbandonRequest0x50、ExtendedResponse0x78等,这些标签正是 BER 编码中识别操作类型的依据。LDAPResult元组则把 resultCode 数值映射为语义(0=success、49=invalidCredentials、53=unwillingToPerform、32=noSuchObject 等),并包含matchedDN、diagnosticMessage及可选 referral 列表。
LDAP 控件(Controls)
LDAP_Control由controlType(OID)、可选的criticality与controlValue组成。源码维护了一张_LDAP_CONTROLS注册表(scapy/layers/ldap.py#L975-L989),可按 OID 自动把 controlValue 解包为专用结构,目前注册了两个:
1.2.840.113556.1.4.319:LDAP_realSearchControlValue(RFC 2696 分页结果控件,size+cookie),search()的分页功能即依赖它;1.2.840.113556.1.4.801:LDAP_serverSDFlagsControl([MS-ADTS],OWNER/GROUP/DACL/SACL标志),LDAPHero 读取安全描述符时使用。
任何未注册 OID 的控件值都会以原始 ASN1_STRING 形式保留,不影响报文往返。
其它相关能力
- CLDAP 与 DC 定位:UDP 389 上的 CLDAP(RFC 1798)同样实现了,并内置
LdapPing_am应答机(ldappingd)以及dclocator命令(按 [MS-ADTS] 6.3.6 执行域控定位,支持nocheck/connect/ldap三种探测模式,源码 scapy/layers/ldap.py#L1479-L1653); - Windows 兼容性:
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 与本文所述的用法都是直接可用的起点。
- 网络
- 网络安全
【免费下载链接】scapy
Scapy: the Python-based interactive packet manipulation program & library.
相关推荐
Scapy SCTP 协议层实战指南:报文构造、Chunk 解析与 Linux 内核扩展能力启用
Scapy SCTP 协议层实战指南:报文构造、Chunk 解析与 Linux 内核扩展能力启用 SCTP(Stream Control Transmissio
网络网络安全终极指南:如何在Blazor应用中快速集成GraphQL实现高效数据绑定
终极指南:如何在Blazor应用中快速集成GraphQL实现高效数据绑定 Blazor是一个基于.NET和WebAssembly的现代Web框架,让开发者可以使
前端Web框架CANN Runtime Context 查询实战:从创建、绑定到默认 Stream 与线程资源限制查询
CANN Runtime Context 查询实战:从创建、绑定到默认 Stream 与线程资源限制查询 本篇基于 CANN runtime 仓库中的样例 0_
CANNAscend人工智能性能剖析系统编程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考