Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8e08cbf

Browse files
authored
LDAP: add modify/add/delete (#4580)
1 parent206f1be commit8e08cbf

File tree

3 files changed

+186
-30
lines changed

3 files changed

+186
-30
lines changed

‎doc/scapy/layers/ldap.rst‎

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ LDAP
33

44
Scapy fully implements the LDAPv2 / LDAPv3 messages, in addition to a very basic:class:`~scapy.layers.ldap.LDAP_Client` class.
55

6-
..warning::
7-
Scapy's LDAP client is currently read-only. PRs are welcome !
8-
96

107
LDAP client usage
118
-----------------
@@ -16,6 +13,7 @@ The general idea when using the :class:`~scapy.layers.ldap.LDAP_Client` class co
1613
- calling:func:`~scapy.layers.ldap.LDAP_Client.connect` with the IP (this is where to specify whether to use SSL or not)
1714
- calling:func:`~scapy.layers.ldap.LDAP_Client.bind` (this is where to specify a SSP if authentication is desired)
1815
- calling:func:`~scapy.layers.ldap.LDAP_Client.search` to search data.
16+
- calling:func:`~scapy.layers.ldap.LDAP_Client.modify` to edit data attributes.
1917

2018
The simplest, unauthenticated demo of the client would be something like:
2119

@@ -36,20 +34,20 @@ The simplest, unauthenticated demo of the client would be something like:
3634
|###[ LDAP_SearchResponseEntry ]###
3735
| objectName= <ASN1_STRING[b'']>
3836
| \attributes\
39-
| |###[LDAP_SearchResponseEntryAttribute ]###
37+
| |###[LDAP_PartialAttribute ]###
4038
| | type = <ASN1_STRING[b'domainFunctionality']>
4139
| | \values \
42-
| | |###[LDAP_SearchResponseEntryAttributeValue ]###
40+
| | |###[LDAP_AttributeValue ]###
4341
| | | value = <ASN1_STRING[b'7']>
44-
| |###[LDAP_SearchResponseEntryAttribute ]###
42+
| |###[LDAP_PartialAttribute ]###
4543
| | type = <ASN1_STRING[b'forestFunctionality']>
4644
| | \values \
47-
| | |###[LDAP_SearchResponseEntryAttributeValue ]###
45+
| | |###[LDAP_AttributeValue ]###
4846
| | | value = <ASN1_STRING[b'7']>
49-
| |###[LDAP_SearchResponseEntryAttribute ]###
47+
| |###[LDAP_PartialAttribute ]###
5048
| | type = <ASN1_STRING[b'domainControllerFunctionality']>
5149
| | \values \
52-
| | |###[LDAP_SearchResponseEntryAttributeValue ]###
50+
| | |###[LDAP_AttributeValue ]###
5351
| | | value = <ASN1_STRING[b'7']>
5452
[...]
5553
@@ -222,3 +220,35 @@ To understand exactly what's going on, note that the previous call is exactly id
222220
223221
..warning::
224222
Our RFC2254 parser currently does not support 'Extensible Match'.
223+
224+
Modifying attributes
225+
~~~~~~~~~~~~~~~~~~~~
226+
227+
It's also possible to change some attributes on an object.
228+
The following issues a ``Modify Request`` that replaces the ``displayName`` attribute and adds a ``servicePrincipalName``:
229+
230+
..code::python
231+
232+
client.modify(
233+
"CN=User1,CN=Users,DC=domain,DC=local",
234+
changes=[
235+
LDAP_ModifyRequestChange(
236+
operation="replace",
237+
modification=LDAP_PartialAttribute(
238+
type="displayName",
239+
values=[
240+
LDAP_AttributeValue(value="Lord User the 1st")
241+
]
242+
)
243+
),
244+
LDAP_ModifyRequestChange(
245+
operation="add",
246+
modification=LDAP_PartialAttribute(
247+
type="servicePrincipalName",
248+
values=[
249+
LDAP_AttributeValue(value="http/lorduser")
250+
]
251+
)
252+
)
253+
]
254+
)

‎scapy/layers/ldap.py‎

Lines changed: 146 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ class ASN1_Class_LDAP(ASN1_Class):
208208

209209

210210
# Bind operation
211-
# https://datatracker.ietf.org/doc/html/rfc1777#section-4.1
211+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.2
212212

213213

214214
classASN1_Class_LDAP_Authentication(ASN1_Class):
@@ -397,7 +397,7 @@ def serverSaslCredsData(self):
397397

398398

399399
# Unbind operation
400-
# https://datatracker.ietf.org/doc/html/rfc1777#section-4.2
400+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.3
401401

402402

403403
classLDAP_UnbindRequest(ASN1_Packet):
@@ -409,7 +409,7 @@ class LDAP_UnbindRequest(ASN1_Packet):
409409

410410

411411
# Search operation
412-
# https://datatracker.ietf.org/doc/html/rfc1777#section-4.3
412+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.5
413413

414414

415415
classLDAP_SubstringFilterInitial(ASN1_Packet):
@@ -759,16 +759,16 @@ class LDAP_SearchRequest(ASN1_Packet):
759759
)
760760

761761

762-
classLDAP_SearchResponseEntryAttributeValue(ASN1_Packet):
762+
classLDAP_AttributeValue(ASN1_Packet):
763763
ASN1_codec=ASN1_Codecs.BER
764764
ASN1_root=AttributeValue("value","")
765765

766766

767-
classLDAP_SearchResponseEntryAttribute(ASN1_Packet):
767+
classLDAP_PartialAttribute(ASN1_Packet):
768768
ASN1_codec=ASN1_Codecs.BER
769769
ASN1_root=ASN1F_SEQUENCE(
770770
AttributeType("type",""),
771-
ASN1F_SET_OF("values", [],LDAP_SearchResponseEntryAttributeValue),
771+
ASN1F_SET_OF("values", [],LDAP_AttributeValue),
772772
)
773773

774774

@@ -778,8 +778,8 @@ class LDAP_SearchResponseEntry(ASN1_Packet):
778778
LDAPDN("objectName",""),
779779
ASN1F_SEQUENCE_OF(
780780
"attributes",
781-
LDAP_SearchResponseEntryAttribute(),
782-
LDAP_SearchResponseEntryAttribute,
781+
LDAP_PartialAttribute(),
782+
LDAP_PartialAttribute,
783783
),
784784
implicit_tag=ASN1_Class_LDAP.SearchResultEntry,
785785
)
@@ -793,14 +793,6 @@ class LDAP_SearchResponseResultDone(ASN1_Packet):
793793
)
794794

795795

796-
classLDAP_AbandonRequest(ASN1_Packet):
797-
ASN1_codec=ASN1_Codecs.BER
798-
ASN1_root=ASN1F_SEQUENCE(
799-
ASN1F_INTEGER("messageID",0),
800-
implicit_tag=ASN1_Class_LDAP.AbandonRequest,
801-
)
802-
803-
804796
classLDAP_SearchResponseReference(ASN1_Packet):
805797
ASN1_codec=ASN1_Codecs.BER
806798
ASN1_root=ASN1F_SEQUENCE_OF(
@@ -811,6 +803,106 @@ class LDAP_SearchResponseReference(ASN1_Packet):
811803
)
812804

813805

806+
# Modify Operation
807+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.6
808+
809+
810+
classLDAP_ModifyRequestChange(ASN1_Packet):
811+
ASN1_codec=ASN1_Codecs.BER
812+
ASN1_root=ASN1F_SEQUENCE(
813+
ASN1F_ENUMERATED(
814+
"operation",
815+
0,
816+
{
817+
0:"add",
818+
1:"delete",
819+
2:"replace",
820+
},
821+
),
822+
ASN1F_PACKET("modification",LDAP_PartialAttribute(),LDAP_PartialAttribute),
823+
)
824+
825+
826+
classLDAP_ModifyRequest(ASN1_Packet):
827+
ASN1_codec=ASN1_Codecs.BER
828+
ASN1_root=ASN1F_SEQUENCE(
829+
LDAPDN("object",""),
830+
ASN1F_SEQUENCE_OF("changes", [],LDAP_ModifyRequestChange),
831+
implicit_tag=ASN1_Class_LDAP.ModifyRequest,
832+
)
833+
834+
835+
classLDAP_ModifyResponse(ASN1_Packet):
836+
ASN1_codec=ASN1_Codecs.BER
837+
ASN1_root=ASN1F_SEQUENCE(
838+
*LDAPResult,
839+
implicit_tag=ASN1_Class_LDAP.ModifyResponse,
840+
)
841+
842+
843+
# Add Operation
844+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.7
845+
846+
847+
classLDAP_Attribute(ASN1_Packet):
848+
ASN1_codec=ASN1_Codecs.BER
849+
ASN1_root=LDAP_PartialAttribute.ASN1_root
850+
851+
852+
classLDAP_AddRequest(ASN1_Packet):
853+
ASN1_codec=ASN1_Codecs.BER
854+
ASN1_root=ASN1F_SEQUENCE(
855+
LDAPDN("entry",""),
856+
ASN1F_SEQUENCE_OF(
857+
"attributes",
858+
LDAP_Attribute(),
859+
LDAP_Attribute,
860+
),
861+
implicit_tag=ASN1_Class_LDAP.AddRequest,
862+
)
863+
864+
865+
classLDAP_AddResponse(ASN1_Packet):
866+
ASN1_codec=ASN1_Codecs.BER
867+
ASN1_root=ASN1F_SEQUENCE(
868+
*LDAPResult,
869+
implicit_tag=ASN1_Class_LDAP.AddResponse,
870+
)
871+
872+
873+
# Delete Operation
874+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.8
875+
876+
877+
classLDAP_DelRequest(ASN1_Packet):
878+
ASN1_codec=ASN1_Codecs.BER
879+
ASN1_root=LDAPDN(
880+
"entry",
881+
"",
882+
implicit_tag=ASN1_Class_LDAP.DelRequest,
883+
)
884+
885+
886+
classLDAP_DelResponse(ASN1_Packet):
887+
ASN1_codec=ASN1_Codecs.BER
888+
ASN1_root=ASN1F_SEQUENCE(
889+
*LDAPResult,
890+
implicit_tag=ASN1_Class_LDAP.DelResponse,
891+
)
892+
893+
894+
# Abandon Operation
895+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.11
896+
897+
898+
classLDAP_AbandonRequest(ASN1_Packet):
899+
ASN1_codec=ASN1_Codecs.BER
900+
ASN1_root=ASN1F_SEQUENCE(
901+
ASN1F_INTEGER("messageID",0),
902+
implicit_tag=ASN1_Class_LDAP.AbandonRequest,
903+
)
904+
905+
814906
# LDAP v3
815907

