Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions doc/scapy/layers/dcerpc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,6 @@ To start an endpoint mapper (this should be a separate process from your RPC ser
)
.. note:: Currently, a DCERPC_Server will let a client bind on all interfaces that Scapy has registered (imported). Supposedly though, you know which RPCs are going to be queried.

Debugging with extended error information (eerr)
------------------------------------------------

Expand Down
10 changes: 8 additions & 2 deletions scapy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,10 @@ def layers(self):
except ImportError:
import __builtin__ # noqa: F401
for lay in self.ldict:
doc = eval(lay).__doc__
try:
doc = eval(lay).__doc__
except AttributeError:
continue
result.append((lay, doc.strip().split("\n")[0] if doc else lay))
return result

Expand Down Expand Up @@ -649,7 +652,10 @@ def load(self, extension: str):
return

# Check the classifiers
if distr.metadata.get('License-Expression', None) not in self.GPLV2_LICENCES:
if (
distr.metadata.get('License-Expression', None) not in self.GPLV2_LICENCES
and distr.metadata.get('License', None) not in self.GPLV2_LICENCES
):
log_loading.warning(
"'%s' has no GPLv2 classifier therefore cannot be loaded." % extension
)
Expand Down
3 changes: 3 additions & 0 deletions scapy/layers/dcerpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1305,9 +1305,12 @@ def register_dcerpc_interface(name, uuid, version, opnums):
if_version,
opnums,
)

# bind for build
for opnum, operations in opnums.items():
bind_top_down(DceRpc5Request, operations.request, opnum=opnum)
operations.request.opnum = opnum
operations.request.intf = uuid


def find_dcerpc_interface(name) -> DceRpcInterface:
Expand Down
42 changes: 27 additions & 15 deletions scapy/layers/msrpce/rpcclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ def from_smblink(cls, smbcli, smb_kwargs={}, **kwargs):

@property
def session(self) -> DceRpcSession:
return self.sock.session
try:
return self.sock.session
except AttributeError:
raise ValueError("Client is not connected ! Please connect()")

