Skip to content

Commit cc8e091

Browse files
authored
SPNEGO: add new from_cli_arguments function, improvements (#4805)
* SPNEGO: add new from_cli_arguments function, improvements * AutoArgparse: support bytes, better naming * Add target_name for KerberosSSP * PEP8 fixes
1 parent 58526ec commit cc8e091

14 files changed

Lines changed: 238 additions & 104 deletions

File tree

doc/scapy/layers/smb.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ You might be wondering if you can pass the ``HashNT`` of the password of the use
7676

7777
.. code:: python
7878
79-
>>> smbclient("server1.domain.local", ssp=KerberosSSP(SPN="cifs/server1", UPN="Administrator@domain.local", PASSWORD="password"))
79+
>>> smbclient("server1.domain.local", ssp=KerberosSSP(UPN="Administrator@domain.local", PASSWORD="password"))
8080
8181
**smbclient using a** :class:`~scapy.layers.ntlm.KerberosSSP` **created by** `Ticketer++ <kerberos.html#ticketer>`_:
8282

@@ -155,7 +155,6 @@ Let's write a script that connects to a share and list the files in the root fol
155155
KerberosSSP(
156156
UPN="Administrator@domain.local",
157157
PASSWORD=password,
158-
SPN="cifs/server1",
159158
)
160159
])
161160
# Connect to the server

scapy/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ class ExtsManager(importlib.abc.MetaPathFinder):
599599
def __init__(self):
600600
self.exts: List[ScapyExt] = []
601601
self.all_specs: Dict[str, ScapyExt.ScapyExtSpec] = {}
602+
self._loaded: List[str] = []
602603
# Add to meta_path as we are an import provider
603604
if self not in sys.meta_path:
604605
sys.meta_path.append(self)
@@ -628,6 +629,9 @@ def load(self, extension: str):
628629
629630
:param extension: the name of the extension, as installed.
630631
"""
632+
if extension in self._loaded:
633+
return
634+
631635
try:
632636
import importlib.metadata
633637
except ImportError:
@@ -686,6 +690,7 @@ def load(self, extension: str):
686690

687691
# Add to the extension list
688692
self.exts.append(ext)
693+
self._loaded.append(extension)
689694

690695
# If there are bash autocompletions, add them
691696
if ext.bash_completions:

scapy/layers/gssapi.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ def GSS_Init_sec_context(
455455
self,
456456
Context: CONTEXT,
457457
token=None,
458+
target_name: Optional[str] = None,
458459
req_flags: Optional[GSS_C_FLAGS] = None,
459460
chan_bindings: GssChannelBindings = GSS_C_NO_CHANNEL_BINDINGS,
460461
):

scapy/layers/http.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,7 @@ def request(
942942
self.sspcontext, token, status = self.ssp.GSS_Init_sec_context(
943943
self.sspcontext,
944944
ssp_blob,
945+
target_name="http/" + host,
945946
req_flags=0,
946947
chan_bindings=self.chan_bindings,
947948
)

scapy/layers/kerberos.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@
143143
from scapy.layers.smb2 import STATUS_ERREF
144144
from scapy.layers.x509 import X509_AlgorithmIdentifier
145145

146+
# Redirect exports from RFC3961
147+
try:
148+
from scapy.libs.rfc3961 import * # noqa: F401,F403
149+
except ImportError:
150+
pass
151+
146152
# Typing imports
147153
from typing import (
148154
Optional,
@@ -4008,7 +4014,8 @@ class KerberosSSP(SSP):
40084014
40094015
:param ST: the service ticket to use for access.
40104016
If not provided, will be retrieved
4011-
:param SPN: the SPN of the service to use
4017+
:param SPN: the SPN of the service to use. If not provided, will use the
4018+
target_name provided in the GSS_Init_sec_context
40124019
:param UPN: The client UPN
40134020
:param DC_IP: (optional) is ST+KEY are not provided, will need to contact
40144021
the KDC at this IP. If not provided, will perform dc locator.
@@ -4506,6 +4513,7 @@ def GSS_Init_sec_context(
45064513
self,
45074514
Context: CONTEXT,
45084515
token=None,
4516+
target_name: Optional[str] = None,
45094517
req_flags: Optional[GSS_C_FLAGS] = None,
45104518
chan_bindings: GssChannelBindings = GSS_C_NO_CHANNEL_BINDINGS,
45114519
):
@@ -4536,8 +4544,8 @@ def GSS_Init_sec_context(
45364544
# Do we have a ST?
45374545
if self.ST is None:
45384546
# Client sends an AP-req
4539-
if not self.SPN:
4540-
raise ValueError("Missing SPN attribute")
4547+
if not self.SPN and not target_name:
4548+
raise ValueError("Missing SPN/target_name attribute")
45414549
additional_tickets = []
45424550
if self.U2U:
45434551
try:
@@ -4559,7 +4567,7 @@ def GSS_Init_sec_context(
45594567
# Use TGT
45604568
res = krb_tgs_req(
45614569
upn=self.UPN,
4562-
spn=self.SPN,
4570+
spn=self.SPN or target_name,
45634571
ip=self.DC_IP,
45644572
sessionkey=self.KEY,
45654573
ticket=self.TGT,
@@ -4571,7 +4579,7 @@ def GSS_Init_sec_context(
45714579
# Ask for TGT then ST
45724580
res = krb_as_and_tgs(
45734581
upn=self.UPN,
4574-
spn=self.SPN,
4582+
spn=self.SPN or target_name,
45754583
ip=self.DC_IP,
45764584
key=self.KEY,
45774585
password=self.PASSWORD,

scapy/layers/ldap.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,7 @@ def __init__(
18001800
verb=True,
18011801
):
18021802
self.sock = None
1803+
self.host = None
18031804
self.verb = verb
18041805
self.ssl = False
18051806
self.sslcontext = None
@@ -1815,7 +1816,7 @@ def __init__(
18151816

18161817
def connect(
18171818
self,
1818-
ip,
1819+
host,
18191820
port=None,
18201821
use_ssl=False,
18211822
sslcontext=None,
@@ -1826,7 +1827,7 @@ def connect(
18261827
"""
18271828
Initiate a connection
18281829
1829-
:param ip: the IP or hostname to connect to.
1830+
:param host: the IP or hostname to connect to.
18301831
:param port: the port to connect to. (Default: 389 or 636)
18311832
18321833
:param use_ssl: whether to use LDAPS or not. (Default: False)
@@ -1844,17 +1845,18 @@ def connect(
18441845
port = 389
18451846
sock = socket.socket()
18461847
self.timeout = timeout
1848+
self.host = host
18471849
sock.settimeout(timeout)
18481850
if self.verb:
18491851
print(
18501852
"\u2503 Connecting to %s on port %s%s..."
18511853
% (
1852-
ip,
1854+
host,
18531855
port,
18541856
" with SSL" if self.ssl else "",
18551857
)
18561858
)
1857-
sock.connect((ip, port))
1859+
sock.connect((host, port))
18581860
if self.verb:
18591861
print(
18601862
conf.color_theme.green(
@@ -1872,7 +1874,7 @@ def connect(
18721874
context = ssl.create_default_context()
18731875
else:
18741876
context = self.sslcontext
1875-
sock = context.wrap_socket(sock, server_hostname=sni or ip)
1877+
sock = context.wrap_socket(sock, server_hostname=sni or host)
18761878
# Wrap the socket in a Scapy socket
18771879
if self.ssl:
18781880
self.sock = SSLStreamSocket(sock, LDAP)
@@ -2042,6 +2044,7 @@ def bind(
20422044
# 2. First exchange: Negotiate
20432045
self.sspcontext, token, status = self.ssp.GSS_Init_sec_context(
20442046
self.sspcontext,
2047+
target_name="ldap/" + self.host,
20452048
req_flags=(
20462049
GSS_C_FLAGS.GSS_C_REPLAY_FLAG
20472050
| GSS_C_FLAGS.GSS_C_SEQUENCE_FLAG
@@ -2068,6 +2071,7 @@ def bind(
20682071
self.sspcontext, token, status = self.ssp.GSS_Init_sec_context(
20692072
self.sspcontext,
20702073
GSSAPI_BLOB(val),
2074+
target_name="ldap/" + self.host,
20712075
chan_bindings=self.chan_bindings,
20722076
)
20732077
resp = self.sr1(
@@ -2090,6 +2094,7 @@ def bind(
20902094
# GSSAPI or SPNEGO
20912095
self.sspcontext, token, status = self.ssp.GSS_Init_sec_context(
20922096
self.sspcontext,
2097+
target_name="ldap/" + self.host,
20932098
req_flags=(
20942099
# Required flags for GSSAPI: RFC4752 sect 3.1
20952100
GSS_C_FLAGS.GSS_C_REPLAY_FLAG
@@ -2122,6 +2127,7 @@ def bind(
21222127
self.sspcontext, token, status = self.ssp.GSS_Init_sec_context(
21232128
self.sspcontext,
21242129
GSSAPI_BLOB(val),
2130+
target_name="ldap/" + self.host,
21252131
chan_bindings=self.chan_bindings,
21262132
)
21272133
else:

scapy/layers/msrpce/msnrpc.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,12 @@ def GSS_VerifyMICEx(self, Context, msgs, signature):
475475
self._unsecure(Context, msgs, signature, False)
476476

477477
def GSS_Init_sec_context(
478-
self, Context, token=None, req_flags: Optional[GSS_C_FLAGS] = None
478+
self,
479+
Context: CONTEXT,
480+
token=None,
481+
target_name: Optional[str] = None,
482+
req_flags: Optional[GSS_C_FLAGS] = None,
483+
chan_bindings: bytes = GSS_C_NO_CHANNEL_BINDINGS,
479484
):
480485
if Context is None:
481486
Context = self.CONTEXT(True, req_flags=req_flags, AES=self.AES)

scapy/layers/msrpce/rpcclient.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def __init__(self, transport, ndr64=False, ndrendian="little", verb=True, **kwar
7676
self.ndr64 = ndr64
7777
self.ndrendian = ndrendian
7878
self.verb = verb
79+
self.host = None
7980
self.auth_level = kwargs.pop("auth_level", DCE_C_AUTHN_LEVEL.NONE)
8081
self.auth_context_id = kwargs.pop("auth_context_id", 0)
8182
self.ssp = kwargs.pop("ssp", None) # type: SSP
@@ -100,7 +101,7 @@ def from_smblink(cls, smbcli, smb_kwargs={}, **kwargs):
100101
)
101102
return client
102103

103-
def connect(self, ip, port=None, timeout=5, smb_kwargs={}):
104+
def connect(self, host, port=None, timeout=5, smb_kwargs={}):
104105
"""
105106
Initiate a connection
106107
"""
@@ -113,14 +114,15 @@ def connect(self, ip, port=None, timeout=5, smb_kwargs={}):
113114
raise ValueError(
114115
"Can't guess the port for transport: %s" % self.transport
115116
)
117+
self.host = host
116118
sock = socket.socket()
117119
sock.settimeout(timeout)
118120
if self.verb:
119121
print(
120122
"\u2503 Connecting to %s on port %s via %s..."
121-
% (ip, port, repr(self.transport))
123+
% (host, port, repr(self.transport))
122124
)
123-
sock.connect((ip, port))
125+
sock.connect((host, port))
124126
if self.verb:
125127
print(
126128
conf.color_theme.green(
@@ -313,6 +315,7 @@ def _bind(self, interface, reqcls, respcls):
313315
else 0
314316
)
315317
),
318+
target_name="host/" + self.host,
316319
)
317320
if status not in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]:
318321
# Authentication failed.
@@ -349,6 +352,7 @@ def _bind(self, interface, reqcls, respcls):
349352
self.sspcontext, token, status = self.ssp.GSS_Init_sec_context(
350353
self.sspcontext,
351354
token=resp.auth_verifier.auth_value,
355+
target_name="host/" + self.host,
352356
)
353357
if status in [GSS_S_CONTINUE_NEEDED, GSS_S_COMPLETE]:
354358
# Authentication should continue, in two ways:
@@ -390,6 +394,7 @@ def _bind(self, interface, reqcls, respcls):
390394
self.sspcontext, token, status = self.ssp.GSS_Init_sec_context(
391395
self.sspcontext,
392396
token=resp.auth_verifier.auth_value,
397+
target_name="host/" + self.host,
393398
)
394399
# Check context acceptance
395400
if (

scapy/layers/ntlm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,7 @@ def GSS_Init_sec_context(
13881388
self,
13891389
Context: CONTEXT,
13901390
token=None,
1391+
target_name: Optional[str] = None,
13911392
req_flags: Optional[GSS_C_FLAGS] = None,
13921393
chan_bindings: GssChannelBindings = GSS_C_NO_CHANNEL_BINDINGS,
13931394
):

scapy/layers/smb2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1594,7 +1594,7 @@ def post_build(self, pkt, pay):
15941594
},
15951595
config=[
15961596
("Offset", _NTLM_ENUM.OFFSET),
1597-
]
1597+
],
15981598
)
15991599
+ pay
16001600
)

0 commit comments

Comments
 (0)