Skip to content

Commit 4e343bb

Browse files
authored
Fix (SSL)StreamSocket handling of underflowed data. (#4315)
Also remove a HTTP 'iptables' option which is terrible.
1 parent ce7596d commit 4e343bb

File tree

3 files changed

+30
-73
lines changed

3 files changed

+30
-73
lines changed

scapy/layers/http.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import struct
4848
import subprocess
4949

50-
from scapy.base_classes import Net
5150
from scapy.compat import plain_str, bytes_encode
5251

5352
from scapy.config import conf
@@ -694,7 +693,7 @@ def guess_payload_class(self, payload):
694693

695694
def http_request(host, path="/", port=80, timeout=3,
696695
display=False, verbose=0,
697-
raw=False, iptables=False, iface=None,
696+
raw=False, iface=None,
698697
**headers):
699698
"""Util to perform an HTTP request, using the TCP_client.
700699
@@ -706,9 +705,6 @@ def http_request(host, path="/", port=80, timeout=3,
706705
:param raw: opens a raw socket instead of going through the OS's TCP
707706
socket. Scapy will then use its own TCP client.
708707
Careful, the OS might cancel the TCP connection with RST.
709-
:param iptables: when raw is enabled, this calls iptables to temporarily
710-
prevent the OS from sending TCP RST to the host IP.
711-
On Linux, you'll almost certainly need this.
712708
:param iface: interface to use. Changing this turns on "raw"
713709
:param headers: any additional headers passed to the request
714710
@@ -730,11 +726,6 @@ def http_request(host, path="/", port=80, timeout=3,
730726
if iface is not None:
731727
raw = True
732728
if raw:
733-
# Use TCP_client on a raw socket
734-
iptables_rule = "iptables -%c INPUT -s %s -p tcp --sport 80 -j DROP"
735-
if iptables:
736-
host = str(Net(host))
737-
assert os.system(iptables_rule % ('A', host)) == 0
738729
sock = TCP_client.tcplink(HTTP, host, port, debug=verbose,
739730
iface=iface)
740731
else:
@@ -751,9 +742,6 @@ def http_request(host, path="/", port=80, timeout=3,
751742
)
752743
finally:
753744
sock.close()
754-
if raw and iptables:
755-
host = str(Net(host))
756-
assert os.system(iptables_rule % ('D', host)) == 0
757745
if ans:
758746
if display:
759747
if Raw not in ans:

scapy/supersocket.py

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
from scapy.error import warning, log_runtime
2828
from scapy.interfaces import network_name
2929
from scapy.packet import Packet, NoPayload
30-
import scapy.packet
3130
from scapy.plist import (
3231
PacketList,
3332
SndRcvList,
@@ -438,23 +437,22 @@ def __init__(self,
438437
self.rcvcls = streamcls(basecls or conf.raw_layer)
439438
self.metadata: Dict[str, Any] = {}
440439
self.streamsession: Dict[str, Any] = {}
441-
self.MTU = MTU
440+
self._buf = b""
442441
super(StreamSocket, self).__init__(sock, basecls=basecls)
443442

444443
def recv(self, x=None, **kwargs):
445444
# type: (Optional[int], Any) -> Optional[Packet]
446445
if x is None:
447-
x = self.MTU
446+
x = MTU
448447
# Block but in PEEK mode
449448
data = self.ins.recv(x, socket.MSG_PEEK)
450449
if data == b"":
451450
raise EOFError
452451
x = len(data)
453-
pkt = self.rcvcls(data, self.metadata, self.streamsession)
452+
pkt = self.rcvcls(self._buf + data, self.metadata, self.streamsession)
454453
if pkt is None: # Incomplete packet.
455-
if len(data) == self.MTU: # Bigger than MTU. Increase
456-
self.MTU *= 2
457-
return None
454+
self._buf += self.ins.recv(x)
455+
return self.recv(x)
458456
self.metadata.clear()
459457
# Strip any madding
460458
pad = pkt.getlayer(conf.padding_layer)
@@ -471,41 +469,30 @@ def recv(self, x=None, **kwargs):
471469
class SSLStreamSocket(StreamSocket):
472470
desc = "similar usage than StreamSocket but specialized for handling SSL-wrapped sockets" # noqa: E501
473471

472+
# Basically StreamSocket but we can't PEEK
473+
474474
def __init__(self, sock, basecls=None):
475475
# type: (socket.socket, Optional[Type[Packet]]) -> None
476-
self._buf = b""
476+
from scapy.sessions import TCPSession
477+
self.sess = TCPSession(app=True)
477478
super(SSLStreamSocket, self).__init__(sock, basecls)
478479

479480
# 65535, the default value of x is the maximum length of a TLS record
480481
def recv(self, x=None, **kwargs):
481482
# type: (Optional[int], **Any) -> Optional[Packet]
482483
if x is None:
483484
x = MTU
484-
pkt = None # type: Optional[Packet]
485-
if self._buf != b"":
486-
try:
487-
pkt = self.basecls(self._buf, **kwargs)
488-
except Exception:
489-
# We assume that the exception is generated by a buffer underflow # noqa: E501
490-
pass
491-
485+
# Block
486+
data = self.ins.recv(x)
487+
try:
488+
pkt = self.sess.process(data, cls=self.basecls) # type: ignore
489+
except struct.error:
490+
# Buffer underflow
491+
pkt = None
492+
if data == b"" and not pkt:
493+
raise EOFError
492494
if not pkt:
493-
buf = self.ins.recv(x)
494-
if len(buf) == 0:
495-
raise socket.error((100, "Underlying stream socket tore down"))
496-
self._buf += buf
497-
498-
x = len(self._buf)
499-
pkt = self.basecls(self._buf, **kwargs)
500-
if pkt is not None:
501-
pad = pkt.getlayer(conf.padding_layer)
502-
503-
if pad is not None and pad.underlayer is not None:
504-
del pad.underlayer.payload
505-
while pad is not None and not isinstance(pad, scapy.packet.NoPayload): # noqa: E501
506-
x -= len(pad.load)
507-
pad = pad.payload
508-
self._buf = self._buf[x:]
495+
return self.recv(x)
509496
return pkt
510497

511498

test/regression.uts

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3663,7 +3663,7 @@ class MockSocket(object):
36633663
self.l = [ b'\x00\x00\x00\x01', b'\x00\x00\x00\x02', b'\x00\x00\x00\x03' ]
36643664
def recv(self, x):
36653665
if len(self.l) == 0:
3666-
raise socket.error(100, 'EOF')
3666+
return b""
36673667
return self.l.pop(0)
36683668
def fileno(self):
36693669
return -1
@@ -3691,7 +3691,7 @@ assert p.data == 3
36913691
try:
36923692
ss.recv()
36933693
ret = False
3694-
except socket.error:
3694+
except EOFError:
36953695
ret = True
36963696

36973697
assert ret
@@ -3705,7 +3705,7 @@ class MockSocket(object):
37053705
self.l = [ b'\x00\x00\x00\x01\x00\x00\x00\x02', b'\x00\x00\x00\x03\x00\x00\x00\x04' ]
37063706
def recv(self, x):
37073707
if len(self.l) == 0:
3708-
raise socket.error(100, 'EOF')
3708+
return b""
37093709
return self.l.pop(0)
37103710
def fileno(self):
37113711
return -1
@@ -3735,7 +3735,7 @@ assert p.data == 4
37353735
try:
37363736
ss.recv()
37373737
ret = False
3738-
except socket.error:
3738+
except EOFError:
37393739
ret = True
37403740

37413741
assert ret
@@ -3749,7 +3749,7 @@ class MockSocket(object):
37493749
self.l = [ b'\x00\x00', b'\x00\x01', b'\x00\x00\x00', b'\x02', b'\x00\x00', b'\x00', b'\x03' ]
37503750
def recv(self, x):
37513751
if len(self.l) == 0:
3752-
raise socket.error(100, 'EOF')
3752+
return b""
37533753
return self.l.pop(0)
37543754
def fileno(self):
37553755
return -1
@@ -3768,40 +3768,22 @@ class TestPacket(Packet):
37683768
s = MockSocket()
37693769
ss = SSLStreamSocket(s, basecls=TestPacket)
37703770

3771-
try:
3772-
p = ss.recv()
3773-
ret = False
3774-
except:
3775-
ret = True
3776-
3777-
assert ret
37783771
p = ss.recv()
37793772
assert p.data == 1
3780-
try:
3781-
p = ss.recv()
3782-
ret = False
3783-
except:
3784-
ret = True
37853773

3786-
assert ret
37873774
p = ss.recv()
37883775
assert p.data == 2
3789-
try:
3790-
p = ss.recv()
3791-
ret = False
3792-
except:
3793-
ret = True
37943776

3795-
assert ret
3777+
p = ss.recv()
3778+
assert p.data == 3
3779+
37963780
try:
3797-
p = ss.recv()
3781+
ss.recv()
37983782
ret = False
3799-
except:
3783+
except EOFError:
38003784
ret = True
38013785

38023786
assert ret
3803-
p = ss.recv()
3804-
assert p.data == 3
38053787

38063788

38073789
############

0 commit comments

Comments
 (0)