diff --git a/prober.py b/prober.py index 0360ee3..e850376 100755 --- a/prober.py +++ b/prober.py @@ -72,6 +72,9 @@ CompressOnly(), CompressOnly12(), CompressOnly12PFS(), + ExtensionsUnderflow(), + ExtensionsUnderflow12(), + ExtensionsUnderflow12PFS(), DoubleClientHello(), DoubleClientHello12(), DoubleClientHello12PFS(), diff --git a/probes.py b/probes.py index ec13435..b60ca43 100644 --- a/probes.py +++ b/probes.py @@ -311,6 +311,35 @@ class InvalidExtLength12PFS(InvalidExtLength, InvalidSessionID12PFS): pass +class ExtensionsUnderflow(InvalidSessionID): + '''Send hello with data length lower than stated size''' + + def make_hello_payload(self, version, cipher_suites): + ciphers = struct.pack('>H{0}H'.format(len(cipher_suites)), + len(cipher_suites) * 2, *cipher_suites) + hello = (struct.pack('>H32sB', + version, + b'01234567890123456789012345678901', + 0) + + ciphers + b'\x01\x00' + b'\x00\x01' # extensions length, just one byte + b'\xff\x01' # extension ID - secure renego indication + b'\x00\x01' # secure renego indication ext length + b'\x00') # valid payload for extension + + return hello + + +class ExtensionsUnderflow12(ExtensionsUnderflow, InvalidSessionID12): + '''As in ExtensionsUnderflow but in TLSv1.2 hello''' + pass + + +class ExtensionsUnderflow12PFS(ExtensionsUnderflow, InvalidSessionID12PFS): + '''As in ExtensionsUnderflow but in PFS TLSv1.2 hello''' + pass + + class EmptyCompression(InvalidSessionID): '''Send hello with no compression methods''' diff --git a/tests/test_probes.py b/tests/test_probes.py index d1acc35..2f4c012 100644 --- a/tests/test_probes.py +++ b/tests/test_probes.py @@ -430,6 +430,73 @@ def test_test(self): b"\x00"]) +class TestExtensionsUnderflow(unittest.TestCase): + def test_test(self): + probe = ExtensionsUnderflow() + sock = MockSock() + + probe.test(sock) + + self.assertEqual(sock.sent_data, + [b'\x16\x03\x01\x00@' + b'\x01\x00\x00<' + b'\x03\x01' + + RANDOM_STR + + b'\x00' + b'\x00\x0e' + + DEFAULT_CIPHERS_STR + + b'\x01\x00' + b'\x00\x01' + b'\xff\x01' + b'\x00\x01' + b'\x00']) + + +class TestExtensionsUnderflow12(unittest.TestCase): + def test_test(self): + probe = ExtensionsUnderflow12() + sock = MockSock() + + probe.test(sock) + + self.assertEqual(sock.sent_data, + [b'\x16\x03\x01\x00X' + b'\x01\x00\x00T' + b'\x03\x03' + + RANDOM_STR + + b'\x00' + b'\x00&' + + DEFAULT_12_CIPHERS_STR + + b'\x01\x00' + b'\x00\x01' + b'\xff\x01' + b'\x00\x01' + b'\x00']) + + +class TestExtensionsUnderflow12PFS(unittest.TestCase): + def test_test(self): + probe = ExtensionsUnderflow12PFS() + sock = MockSock() + + probe.test(sock) + + self.maxDiff = None + self.assertEqual(sock.sent_data, + [b"\x16\x03\x01\x00\x90" + b"\x01\x00\x00\x8c" + b"\x03\x03" + + RANDOM_STR + + b"\x00" + b"\x00^" + + DEFAULT_PFS_CIPHERS_STR + + b"\x01\x00" + b'\x00\x01' + b'\xff\x01' + b'\x00\x01' + b'\x00']) + + class TestEmptyCompression(unittest.TestCase): def test_test(self): probe = EmptyCompression()