Skip to content

Commit fbd0956

Browse files
authored
Minor improvements to Kerberos, IGMP and FWDM (#5019)
* kerberos: support password info in KPASSWD errors AI-Assisted: no * fwdm: support setting a remote port in server mode AI-Assisted: no * igmp: fix typo + implement IGMPv3 leave equivalent AI-Assisted: no
1 parent 6ba0390 commit fbd0956

4 files changed

Lines changed: 69 additions & 8 deletions

File tree

scapy/fwdmachine.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class ForwardMachine:
7474
:param proto: the proto to use (default SOCK_STREAM)
7575
:param remote_address: the IP to use in SERVER mode, or by default in TPROXY when
7676
the destination is the local IP.
77+
:param remote_port: the port to use in SERVER mode. (else use 'port')
7778
:param remote_af: (optional) if provided, use a different address family to connect
7879
to the remote host.
7980
:param bind_address: the IP to bind locally. "0.0.0.0" by default in SERVER mode,
@@ -108,6 +109,7 @@ def __init__(
108109
af: socket.AddressFamily = socket.AF_INET,
109110
proto: socket.SocketKind = socket.SOCK_STREAM,
110111
remote_address: str = None,
112+
remote_port: int = None,
111113
remote_af: Optional[socket.AddressFamily] = None,
112114
bind_address: str = None,
113115
tls: bool = False,
@@ -129,6 +131,7 @@ def __init__(
129131
self.timeout = timeout
130132
self.MTU = MTU
131133
self.remote_address = remote_address
134+
self.remote_port = remote_port
132135
if self.tls or self.af == 40: # TLS or VSOCK
133136
self.sockcls = StreamSocketPeekless
134137
else:
@@ -164,9 +167,9 @@ def run(self):
164167
conn, addr = self.ssock.accept()
165168
# Calc dest
166169
dest = conn.getsockname()
167-
if self.mode == ForwardMachine.MODE.SERVER or (
168-
dest[0] in self.local_ips and self.remote_address
169-
):
170+
if self.mode == ForwardMachine.MODE.SERVER:
171+
dest = (self.remote_address, self.remote_port or self.port)
172+
elif dest[0] in self.local_ips and self.remote_address:
170173
dest = (self.remote_address,) + dest[1:]
171174
print(self.ct.green("%s -> %s connected !" % (repr(addr), repr(dest))))
172175
try:

scapy/layers/igmp.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ class IGMPv3_MRT(IGMPv3):
412412

413413

414414
bind_layers(IP, IGMP, proto=2)
415-
bind_top_down(IP, IGMP, proto=2, ttl=1, tox=0xC0)
415+
bind_top_down(IP, IGMP, proto=2, ttl=1, tos=0xC0)
416416

417417

418418
def _igmp_mq_addr(pkt):
@@ -455,14 +455,20 @@ def igmp_join(gaddr: str, version=2, psrc=None, iface=None):
455455

456456

457457
@conf.commands.register
458-
def igmp_leave(gaddr: str, psrc=None, iface=None):
458+
def igmp_leave(gaddr: str, version=2, psrc=None, iface=None):
459459
"""
460460
Send a IGMP Leave Group to leave a multicast group
461461
462462
:param gaddr: the IPv4 of the group to leave
463463
:param psrc: (optional) the source IP
464464
"""
465-
send(IP(src=psrc) / IGMPv2_LG(gaddr=gaddr), iface=iface)
465+
if version == 1:
466+
raise ValueError("IGMPv1 does not include a mechanism to leave !")
467+
elif version == 2:
468+
pkt = IP(src=psrc) / IGMPv2_LG(gaddr=gaddr)
469+
elif version == 3:
470+
pkt = IP(src=psrc) / IGMPv3_MR(records=[IGMPv3_MR_Group(rtype=3, maddr=gaddr)])
471+
send(pkt, iface=iface)
466472

467473

468474
class IGMPMQResult(PacketList):

scapy/layers/kerberos.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,19 @@
9898
FieldLenField,
9999
FlagsField,
100100
IntEnumField,
101+
IntField,
101102
LEIntEnumField,
102-
LenField,
103103
LEShortEnumField,
104104
LEShortField,
105+
LenField,
105106
LongField,
106107
MayEnd,
107108
MultipleTypeField,
108109
PacketField,
109110
PacketLenField,
110111
PacketListField,
111112
PadField,
113+
ScalingField,
112114
ShortEnumField,
113115
ShortField,
114116
StrField,
@@ -2848,10 +2850,55 @@ def answers(self, other):
28482850
}
28492851

28502852

2853+
class DOMAIN_PASSWORD_INFORMATION(Packet):
2854+
# [MS-SAMR] sect 2.2.3.5
2855+
fields_desc = [
2856+
IntField("MinPasswordLength", 0),
2857+
IntField("PasswordHistoryLength", 0),
2858+
FlagsField(
2859+
"PasswordProperties",
2860+
0,
2861+
32,
2862+
{
2863+
0x00000001: "DOMAIN_PASSWORD_COMPLEX",
2864+
0x00000002: "DOMAIN_PASSWORD_NO_ANON_CHANGE",
2865+
0x00000004: "DOMAIN_PASSWORD_NO_CLEAR_CHANGE",
2866+
0x00000008: "DOMAIN_LOCKOUT_ADMINS",
2867+
0x00000010: "DOMAIN_PASSWORD_STORE_CLEARTEXT",
2868+
0x00000020: "DOMAIN_REFUSE_PASSWORD_CHANGE",
2869+
0x00000040: "DOMAIN_NO_LM_OWF_CHANGE",
2870+
},
2871+
),
2872+
ScalingField("MaxPasswordAge", 30 * 24 * 3600, scaling=1 / 1e7, fmt="!Q"),
2873+
ScalingField("MinPasswordAge", 0, scaling=1 / 1e7, fmt="!Q"),
2874+
]
2875+
2876+
2877+
class KPasswdResult(Packet):
2878+
# This is guessed from looking at MIT's implementation + ntsecapi.h
2879+
fields_desc = [
2880+
ShortField("PasswordInfoValid", 0),
2881+
PacketField(
2882+
"DomainPasswordInfo",
2883+
DOMAIN_PASSWORD_INFORMATION(),
2884+
DOMAIN_PASSWORD_INFORMATION,
2885+
),
2886+
]
2887+
2888+
2889+
class _KPasswdRepDataResult_Field(StrField):
2890+
def m2i(self, pkt, s):
2891+
val = super(_KPasswdRepDataResult_Field, self).m2i(pkt, s)
2892+
if len(val or b"") == 30:
2893+
# A 30 octets blob is most likely the AD policy block
2894+
return KPasswdResult(val)
2895+
return val
2896+
2897+
28512898
class KPasswdRepData(Packet):
28522899
fields_desc = [
28532900
ShortEnumField("resultCode", 0, KPASSWD_RESULTS),
2854-
StrField("resultString", ""),
2901+
_KPasswdRepDataResult_Field("resultString", ""),
28552902
]
28562903

28572904

test/scapy/layers/igmp.uts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ b=IP(src="1.2.3.4")
1111
c=IGMP(gaddr="0.0.0.0")
1212
x = a/b/c
1313
assert x.mrcode == 20
14+
assert x[IP].tos == 0xc0
1415
assert x[IP].dst == "224.0.0.1"
1516

1617
= Build IGMP - Custom membership
@@ -20,6 +21,7 @@ b=IP(src="1.2.3.4")
2021
c=IGMP(gaddr="224.0.1.2")
2122
x = a/b/c
2223
assert x.mrcode == 20
24+
assert x[IP].tos == 0xc0
2325
assert x[IP].dst == "224.0.1.2"
2426

2527
= Build IGMP - LG
@@ -31,17 +33,20 @@ x = a/b/c
3133
x = Ether(bytes(x))
3234
assert x.dst == "01:00:5e:00:00:02"
3335
assert x.mrcode == 0
36+
assert x[IP].tos == 0xc0
3437
assert x[IP].dst == "224.0.0.2"
3538

3639
= Change IGMP params
3740

3841
x = Ether(src="00:01:02:03:04:05")/IP()/IGMP()
3942
assert x.mrcode == 20
43+
assert x[IP].tos == 0xc0
4044
assert x[IP].dst == "224.0.0.1"
4145

4246
x = Ether(src="00:01:02:03:04:05")/IP()/IGMP(gaddr="224.2.3.4", type=0x12)
4347
x.mrcode = 1
4448
x = Ether(raw(x))
49+
assert x[IP].tos == 0xc0
4550
assert x.mrcode == 1
4651

4752
x.gaddr = "224.3.2.4"

0 commit comments

Comments
 (0)