def connect(
self,
Expand Down Expand Up @@ -197,6 +200,7 @@ def connect(
transport=self.transport,
ndrendian=self.ndrendian,
verb=self.verb,
ssp=self.ssp,
smb_kwargs=smb_kwargs,
)
if endpoints:
Expand Down Expand Up @@ -234,14 +238,15 @@ def connect(
)

if self.transport == DCERPC_Transport.NCACN_NP: # SMB
# If the endpoint is provided, connect to it.
if endpoint is not None:
self.open_smbpipe(endpoint)

# We pack the socket into a SMB_RPC_SOCKET
sock = self.smbrpcsock = SMB_RPC_SOCKET.from_tcpsock(
sock, ssp=self.ssp, **smb_kwargs
)

# If the endpoint is provided, connect to it.
if endpoint is not None:
self.open_smbpipe(endpoint)

self.sock = DceRpcSocket(sock, DceRpc5, **self.dcesockargs)
elif self.transport == DCERPC_Transport.NCACN_IP_TCP:
self.sock = DceRpcSocket(
Expand Down Expand Up @@ -351,6 +356,9 @@ def sr1_req(self, pkt, **kwargs):
if "opnum" in kwargs:
opnum["opnum"] = kwargs.pop("opnum")

# Set NDR64
pkt.ndr64 = self.ndr64

# Send/receive
resp = self.sr1(
DceRpc5Request(
Expand Down Expand Up @@ -480,13 +488,17 @@ def _check_bind_context(self, interface, contexts) -> bool:
for i, ctx in enumerate(contexts):
if ctx.result == 0:
# Context was accepted. Remove all others from cache
self.contexts[interface] = [self.contexts[interface][i]]
if len(self.contexts[interface]) != 1:
self.contexts[interface] = [self.contexts[interface][i]]
return True

return False

def _bind(
self, interface: Union[DceRpcInterface, ComInterface], reqcls, respcls
self,
interface: Union[DceRpcInterface, ComInterface],
reqcls,
respcls,
) -> bool:
"""
Internal: used to send a bind/alter request
Expand Down Expand Up @@ -681,11 +693,10 @@ def _bind(
else:
print(conf.color_theme.fail("! Failure"))
resp.show()
if DceRpc5Fault in resp:
if resp[DceRpc5Fault].payload and not isinstance(
resp[DceRpc5Fault].payload, conf.raw_layer
):
resp[DceRpc5Fault].payload.show()
if resp[DceRpc5Fault].payload and not isinstance(
resp[DceRpc5Fault].payload, conf.raw_layer
):
resp[DceRpc5Fault].payload.show()
else:
print(conf.color_theme.fail("! Failure"))
resp.show()
Expand Down Expand Up @@ -900,7 +911,6 @@ def epm_map(self, interface):
return endpoints
elif status == 0x16C9A0D6:
if self.verb:
pkt.show()
print(
conf.color_theme.fail(
"! Server errored: 'There are no elements that satisfy"
Expand Down Expand Up @@ -953,7 +963,9 @@ def get_endpoint(
client.connect(ip, endpoint=endpoint, smb_kwargs=smb_kwargs)

client.bind(find_dcerpc_interface("ept"))
endpoints = client.epm_map(interface)
try:
endpoints = client.epm_map(interface)
finally:
client.close()

client.close()
return endpoints
51 changes: 47 additions & 4 deletions scapy/layers/msrpce/rpcserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

import socket
import uuid
import threading
from collections import deque

Expand All @@ -27,13 +28,15 @@
DceRpc5Bind,
DceRpc5BindAck,
DceRpc5BindNak,
DceRpc5Fault,
DceRpc5PortAny,
DceRpc5Request,
DceRpc5Response,
DceRpc5Result,
DceRpc5TransferSyntax,
DceRpcInterface,
DceRpcSession,
NDRPacket,
RPC_C_AUTHN_LEVEL,
)

Expand All @@ -49,11 +52,17 @@
# Typing
from typing import (
Dict,
Callable,
Optional,
Tuple,
)


class _DCERPC_Server_metaclass(type):
# This value is calculated for each DCE/RPC server, and contains
# the callables sorted by interface+opnum
dcerpc_commands: Dict[Tuple[uuid.UUID, int], Callable] = {}

def __new__(cls, name, bases, dct):
dct.setdefault(
"dcerpc_commands",
Expand Down Expand Up @@ -108,7 +117,14 @@ def answer(reqcls):
"""

def deco(func):
func.dcerpc_command = reqcls
if not issubclass(reqcls, NDRPacket):
raise ValueError("Cannot answer a non NDRPacket class !")
try:
func.dcerpc_command = reqcls.intf, reqcls.opnum
except AttributeError:
raise ValueError(
"NDRPacket class isn't registered or isn't a request !"
)
return func

return deco
Expand All @@ -120,10 +136,17 @@ def extend(self, server_cls):
self.dcerpc_commands.update(server_cls.dcerpc_commands)

def make_reply(self, req):
cls = req[DceRpc5Request].payload.__class__
if cls in self.dcerpc_commands:
"""
Make a response to the DCE/RPC request.

This finds whether a callback has been registered for this particular packet,
and call it if available.
"""
opnum = req[DceRpc5Request].opnum
intf = self.session.rpc_bind_interface.uuid
if (intf, opnum) in self.dcerpc_commands:
# call handler
return self.dcerpc_commands[cls](self, req)
return self.dcerpc_commands[(intf, opnum)](self, req)
return None

@classmethod
Expand Down Expand Up @@ -408,6 +431,26 @@ def recv(self, data):
">> RESPONSE: %s" % (resp.__class__.__name__)
)
)
else:
# Unimplemented request !
if self.verb:
print(
conf.color_theme.fail(
"! RPC request not implemented by server."
)
)
req.show()

# Return a Fault
hdr.pfc_flags += "PFC_DID_NOT_EXECUTE"
self.queue.extend(
hdr
/ DceRpc5Fault(
# nca_s_op_rng_error
status=0x1C010002,
cont_id=req.cont_id,
)
)
# If there was padding, process the second frag
if pad is not None:
self.recv(pad)
Expand Down
22 changes: 17 additions & 5 deletions scapy/layers/ntlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1416,7 +1416,15 @@ def __init__(
self.COMPUTER_NB_NAME.lower() + "." + self.DOMAIN_FQDN
)

self.IDENTITIES = IDENTITIES
if IDENTITIES:
self.IDENTITIES = {
# Windows usernames are case insensitive
user.upper(): hashnt
for user, hashnt in IDENTITIES.items()
}
else:
self.IDENTITIES = IDENTITIES

self.DO_NOT_CHECK_LOGIN = DO_NOT_CHECK_LOGIN
self.SERVER_CHALLENGE = SERVER_CHALLENGE
super(NTLMSSP, self).__init__(**kwargs)
Expand Down Expand Up @@ -1681,7 +1689,10 @@ def GSS_Init_sec_context(
+ [
AV_PAIR(
AvId="MsvAvSingleHost",
Value=Single_Host_Data(MachineID=os.urandom(32)),
Value=Single_Host_Data(
MachineID=os.urandom(32),
PermanentMachineID=os.urandom(32),
),
),
]
+ (
Expand Down Expand Up @@ -2048,7 +2059,8 @@ def _getSessionBaseKey(self, Context, auth_tok):
Function that returns the SessionBaseKey from the ntlm Authenticate.
"""
try:
username = auth_tok.UserName
# Windows usernames are case insensitive
username = auth_tok.UserName.upper()
except AttributeError:
username = None
try:
Expand Down Expand Up @@ -2076,9 +2088,9 @@ def _checkLogin(self, Context, auth_tok):

Overwrite and return True to bypass.
"""
# Create the NTLM AUTH
try:
username = auth_tok.UserName
# Windows usernames are case insensitive
username = auth_tok.UserName.upper()
except AttributeError:
username = None
try:
Expand Down
5 changes: 2 additions & 3 deletions scapy/layers/smbclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
from scapy.layers.msrpce.raw.ms_srvs import (
LPSHARE_ENUM_STRUCT,
NetrShareEnum_Request,
NetrShareEnum_Response,
SHARE_INFO_1_CONTAINER,
)
from scapy.layers.ntlm import (
Expand Down Expand Up @@ -1301,7 +1300,7 @@ def shares(self):
)
resp = self.rpcclient.sr1_req(req, timeout=self.timeout)
self.rpcclient.close_smbpipe()
if not isinstance(resp, NetrShareEnum_Response):
if resp.status != 0:
resp.show()
raise ValueError("NetrShareEnum_Request failed !")
results = []
Expand Down Expand Up @@ -1732,7 +1731,7 @@ def cat_complete(self, file):
return []
return self._fs_complete(file)

@CLIUtil.addcommand(spaces=True)
@CLIUtil.addcommand(spaces=True, globsupport=True)
def put(self, file):
"""
Upload a file
Expand Down
3 changes: 3 additions & 0 deletions scapy/tools/UTscapy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,9 @@ def main():
if VERB > 2:
print(" " + arrow + " libpcap mode")

if sys.version_info < (3, 8):
KW_KO.append("needs_py38plus")

KW_KO.append("disabled")

if ANNOTATIONS_MODE:
Expand Down
1 change: 1 addition & 0 deletions test/configs/bsd.utsc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"test/contrib/isotp_soft_socket.uts"
],
"onlyfailed": true,
"extensions": ["scapy-rpc"],
"preexec": {
"test/contrib/*.uts": "load_contrib(\"%name%\")",
"test/cert.uts": "load_layer(\"tls\")",
Expand Down
1 change: 1 addition & 0 deletions test/configs/linux.utsc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
],
"breakfailed": true,
"onlyfailed": true,
"extensions": ["scapy-rpc"],
"preexec": {
"test/contrib/*.uts": "load_contrib(\"%name%\")",
"test/scapy/layers/tls/*.uts": "load_layer(\"tls\")"
Expand Down
1 change: 1 addition & 0 deletions test/configs/windows.utsc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
],
"breakfailed": true,
"onlyfailed": true,
"extensions": ["scapy-rpc"],
"preexec": {
"test\\contrib\\*.uts": "load_contrib(\"%name%\")",
"test\\scapy\\layers\\tls\\*.uts": "load_layer(\"tls\")"
Expand Down
1 change: 1 addition & 0 deletions test/configs/windows2.utsc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
],
"breakfailed": true,
"onlyfailed": true,
"extensions": ["scapy-rpc"],
"preexec": {
"contrib\\*.uts": "load_contrib(\"%name%\")",
"scapy\\layers\\tls\\*.uts": "load_layer(\"tls\")"
Expand Down
2 changes: 1 addition & 1 deletion test/scapy/layers/dcerpc.uts
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ conf.dcerpc_session_enable = False

# Packet 15 has an encrypted vt_trailer
assert pkts[15].vt_trailer.commands[0].Command == 2
assert pkts[15].load == b'\x00\x00\x02\x00\x00\x00\x00\x00\x1a M\xe2\xd6O\xd1\x11\xa3\xda\x00\x00\xf8u\xae\r\x00\x00\x02\x00\x00\x00\x00\x004\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
assert bytes(pkts[15][DceRpc5Request].payload) == b'\x00\x00\x02\x00\x00\x00\x00\x00\x1a M\xe2\xd6O\xd1\x11\xa3\xda\x00\x00\xf8u\xae\r\x00\x00\x02\x00\x00\x00\x00\x004\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

assert pkts[21].obj.referent_id == 0x1
assert pkts[21].map_tower.value.tower_octet_string == b'\x05\x00\x13\x00\r5BQ\xe3\x06K\xd1\x11\xab\x04\x00\xc0O\xc2\xdc\xd2\x04\x00\x02\x00\x00\x00\x13\x00\r\x04]\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00+\x10H`\x02\x00\x02\x00\x00\x00\x01\x00\x0b\x02\x00\x00\x00\x01\x00\x07\x02\x00\x00\x87\x01\x00\t\x04\x00\x00\x00\x00\x00'
Expand Down
Loading
Loading