816908
# RFC 4511 sect 4.12 - Extended Operation
@@ -926,6 +1018,12 @@ class LDAP(ASN1_Packet):
9261018
LDAP_SearchResponseResultDone,
9271019
LDAP_AbandonRequest,
9281020
LDAP_SearchResponseReference,
1021+
LDAP_ModifyRequest,
1022+
LDAP_ModifyResponse,
1023+
LDAP_AddRequest,
1024+
LDAP_AddResponse,
1025+
LDAP_DelRequest,
1026+
LDAP_DelResponse,
9291027
LDAP_UnbindRequest,
9301028
LDAP_ExtendedResponse,
9311029
),
@@ -966,8 +1064,8 @@ def tcp_reassemble(cls, data, *args, **kwargs):
9661064
pkt=cls(data)
9671065
# Packet can be a whole response yet still miss some content.
9681066
if (
969-
LDAP_SearchResponseEntryinpktand
970-
LDAP_SearchResponseResultDonenotinpkt
1067+
LDAP_SearchResponseEntryinpkt
1068+
andLDAP_SearchResponseResultDonenotinpkt
9711069
):
9721070
returnNone
9731071
returnpkt
@@ -1242,9 +1340,9 @@ def make_reply(self, req):
12421340
/CLDAP(
12431341
protocolOp=LDAP_SearchResponseEntry(
12441342
attributes=[
1245-
LDAP_SearchResponseEntryAttribute(
1343+
LDAP_PartialAttribute(
12461344
values=[
1247-
LDAP_SearchResponseEntryAttributeValue(
1345+
LDAP_AttributeValue(
12481346
value=ASN1_STRING(
12491347
val=bytes(
12501348
NETLOGON_SAM_LOGON_RESPONSE_EX(
@@ -2146,6 +2244,34 @@ def _ssafe(x):
21462244
break
21472245
returnentries
21482246

2247+
defmodify(
2248+
self,
2249+
object:str,
2250+
changes:List[LDAP_ModifyRequestChange],
2251+
controls:List[LDAP_Control]= [],
2252+
)->None:
2253+
"""
2254+
Perform a LDAP modify request.
2255+
2256+
:returns:
2257+
"""
2258+
resp=self.sr1(
2259+
LDAP_ModifyRequest(
2260+
object=object,
2261+
changes=changes,
2262+
),
2263+
controls=controls,
2264+
timeout=3,
2265+
)
2266+
if (
2267+
LDAP_ModifyResponsenotinresp.protocolOp
2268+
orresp.protocolOp.resultCode!=0
2269+
):
2270+
raiseLDAP_Exception(
2271+
"LDAP modify failed !",
2272+
resp=resp,
2273+
)
2274+
21492275
defclose(self):
21502276
ifself.verb:
21512277
print("X Connection closed\n")

‎test/scapy/layers/ldap.uts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ assert raw(pkt[CLDAP]) == b'0k\x02\x01\x01cf\x04\x00\n\x01\x00\n\x01\x00\x02\x01
113113

114114
pkt = Ether(b'RT\x00y\xb1FRT\x00\xbc\xe0=\x08\x00E\x00\x00\xb3\x00\x00@\x00@\x11\xc4T\xc0\xa8z\x03\xc0\xa8z\x91\x01\x85\xf1!\x00\x9fv\x960\x81\x86\x02\x01\x01d\x81\x80\x04\x000|0z\x04\x08netlogon1n\x04l\x17\x00\x00\x00\xbd\x11\x00\x00t\x97x\x1f\x05;\xd7B\x8b\xb2\x8c\xf3\xd9z\x7fj\x02s4\x05howto\x08abartlet\x03net\x00\xc0\x18\x04obed\xc0\x18\x08S4-HOWTO\x00\x04OBED\x00\x00\x17Default-First-Site-Name\x00\xc0I\x05\x00\x00\x00\xff\xff\xff\xff0\x0c\x02\x01\x01e\x07\n\x01\x00\x04\x00\x04\x00')
115115
assert pkt.getlayer(CLDAP, 2)
116-
assert isinstance(pkt.protocolOp[0].attributes[0].values[0],LDAP_SearchResponseEntryAttributeValue)
116+
assert isinstance(pkt.protocolOp[0].attributes[0].values[0],LDAP_AttributeValue)
117117
assert pkt.getlayer(CLDAP, 2).protocolOp.resultCode == 0x0
118118

119119
pkt2 = Ether(raw(pkt))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp