Closed krizhanovsky closed 3 years ago
The problem is in test suit, in scapy_ssl_tls layer actually. Multiple issues happens there and all of them trigger different issues.
First is dissecting issue: of encrypted Finished messages. They are formed in the next way:
TLSRecord: [
type: Handshake
version: 0x0303
length: 40
payload: [
iv[8]
EncryptedFinished
mac
]
]
How the scapy dissects it:
TlsHandshakes
container is used to dissect the payload.TlsHandshake
objects are dissected one-by-one using fields_desc
: 1-byte type
and 3-byte length
TlsHandshake
scapy checks. is it encrypted, or not. The check is awfully simple and buggy: if the length
is bigger than dissected payload, then the message is encrypted.This simple check cause multiple issues. First, Finished message from Tempesta is not dissected correctly: iv
has zero value, that is perfectly dissected into two TlsHandshake
messages with zero type and zero length, the rest of the message is dissected as third TlsHandshake
message. Thus Finished message is never recognized by scapy. This is a reason for two issues: wrong checksum is sent by scapy on abbreviated handshake, since cleartext, not cipher text of Finished message must be used in the checksum calculation; and checksum of servers Finished message on Full handshake is never validated.
In the same time, scapy is happy with openssl servers, because openssl sends randomly generated iv
. But in very unlikely situations, a very small or zero length can be parsed out of iv
and the same situation as above will happen with openssl-based server.
Also a linked issue was caught: validaton of openssls Finished message may fail. Replace port 443
in tls.test_tls_tickets.TlsTicketTest.test_empty_ticket
test with a port of openssl server, e.g. nginx. and star the test. A UserWarning: Verification of GCM tag failed: MAC check failed
message may appear. Here is the trace back:
test_empty_ticket (tls.test_tls_tickets.TlsTicketTest) ... /home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls_crypto.py:1094: UserWarning: Verification of GCM tag failed: MAC check failed
warnings.warn("Verification of GCM tag failed: %s" % why)
File "./run_tests.py", line 303, in <module>
result = testRunner.run(testsuite)
File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
test(result)
File "/usr/lib/python2.7/unittest/suite.py", line 70, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.7/unittest/suite.py", line 108, in run
test(result)
File "/usr/lib/python2.7/unittest/case.py", line 393, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.7/unittest/case.py", line 329, in run
testMethod()
File "/home/user/tempesta-test/tls/test_tls_tickets.py", line 85, in test_empty_ticket
res = hs.do_12()
File "/home/user/tempesta-test/tls/handshake.py", line 431, in do_12
if not self._do_12_hs(fuzzer):
File "/home/user/tempesta-test/tls/handshake.py", line 337, in _do_12_hs
resp = self.send_recv(msg1)
File "/home/user/tempesta-test/tls/handshake.py", line 189, in send_recv
resp = self.sock.recvall(timeout=self.io_to)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1298, in recvall
records = TLS("".join(resp), ctx=self.tls_ctx, _origin=self._get_pkt_origin('in'))
File "/usr/local/lib/python2.7/dist-packages/scapy/base_classes.py", line 266, in __call__
i.__init__(*args, **kargs)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1332, in __init__
Packet.__init__(self, *args, **fields)
File "/usr/local/lib/python2.7/dist-packages/scapy/packet.py", line 158, in __init__
self.dissect(_pkt)
File "/usr/local/lib/python2.7/dist-packages/scapy/packet.py", line 875, in dissect
s = self.do_dissect(s)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1363, in do_dissect
payload = self.do_decrypt_payload(payload)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1382, in do_decrypt_payload
record.content_type)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls_crypto.py", line 1095, in decrypt
traceback.print_stack()
File "./run_tests.py", line 303, in <module>
result = testRunner.run(testsuite)
File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
test(result)
File "/usr/lib/python2.7/unittest/suite.py", line 70, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.7/unittest/suite.py", line 108, in run
test(result)
File "/usr/lib/python2.7/unittest/case.py", line 393, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.7/unittest/case.py", line 329, in run
testMethod()
File "/home/user/tempesta-test/tls/test_tls_tickets.py", line 85, in test_empty_ticket
res = hs.do_12()
File "/home/user/tempesta-test/tls/handshake.py", line 433, in do_12
return self._do_12_req(fuzzer)
File "/home/user/tempesta-test/tls/handshake.py", line 414, in _do_12_req
resp = self.send_recv(tls.TLSPlaintext(data=req))
File "/home/user/tempesta-test/tls/handshake.py", line 189, in send_recv
resp = self.sock.recvall(timeout=self.io_to)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1298, in recvall
records = TLS("".join(resp), ctx=self.tls_ctx, _origin=self._get_pkt_origin('in'))
File "/usr/local/lib/python2.7/dist-packages/scapy/base_classes.py", line 266, in __call__
i.__init__(*args, **kargs)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1332, in __init__
Packet.__init__(self, *args, **fields)
File "/usr/local/lib/python2.7/dist-packages/scapy/packet.py", line 158, in __init__
self.dissect(_pkt)
File "/usr/local/lib/python2.7/dist-packages/scapy/packet.py", line 875, in dissect
s = self.do_dissect(s)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1363, in do_dissect
payload = self.do_decrypt_payload(payload)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls.py", line 1382, in do_decrypt_payload
record.content_type)
File "/home/user/tempesta-test/tls/scapy_ssl_tls/ssl_tls_crypto.py", line 1095, in decrypt
traceback.print_stack()
Reverting eb4700fe55b392297b9e00b77e839aad58bb9c3f fixes this issue. But the message appears for Tepesta responses then. Looks like the origin issue in reverted patch was not linked to incorrect nonce, but it intriduces access to memory out of dissected data and that is why it works for tempesta, where the diisection is buggy, and fails for nginx with correct dissection
It seems there is some bug in scapy-ssl_tls checksumming: