Skip to content

Commit e24d9d0

Browse files
committed
SPNEGO: add new from_cli_arguments function, improvements
1 parent e20f373 commit e24d9d0

File tree

4 files changed

+148
-72
lines changed

4 files changed

+148
-72
lines changed

scapy/layers/kerberos.py

Lines changed: 6 additions & 0 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,

scapy/layers/smbclient.py

Lines changed: 21 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import threading
2121

2222
from scapy.automaton import ATMT, Automaton, ObjectPipe
23-
from scapy.base_classes import Net
2423
from scapy.config import conf
2524
from scapy.error import Scapy_Exception
2625
from scapy.fields import UTCTimeField
@@ -29,8 +28,6 @@
2928
CLIUtil,
3029
pretty_list,
3130
human_size,
32-
valid_ip,
33-
valid_ip6,
3431
)
3532
from scapy.volatile import RandUUID
3633

@@ -40,12 +37,6 @@
4037
GSS_S_CONTINUE_NEEDED,
4138
GSS_C_FLAGS,
4239
)
43-
from scapy.layers.inet6 import Net6
44-
from scapy.layers.kerberos import (
45-
KerberosSSP,
46-
krb_as_and_tgs,
47-
_parse_upn,
48-
)
4940
from scapy.layers.msrpce.raw.ms_srvs import (
5041
LPSHARE_ENUM_STRUCT,
5142
NetrShareEnum_Request,
@@ -54,7 +45,6 @@
5445
)
5546
from scapy.layers.ntlm import (
5647
NTLMSSP,
57-
MD4le,
5848
)
5949
from scapy.layers.smb import (
6050
SMBNegotiate_Request,
@@ -1104,11 +1094,12 @@ class smbclient(CLIUtil):
11041094
:param UPN: the upn to use (DOMAIN/USER, DOMAIN\USER, USER@DOMAIN or USER)
11051095
:param guest: use guest mode (over NTLM)
11061096
:param ssp: if provided, use this SSP for auth.
1107-
:param kerberos: if available, whether to use Kerberos or not
11081097
:param kerberos_required: require kerberos
11091098
:param port: the TCP port. default 445
11101099
:param password: (string) if provided, used for auth
11111100
:param HashNt: (bytes) if provided, used for auth (NTLM)
1101+
:param HashAes256Sha96: (bytes) if provided, used for auth (Kerberos)
1102+
:param HashAes128Sha96: (bytes) if provided, used for auth (Kerberos)
11121103
:param ST: if provided, the service ticket to use (Kerberos)
11131104
:param KEY: if provided, the session key associated to the ticket (Kerberos)
11141105
:param cli: CLI mode (default True). False to use for scripting
@@ -1125,9 +1116,10 @@ def __init__(
11251116
UPN: str = None,
11261117
password: str = None,
11271118
guest: bool = False,
1128-
kerberos: bool = True,
11291119
kerberos_required: bool = False,
1130-
HashNt: str = None,
1120+
HashNt: bytes = None,
1121+
HashAes256Sha96: bytes = None,
1122+
HashAes128Sha96: bytes = None,
11311123
port: int = 445,
11321124
timeout: int = 2,
11331125
debug: int = 0,
@@ -1141,71 +1133,30 @@ def __init__(
11411133
):
11421134
if cli:
11431135
self._depcheck()
1144-
hostname = None
1145-
# Check if target is a hostname / Check IP
1146-
if ":" in target:
1147-
family = socket.AF_INET6
1148-
if not valid_ip6(target):
1149-
hostname = target
1150-
target = str(Net6(target))
1151-
else:
1152-
family = socket.AF_INET
1153-
if not valid_ip(target):
1154-
hostname = target
1155-
target = str(Net(target))
11561136
assert UPN or ssp or guest, "Either UPN, ssp or guest must be provided !"
11571137
# Do we need to build a SSP?
11581138
if ssp is None:
11591139
# Create the SSP (only if not guest mode)
11601140
if not guest:
1161-
# Check UPN
1162-
try:
1163-
_, realm = _parse_upn(UPN)
1164-
if realm == ".":
1165-
# Local
1166-
kerberos = False
1167-
except ValueError:
1168-
# not a UPN: NTLM
1169-
kerberos = False
1170-
# Do we need to ask the password?
1171-
if HashNt is None and password is None and ST is None:
1172-
# yes.
1173-
from prompt_toolkit import prompt
1174-
1175-
password = prompt("Password: ", is_password=True)
1176-
ssps = []
1177-
# Kerberos
1178-
if kerberos and hostname:
1179-
if ST is None:
1180-
resp = krb_as_and_tgs(
1181-
upn=UPN,
1182-
spn="cifs/%s" % hostname,
1183-
password=password,
1184-
debug=debug,
1185-
)
1186-
if resp is not None:
1187-
ST, KEY = resp.tgsrep.ticket, resp.sessionkey
1188-
if ST:
1189-
ssps.append(KerberosSSP(UPN=UPN, ST=ST, KEY=KEY, debug=debug))
1190-
elif kerberos_required:
1191-
raise ValueError(
1192-
"Kerberos required but target isn't a hostname !"
1193-
)
1194-
elif kerberos_required:
1195-
raise ValueError(
1196-
"Kerberos required but domain not specified in the UPN, "
1197-
"or target isn't a hostname !"
1198-
)
1199-
# NTLM
1200-
if not kerberos_required:
1201-
if HashNt is None and password is not None:
1202-
HashNt = MD4le(password)
1203-
ssps.append(NTLMSSP(UPN=UPN, HASHNT=HashNt))
1204-
# Build the SSP
1205-
ssp = SPNEGOSSP(ssps)
1141+
ssp = SPNEGOSSP.from_cli_arguments(
1142+
UPN=UPN,
1143+
target=target,
1144+
password=password,
1145+
HashNt=HashNt,
1146+
HashAes256Sha96=HashAes256Sha96,
1147+
HashAes128Sha96=HashAes128Sha96,
1148+
ST=ST,
1149+
KEY=KEY,
1150+
kerberos_required=kerberos_required,
1151+
)
12061152
else:
12071153
# Guest mode
12081154
ssp = None
1155+
# Check if target is IPv4 or IPv6
1156+
if ":" in target:
1157+
family = socket.AF_INET6
1158+
else:
1159+
family = socket.AF_INET
12091160
# Open socket
12101161
sock = socket.socket(family, socket.SOCK_STREAM)
12111162
# Configure socket for SMB:

scapy/layers/spnego.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
ASN1F_optional,
3939
)
4040
from scapy.asn1packet import ASN1_Packet
41+
from scapy.base_classes import Net
4142
from scapy.fields import (
4243
FieldListField,
4344
LEIntEnumField,
@@ -56,7 +57,12 @@
5657
XStrLenField,
5758
)
5859
from scapy.packet import Packet, bind_layers
60+
from scapy.utils import (
61+
valid_ip,
62+
valid_ip6,
63+
)
5964

65+
from scapy.layers.inet6 import Net6
6066
from scapy.layers.gssapi import (
6167
GSSAPI_BLOB,
6268
GSSAPI_BLOB_SIGNATURE,
@@ -75,8 +81,13 @@
7581
# SSP Providers
7682
from scapy.layers.kerberos import (
7783
Kerberos,
84+
KerberosSSP,
85+
krb_as_and_tgs,
86+
_parse_upn,
7887
)
7988
from scapy.layers.ntlm import (
89+
NTLMSSP,
90+
MD4le,
8091
NEGOEX_EXCHANGE_NTLM,
8192
NTLM_Header,
8293
_NTLMPayloadField,
@@ -619,6 +630,112 @@ def __init__(self, ssps, **kwargs):
619630
self.force_supported_mechtypes = kwargs.pop("force_supported_mechtypes", None)
620631
super(SPNEGOSSP, self).__init__(**kwargs)
621632

633+
@classmethod
634+
def from_cli_arguments(
635+
cls,
636+
UPN: str,
637+
target: str,
638+
password: str = None,
639+
HashNt: bytes = None,
640+
HashAes256Sha96: bytes = None,
641+
HashAes128Sha96: bytes = None,
642+
kerberos_required: bool = False,
643+
ST=None,
644+
KEY=None,
645+
debug: int = 0,
646+
):
647+
"""
648+
Initialize a SPNEGOSSP from a list of many arguments.
649+
This is useful in a CLI, with NTLM and Kerberos supported by default.
650+
651+
:param UPN: the UPN of the user to use.
652+
:param target: the target IP/hostname entered by the user.
653+
:param kerberos_required: require kerberos
654+
:param password: (string) if provided, used for auth
655+
:param HashNt: (bytes) if provided, used for auth (NTLM)
656+
:param HashAes256Sha96: (bytes) if provided, used for auth (Kerberos)
657+
:param HashAes128Sha96: (bytes) if provided, used for auth (Kerberos)
658+
:param ST: if provided, the service ticket to use (Kerberos)
659+
:param KEY: if ST provided, the session key associated to the ticket (Kerberos).
660+
Else, the user secret key.
661+
"""
662+
kerberos = True
663+
hostname = None
664+
# Check if target is a hostname / Check IP
665+
if ":" in target:
666+
if not valid_ip6(target):
667+
hostname = target
668+
target = str(Net6(target))
669+
else:
670+
if not valid_ip(target):
671+
hostname = target
672+
target = str(Net(target))
673+
# Check UPN
674+
try:
675+
_, realm = _parse_upn(UPN)
676+
if realm == ".":
677+
# Local
678+
kerberos = False
679+
except ValueError:
680+
# not a UPN: NTLM only
681+
kerberos = False
682+
# Do we need to ask the password?
683+
if HashNt is None and password is None and ST is None:
684+
# yes.
685+
from prompt_toolkit import prompt
686+
687+
password = prompt("Password: ", is_password=True)
688+
ssps = []
689+
# Kerberos
690+
if kerberos and hostname:
691+
# Get ticket if we don't already have one.
692+
if ST is None:
693+
# In this case, KEY is supposed to be the user's key.
694+
from scapy.libs.rfc3961 import Key, EncryptionType
695+
if KEY is None and HashAes256Sha96:
696+
KEY = Key(
697+
EncryptionType.AES256_CTS_HMAC_SHA1_96,
698+
HashAes256Sha96,
699+
)
700+
elif KEY is None and HashAes128Sha96:
701+
KEY = Key(
702+
EncryptionType.AES128_CTS_HMAC_SHA1_96,
703+
HashAes128Sha96,
704+
)
705+
elif KEY is None and HashNt:
706+
KEY = Key(
707+
EncryptionType.RC4_HMAC,
708+
HashNt,
709+
)
710+
# Get the ticket.
711+
resp = krb_as_and_tgs(
712+
upn=UPN,
713+
key=KEY,
714+
spn="cifs/%s" % hostname,
715+
password=password,
716+
debug=debug,
717+
)
718+
if resp is not None:
719+
ST, KEY = resp.tgsrep.ticket, resp.sessionkey
720+
if ST:
721+
ssps.append(KerberosSSP(UPN=UPN, ST=ST, KEY=KEY, debug=debug))
722+
elif kerberos_required:
723+
raise ValueError(
724+
"Kerberos required but target isn't a hostname !"
725+
)
726+
elif kerberos_required:
727+
raise ValueError(
728+
"Kerberos required but domain not specified in the UPN, "
729+
"or target isn't a hostname !"
730+
)
731+
# NTLM
732+
if not kerberos_required:
733+
if HashNt is None and password is not None:
734+
HashNt = MD4le(password)
735+
ssps.append(NTLMSSP(UPN=UPN, HASHNT=HashNt))
736+
# Build the SSP
737+
return cls(ssps)
738+
622739
def _extract_gssapi(self, Context, x):
623740
status, otherMIC, rawToken = None, None, False
624741
# Extract values from GSSAPI

scapy/libs/rfc3961.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
# TODO: support cipher states...
2323

2424
__all__ = [
25-
"EncryptionType",
2625
"ChecksumType",
27-
"Key",
26+
"EncryptionType",
2827
"InvalidChecksum",
28+
"KRB_FX_CF2",
29+
"Key",
30+
"SP800108_KDFCTR",
2931
"_rfc1964pad",
3032
]
3133

0 commit comments

Comments
 (0)