def skip_server_handshake(sock, timeout): end_time = time.time() + timeout hs_struct = struct.Struct(b'!BBH') for i in range(0, 5): record, error = read_record(sock, timeout) timeout = end_time - time.time() if not record: raise Failure('Unexpected server handshake! ' + str(error)) content_type, sslver_num, fragment = record if content_type != 22: raise Failure('Expected handshake type, got ' + str(content_type)) sslver = '{0:02x} {1:02x}'.format(sslver_num >> 8, sslver_num & 0xFF) off = 0 # Records may consist of multiple handshake messages while off + hs_struct.size <= len(fragment): hs_type, len_high, len_low = hs_struct.unpack_from(fragment, off) if off + len_low > len(fragment): raise Failure('Illegal handshake length!') off += hs_struct.size + len_low # Server handshake is complete after ServerHelloDone if hs_type == 14: # Ready to check for vulnerability return sslver raise Failure('Too many handshake messages')
def read_hb_response(sock, timeout): end_time = time.time() + timeout memory = bytearray() hb_len = 1 # Will be initialized after first heartbeat read_error = None alert = None while len(memory) < hb_len and timeout > 0: record, read_error = read_record(sock, timeout, partial=True) if not record: break record_type, _, fragment = record if record_type == 24: if not memory: # First Heartbeat # Check for enough room for type + len if len(fragment) < 3: if read_error: # Ignore error due to partial read break raise Failure('Response too small') # Sanity check, should not happen with OpenSSL if fragment[0:1] != b'\2': raise Failure('Expected Heartbeat in first response') hb_len, = struct.unpack_from(b'!H', fragment, 1) memory += fragment[3:] else: # Heartbeat continuation memory += fragment elif record_type == 21 and len(fragment) == 2: # Alert alert = fragment break else: # Cannot tell whether vulnerable or not! raise Failure('Unexpected record type {0}'.format(record_type)) timeout = end_time - time.time() # Drop heartbeat padding that is included in buffer if len(memory) - 16 == hb_len: del memory[hb_len:] # Check for Alert (sent by NSS) if alert: lvl, desc = alert[0:1], ord(alert[1:2]) lvl = 'Warning' if lvl == 1 else 'Fatal' print('Got Alert, level={0}, description={1}'.format(lvl, desc)) if not memory: print('Not vulnerable! (Heartbeats disabled or not OpenSSL)') return None # Do not print error if we have memory, server could be crashed, etc. if read_error and not memory: print('Did not receive heartbeat response! ' + str(read_error)) return memory
def readline_expect(reader, expected, what=None): line = reader.readline() if not line.upper().startswith(expected.upper()): if what is None: what = expected raise Failure('Expected ' + expected + ', got ' + line) return line
def do_serverhello(self): # Read TLS record header content_type, ver, rec_len = self.recv_s(b'>BHH', 'TLS record') # Session-ID length (1 byte) starts at offset 38 self.expect(rec_len >= 39, 'Illegal handshake packet') if content_type == 0x80: # SSLv2 (assume length < 256) raise Failure('SSL 2.0 clients cannot be tested') else: self.expect(content_type == 22, 'Expected Handshake type') # Read handshake hnd = self.request.recv(rec_len) self.expect(len(hnd) == rec_len, 'Unable to read handshake') hnd_type, len_high, len_low, ver = struct.unpack(b'>BBHH', hnd[:6]) self.expect(hnd_type == 1, 'Expected Client Hello') # hnd[6:6+32] is Random off = 6 + 32 sid_len, = struct.unpack(b'B', hnd[off:off + 1]) off += 1 + sid_len # Skip length and SID # Enough room for ciphers? self.expect(rec_len - off >= 4, 'Illegal handshake packet (2)') ciphers_len = struct.unpack(b'<H', hnd[off:off + 2]) off += 2 # The first cipher is fine... cipher = bytearray(hnd[off:off + 2]) self.sslver = '{0:02x} {1:02x}'.format(ver >> 8, ver & 0xFF) # (1) Handshake: ServerHello self.request.sendall(make_hello(self.sslver, cipher))
def prepare_ftp(cls, sock): reader = Linereader(sock) tls = False cls.readline_expect(reader, '220 ', 'FTP greeting') sock.sendall(b'FEAT\r\n') cls.readline_expect(reader, '211-', 'FTP features') for i in range(0, 64): line = reader.readline().upper() if line.startswith(' AUTH TLS'): tls = True if line.startswith('211'): break if not tls: raise Failure('AUTH TLS not supported') sock.sendall(b'AUTH TLS\r\n') cls.readline_expect(reader, '234 ', 'AUTH TLS ack')
def prepare_smtp(cls, sock): reader = Linereader(sock) tls = False # Server greeting cls.readline_expect(reader, '220 ', 'SMTP banner') sock.sendall(b'EHLO pacemaker\r\n') # Assume no more than 16 extensions for i in range(0, 16): line = cls.readline_expect(reader, '250', 'extension') if line[4:].upper().startswith('STARTTLS'): tls = True if line[3] == ' ': break if not tls: raise Failure('STARTTLS not supported') sock.sendall(b'STARTTLS\r\n') cls.readline_expect(reader, '220 ', 'STARTTLS acknowledgement')
def expect(self, cond, what): if not cond: raise Failure(what)