@@ -65,22 +65,6 @@ f.addfield(None, b'', f.default) == hex_bytes('0123456789abcdef0123456789abcdef'
6565
6666+ DCE/RPC v5
6767
68- = Require a verifier for requests after packet-protection negotiation
69-
70- protected_session = DceRpcSession(
71- sspcontext=object(),
72- auth_level=RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
73- )
74- for protected_payload in (DceRpc5Request(), DceRpc5Response()):
75- unsigned_packet = DceRpc5(
76- pfc_flags="PFC_FIRST_FRAG+PFC_LAST_FRAG",
77- ) / protected_payload
78- try:
79- protected_session.in_pkt(unsigned_packet)
80- assert False, "The unsigned packet was accepted"
81- except ValueError as exc:
82- assert str(exc) == "DCE/RPC packet protection is required !"
83-
8468= Dissect DCE/RPC v5 Request with Kerberos GSSAPI/RFC1964
8569
8670pkt = DceRpc(b"\x05\x00\x00\x03\x10\x00\x00\x00\xcd\x00-\x00\x01\x00\x00\x00x\x00\x00\x00\x00\x00\x00\x00j\x87\xb4\xa8DrE3\xfa\xc1\x1d\x9e\xb7\x8a_\xffr\xbe\x13\xc4<\x85\xf0\xf2'y\x84t%u|e\xef/\x04\xb0m\x98\xb1\xd2\x00KwW#P\x8f2\xecB\x81\x19\xf3g\xd2o[\x07L-\xb8\x89\x05\xcf?\xcf\t\xeb\xb3&&6\xb7\x84\xb6\xcd8Ao\x8c\x94\xca\x03\xe3\x0e\x86'-\xfaHj\xcez\xf0A\x83\x9dX\r\xe8\x96\x07Bs\xaf\x9c[=2\x9eS\xb1\x18\x84 \xb4y\n9\xdf\x92\x1c\xd8\xe2e\xd3^,\t\x06\x08\x00pj\x8f\x04`+\x06\t*\x86H\x86\xf7\x12\x01\x02\x02\x02\x01\x11\x00\x10\x00\xff\xffp\xc0\\m\xfe\xa4\xe1!\xf7\xdf\xbf\xa4\xad\xdf\xcb\x16\x1e\xb5+{\x97\xaf\xd5~")
@@ -171,6 +155,41 @@ assert DceRpc5Request in pkt2
171155assert conf.padding_layer not in pkt2
172156assert pkt2.vt_trailer.commands[1].InterfaceId == pkt2.object
173157
158+ = Build and dissect DCE/RPC - Fragment test
159+
160+ from unittest import mock
161+ from scapy.layers.msrpce.raw.ept import ept_lookup_Request
162+
163+ # 1. Build a KerberosSSP ready to encrypt
164+ ssp = KerberosSSP()
165+ ctx = KerberosSSP.CONTEXT(IsAcceptor=False, req_flags=GSS_C_FLAGS.GSS_C_CONF_FLAG)
166+ ctx.KrbSessionKey = Key(EncryptionType.AES128_CTS_HMAC_SHA1_96, key=bytes.fromhex("3705D96080C17728A0E800EAB6E0D23C"))
167+ ctx.SendSeqNum = 0x60cbacd3
168+ Confounder = bytes.fromhex("5256f3fb630cf12a")
169+ session = DceRpcSession(
170+ auth_level=RPC_C_AUTHN_LEVEL.PKT_PRIVACY,
171+ ssp=ssp,
172+ )
173+ session.sspcontext = ctx
174+
175+ # 2. Make a big packet go out, this will trigger fragmentation
176+ pkt = DceRpc5(call_id=12, vt_trailer=DceRpcSecVT(commands=[DceRpcSecVTCommand(SEC_VT_COMMAND_END=1)/DceRpcSecVTPcontext()])) / DceRpc5Request() / (b"\x11" * 5000)
177+ with mock.patch('scapy.layers.kerberos.os.urandom', side_effect=lambda x: Confounder):
178+ pkt = session.out_pkt(pkt)
179+
180+ # Checks on the fragmented packet
181+ assert len(pkt) == 2
182+
183+ assert pkt[0].pfc_flags == 1
184+ assert pkt[0].vt_trailer is None
185+ assert bytes(pkt[0].auth_verifier.auth_value) == b'\x05\x04\x06\xff\x00\x10\x00\x1c\x00\x00\x00\x00`\xcb\xac\xd3\xc3\xb9\r\xf4F\xf5\x17\xc2\x9cvA\xd2,\x15\x13\xb4\xdcI\x06\x8b^\x057\xe1\xa0\x18\xbd\t=\xba)\xa3\xe1\xa1y\x81\xc7b\xdc\xbd\xd8\xf2(\xd1q?\xd6#\x8e\xd2^\xa0'
186+ assert len(pkt[0].load) == 4176
187+
188+ assert pkt[1].pfc_flags == 2
189+ assert pkt[1].vt_trailer is not None
190+ assert bytes(pkt[1].auth_verifier.auth_value) == b'\x05\x04\x06\xff\x00\x10\x00\x1c\x00\x00\x00\x00`\xcb\xac\xd4\x9c_--ht\xc4\xc3\xfd\x8e!\x04;\x8d\xe1\xe1\x8c\xf3b\xb9\xb4\xb5%J\xff\xc5\xcb\xc1\x87\xdc]\x85S\x18\x07\x1d~e\x8f\xa6v]G\xa1q?\xd6#\x8e\xd2^\xa0'
191+ assert len(pkt[1].load) == 824
192+
174193+ Check DCE/RPC 4 layer
175194
176195= DCE/RPC 4 default values
@@ -617,15 +636,6 @@ assert pkts[22][ept_map_Response].valueof("ITowers")[0].tower_octet_string == b'
617636
618637% The fact that all of this actually works is crazy to me.
619638
620- = Server faults a request received before bind
621- from scapy.layers.dcerpc import DCERPC_Transport, DceRpc5Fault
622- from scapy.layers.msrpce.rpcserver import DCERPC_Server
623-
624- server = DCERPC_Server(DCERPC_Transport.NCACN_IP_TCP, port=135, verb=False)
625- request = hex_bytes("050000031000000018000000000000000000000000000000")
626- server.recv(request)
627- assert DceRpc5Fault in server.get_response()
628-
629639= Functional: Define a MS-RPC server
630640% Same as in dcerpc.rst
631641
@@ -670,27 +680,6 @@ class MyRPCServer(DCERPC_Server):
670680 ndr64=self.ndr64,
671681 )
672682
673- = Auth3 cannot lower the protection level negotiated during bind
674-
675- class TestSSP:
676- def GSS_Accept_sec_context(self, context, token):
677- return object(), None, 0
678-
679- server = DCERPC_Server(
680- DCERPC_Transport.NCACN_IP_TCP,
681- verb=False,
682- ssp=TestSSP(),
683- )
684- server.session.rpc_bind_interface = find_dcerpc_interface("wkssvc")
685- server.session.auth_level = DCE_C_AUTHN_LEVEL.PKT_INTEGRITY
686- auth3 = DceRpc5() / DceRpc5Auth3()
687- auth3.auth_verifier = CommonAuthVerifier(
688- auth_level=DCE_C_AUTHN_LEVEL.CONNECT,
689- auth_value=Raw(b"authenticate"),
690- )
691- server.recv(auth3)
692- assert server.session.auth_level == DCE_C_AUTHN_LEVEL.PKT_INTEGRITY
693-
694683= Functional: Define wrapper over samba's rpcclient
695684~ linux samba
696685
@@ -1025,3 +1014,36 @@ rpcserver.close()
10251014= Restore conf.debug_dissector
10261015
10271016conf.debug_dissector = old_debug_dissector
1017+
1018+ + Security regression tests
1019+
1020+ = Security - Auth3 cannot lower the protection level negotiated during bind
1021+
1022+ class TestSSP:
1023+ def GSS_Accept_sec_context(self, context, token):
1024+ return object(), None, 0
1025+
1026+ server = DCERPC_Server(
1027+ DCERPC_Transport.NCACN_IP_TCP,
1028+ verb=False,
1029+ ssp=TestSSP(),
1030+ )
1031+ server.session.rpc_bind_interface = find_dcerpc_interface("wkssvc")
1032+ server.session.auth_level = DCE_C_AUTHN_LEVEL.PKT_INTEGRITY
1033+ auth3 = DceRpc5() / DceRpc5Auth3()
1034+ auth3.auth_verifier = CommonAuthVerifier(
1035+ auth_level=DCE_C_AUTHN_LEVEL.CONNECT,
1036+ auth_value=Raw(b"authenticate"),
1037+ )
1038+ server.recv(auth3)
1039+ assert server.session.auth_level == DCE_C_AUTHN_LEVEL.PKT_INTEGRITY
1040+
1041+ = Security - Server faults a request received before bind
1042+
1043+ from scapy.layers.dcerpc import DCERPC_Transport, DceRpc5Fault
1044+ from scapy.layers.msrpce.rpcserver import DCERPC_Server
1045+
1046+ server = DCERPC_Server(DCERPC_Transport.NCACN_IP_TCP, port=135, verb=False)
1047+ request = hex_bytes("050000031000000018000000000000000000000000000000")
1048+ server.recv(request)
1049+ assert DceRpc5Fault in server.get_response()
0 commit comments