From 7ba522dfbefa19a7dc49536d9cdb42bbd89c0a03 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Mon, 10 Aug 2015 16:27:09 -0700 Subject: [PATCH 01/21] Add peek_request and peek_response These handler methods get a first pass at socket data before the consuming recv. These can be used to, for example, hook into libraries that read from sockets themselves or otherwise consume the data in the handler. This is the start of moving all the SSL MiTM code out of Connection and into SSL specific handlers as well as adding handlers dynamically. This change is pretty straightfoward except for the work to be done to support peek on mitm'd connections. pyOpenSSL does not support peek so we need to read into a buffer and read from that when peeking. This requires some pretty hacky code to keep select working correct on a connection where there is data remaning in the peek buffer as the underlying socket is no longer ready to be selected for reading (as it would be with real MSG_PEEK) so in that case we use a different fd for select that is always ready for reading. --- nogotofail/mitm/connection/connection.py | 82 +++++++++++++++++---- nogotofail/mitm/connection/handlers/base.py | 18 +++++ 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/nogotofail/mitm/connection/connection.py b/nogotofail/mitm/connection/connection.py index 6d4e7636..2a4d20a1 100644 --- a/nogotofail/mitm/connection/connection.py +++ b/nogotofail/mitm/connection/connection.py @@ -28,35 +28,81 @@ import os class ConnectionWrapper(object): - """Wrapper around OpenSSL's Connection object to make recv act like socket.recv() + """Wrapper around OpenSSL's Connection object to make it act like a real socket. """ def __init__(self, connection): self._connection = connection + self.buffer = "" + self._is_short_send = False def __getattr__(self, name): return getattr(self._connection, name) - def recv(self, size): + def recv(self, size, flags=0): """Wrapper around pyOpenSSL's Connection.recv PyOpenSSL doesn't return "" on error like socket.recv does, instead it throws a SSL.ZeroReturnError or (-1, "Unexpected EOF") erorrs. Wrap recv so we don't have to deal with that noise. """ - buf = "" + if flags & socket.MSG_PEEK == 0: + return self._recv(size) + if len(self.buffer) >= size: + return self.buffer[:size] try: - buf = self._connection.recv(size) + self.buffer += self._recv(size - len(self.buffer)) + except SSL.WantReadError: + pass + return self.buffer[:size] + + def _recv(self, size): + if size <= len(self.buffer): + out = self.buffer[:size] + self.buffer = self.buffer[size:] + return out + buf = self.buffer + size -= len(buf) + try: + buf += self._connection.recv(size) except SSL.SysCallError as e: if e.args != (-1, "Unexpected EOF"): raise e except SSL.ZeroReturnError: pass + except SSL.WantReadError as e: + # Rethrow the WantRead if we really have no data + if not buf: + raise e except SSL.Error as e: if e.args != (-1, "Unexpected EOF"): raise e + self.buffer = "" return buf + def send(self, string): + sent = self._connection.send(string) + # Track short send state for our awful fileno hacks + self._is_short_send = sent != len(string) + return sent + + _always_read_fd = None + def always_read_fd(self): + """Return an fd that is always ready for read when passed to select.select. See fileno for why this is needed.""" + if ConnectionWrapper._always_read_fd: + return ConnectionWrapper._always_read_fd + ConnectionWrapper._always_read_fd = open("/dev/zero") + return ConnectionWrapper._always_read_fd + + def fileno(self): + # _AWFUL_ HACK to support MSG_PEEK without breaking select.select. + # If we read data with a peeking recv then return a fd that is always selectable on read to make sure the connection keeps flowing. + # Note that if the conneciton is handling a short send then we're only waiting for write not read, so use the underlying connection. + # Once the backlog is sent the connection will start trying to read again and we'll return the always_read_fd. + if self.buffer and not self._is_short_send: + return self.always_read_fd().fileno() + + return self._connection.fileno() def stub_verify(conn, cert, errno, errdepth, code): """We don't verify the server when we attempt a MiTM. @@ -411,17 +457,20 @@ def _handle_hello(self, client_hello): def _bridge_client(self): try: - # Check for a TLS client hello we might need to intercept - if not self.ssl: + try: client_request = self.client_socket.recv(65536, socket.MSG_PEEK) - if not client_request: - return False - # If a MiTM was attempted discard client_request, we used it - # for establishing a MiTM with the client. - if self._check_for_ssl(client_request): + handled = self.handler.peek_request(client_request) + if handled: return not self.closed - - try: + for handler in self.data_handlers: + if handler.peek_request(client_request): + return not self.closed + # Check for a TLS client hello we might need to intercept + if not self.ssl: + # If a MiTM was attempted discard client_request, we used it + # for establishing a MiTM with the client. + if self._check_for_ssl(client_request): + return not self.closed client_request = self.client_socket.recv(65536) except (socket.error, SSL.WantReadError): # recv can still time out even if select returned this socket @@ -456,6 +505,13 @@ def _bridge_client(self): def _bridge_server(self): try: try: + server_response = self.server_socket.recv(65536, socket.MSG_PEEK) + handled = self.handler.peek_response(server_response) + if handled: + return not self.closed + for handler in self.data_handlers: + if handler.peek_response(server_response): + return not self.closed server_response = self.server_socket.recv(65536) except (socket.error, SSL.WantReadError): # recv can still time out even if select returned this socket diff --git a/nogotofail/mitm/connection/handlers/base.py b/nogotofail/mitm/connection/handlers/base.py index 912c02c5..f4c8d99e 100644 --- a/nogotofail/mitm/connection/handlers/base.py +++ b/nogotofail/mitm/connection/handlers/base.py @@ -101,6 +101,24 @@ def on_ssl(self, client_hello): """ pass + def peek_request(self, request): + """Called with the data from a request _before_ it has been read from the socket. + + This can be used to prempt the socket recv and handle data yourself. + + Returns if the request should be considered handled and recv should not be called on the underlying socket + """ + return False + + def peek_response(self, response): + """Called with the data from a response _before_ it has been read from the socket. + + This can be used to prempt the socket recv and handle data yourself. + + Returns if the response should be considered handled and recv should not be called on the underlying socket + """ + return False + class BaseConnectionHandler(BaseHandler): From d85c43f8b81509af4ea4af78420b5b65201a8695 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Fri, 25 Sep 2015 17:32:58 -0700 Subject: [PATCH 02/21] Refactor TLS parsing to support fragmented records 1) tls.parse_tls now handles messages split over multiple records. If kwarg throw_on_incomplete is True then if more records are needed a tls.types.TlsMessageFragmentedError will be thrown, and if the record was incomplete a tls.types.TlsRecordIncompleteError will be thrown. 2) TlsRecord.to_bytes() will now split the wire representation into multiple TlsRecords if the size of the messages is greater than max_fragment_size (2**12). 3) Callers of the old parse_tls or TlsRecord.from_stream have been refactored to use the new method 4) Tests, :O --- nogotofail/mitm/connection/connection.py | 2 +- .../connection/handlers/connection/ccs.py | 2 +- .../handlers/connection/heartbleed.py | 13 +-- .../handlers/connection/serverkeyreplace.py | 20 ++-- nogotofail/mitm/util/tls/__init__.py | 2 +- nogotofail/mitm/util/tls/tls.py | 47 +++++--- nogotofail/mitm/util/tls/types/__init__.py | 2 + nogotofail/mitm/util/tls/types/errors.py | 33 ++++++ nogotofail/mitm/util/tls/types/handshake.py | 8 +- nogotofail/mitm/util/tls/types/record.py | 53 ++++++--- nogotofail/test/__init__.py | 15 +++ nogotofail/test/mitm/__init__.py | 15 +++ nogotofail/test/mitm/util/__init__.py | 15 +++ nogotofail/test/mitm/util/tls/__init__.py | 15 +++ .../test/mitm/util/tls/test_handshake.py | 104 ++++++++++++++++++ 15 files changed, 294 insertions(+), 52 deletions(-) create mode 100644 nogotofail/mitm/util/tls/types/errors.py create mode 100644 nogotofail/test/__init__.py create mode 100644 nogotofail/test/mitm/__init__.py create mode 100644 nogotofail/test/mitm/util/__init__.py create mode 100644 nogotofail/test/mitm/util/tls/__init__.py create mode 100644 nogotofail/test/mitm/util/tls/test_handshake.py diff --git a/nogotofail/mitm/connection/connection.py b/nogotofail/mitm/connection/connection.py index 6d4e7636..9f723523 100644 --- a/nogotofail/mitm/connection/connection.py +++ b/nogotofail/mitm/connection/connection.py @@ -360,7 +360,7 @@ def _check_for_ssl(self, client_request): Returns if client_request was used(and should not be sent to the server) """ # check for a TLS Client Hello - record = tls.parse_tls(client_request) + record, ignored = tls.parse_tls(client_request) client_hello = None if record: first = record.messages[0] diff --git a/nogotofail/mitm/connection/handlers/connection/ccs.py b/nogotofail/mitm/connection/handlers/connection/ccs.py index 83ee170a..7b15b6f3 100644 --- a/nogotofail/mitm/connection/handlers/connection/ccs.py +++ b/nogotofail/mitm/connection/handlers/connection/ccs.py @@ -42,7 +42,7 @@ def on_request(self, request): if not self.ssl or self.bridge: return request try: - record, size = TlsRecord.from_stream(request) + record, remaining = tls.parse_tls(request) message = record.messages[0] if not self.clienthello_handled: self.clienthello_handled = True diff --git a/nogotofail/mitm/connection/handlers/connection/heartbleed.py b/nogotofail/mitm/connection/handlers/connection/heartbleed.py index 953b448d..dcef7a5a 100644 --- a/nogotofail/mitm/connection/handlers/connection/heartbleed.py +++ b/nogotofail/mitm/connection/handlers/connection/heartbleed.py @@ -36,9 +36,9 @@ class ClientHeartbleedHandler(LoggingHandler): def on_request(self, request): # parse out request and check for heartbeat try: - index = 0 - while index < len(request): - record, size = tls.types.TlsRecord.from_stream(request[index:]) + remaining = request + while remaining: + record, remaining = tls.parse_tls(remaining) if record.content_type == TlsRecord.CONTENT_TYPE.HEARTBEAT: self.log(logging.CRITICAL, "Heartbleed response received") self.log_event( @@ -49,7 +49,6 @@ def on_request(self, request): self.connection.vuln_notify( util.vuln.VULN_TLS_CLIENT_HEARTBLEED) self.success = True - index += size except: pass return request @@ -66,11 +65,11 @@ def on_close(self, handler_initiated): def on_response(self, response): if self.first: try: - record, size = tls.types.TlsRecord.from_stream(response) + record, remaining = tls.parse_tls(response) version = record.version - response = (response[:size] + response = (record.to_bytes() + self.heartbleed % (version.to_bytes()) - + response[size:]) + + remaining) except: self.log(logging.INFO, "Failed to parse TLS record from server") self.first = False diff --git a/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py b/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py index aebec4f7..e24b94d3 100644 --- a/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py +++ b/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py @@ -18,6 +18,7 @@ from nogotofail.mitm.connection.handlers.connection import LoggingHandler from nogotofail.mitm.connection.handlers.connection import handlers from nogotofail.mitm.connection.handlers.store import handler +from nogotofail.mitm.util.tls import tls from nogotofail.mitm.util.tls.types import Alert, Extension, HandshakeMessage, OpaqueMessage, TlsRecord from nogotofail.mitm.event import connection @@ -56,7 +57,7 @@ def on_request(self, request): if not self.ssl: return request try: - record, size = TlsRecord.from_stream(request) + record, remaining = tls.parse_tls(request) message = record.messages[0] if not self.clienthello_adjusted: self.clienthello_adjusted = True @@ -124,9 +125,10 @@ def on_response(self, response): self.buffer = "" # Tamper with the ServerKeyExchange message. try: - index = 0 - while index < len(response): - record, size = TlsRecord.from_stream(response[index:]) + remaining = response + new_response = "" + while remaining: + record, remaining = tls.parse_tls(remaining, throw_on_incomplete=True) version = record.version for i, message in enumerate(record.messages): if (isinstance(message, HandshakeMessage) @@ -134,15 +136,15 @@ def on_response(self, response): HandshakeMessage.TYPE.SERVER_KEY_EXCHANGE)): tampered_record_bytes = ( self._tamper_with_server_key_exchange(record, i)) - response = (response[:index] + + response = (new_response + tampered_record_bytes + - response[index+size:]) + remaining) self.signature_tampered = True return response + else: + new_response += record.to_bytes() - index += size - - except ValueError: + except tls.types.TlsNotEnoughDataError: # Failed to parse TLS, this is probably due to a short read of a TLS # record. Buffer the response to try and get more data. self.buffer = response diff --git a/nogotofail/mitm/util/tls/__init__.py b/nogotofail/mitm/util/tls/__init__.py index 0c0b401b..ecc83920 100644 --- a/nogotofail/mitm/util/tls/__init__.py +++ b/nogotofail/mitm/util/tls/__init__.py @@ -13,5 +13,5 @@ See the License for the specific language governing permissions and limitations under the License. ''' -from tls import parse_tls import types +from tls import parse_tls diff --git a/nogotofail/mitm/util/tls/tls.py b/nogotofail/mitm/util/tls/tls.py index 98e53211..718d4094 100644 --- a/nogotofail/mitm/util/tls/tls.py +++ b/nogotofail/mitm/util/tls/tls.py @@ -16,20 +16,41 @@ import struct from nogotofail.mitm.util.tls import types +def parse_tls(message, throw_on_incomplete=False): + """Try and parse a TLS record. If the message is fragmented over multiple TLS records this + will return one TLS record with the defragmented payload -def parse_tls(message, enforce_length=True): - """Try and parse a TLS Record from message. + Arguments: + message -- wire representation of a TLS message. + throw_on_incomplete -- throw a TlsRecordIncompleteError or TlsMessageFragmentedError if + message is not complete, otherwise return None - Message should be the byte representation of a TLS Record. - Returns a nogotofail.mitm.util.tls.TlsRecord on success or None or error. - If enforce_length is True then parse_tls will return None if there are bytes - remaining in - message after parsing the record. + Returns (nogotofail.mitm.util.tls.TlsRecord, remaining_message) if message consumed + or None, message if parsing was unsuccessful """ + extra_fragment_data = "" + original_message = message try: - record, size = types.TlsRecord.from_stream(message) - if enforce_length and size != len(message): - return None - return record - except (IndexError, ValueError, struct.error) as e: - return None + while message: + try: + record, size = types.TlsRecord.from_stream(message, + previous_fragment_data=extra_fragment_data) + return record, message[size:] + except types.TlsMessageFragmentedError as e: + # If we're fragmented try and keep parsing + extra_fragment_data += e.fragment_data + message = message[e.data_consumed:] + # If we're fragmented but out of data error out + if not message: + if throw_on_incomplete: + raise e + else: + return None, original_message + except (IndexError, ValueError, struct.error): + return None, original_message + except types.TlsRecordIncompleteError as e: + if throw_on_incomplete: + raise e + else: + return None, original_message + return None, original_message diff --git a/nogotofail/mitm/util/tls/types/__init__.py b/nogotofail/mitm/util/tls/types/__init__.py index 9ab69668..b528eb1a 100644 --- a/nogotofail/mitm/util/tls/types/__init__.py +++ b/nogotofail/mitm/util/tls/types/__init__.py @@ -13,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. ''' +import parse +from errors import TlsNotEnoughDataError, TlsRecordIncompleteError, TlsMessageFragmentedError from cipher import Cipher from compression_method import CompressionMethod from simple import Version, Random diff --git a/nogotofail/mitm/util/tls/types/errors.py b/nogotofail/mitm/util/tls/types/errors.py new file mode 100644 index 00000000..3fb51dda --- /dev/null +++ b/nogotofail/mitm/util/tls/types/errors.py @@ -0,0 +1,33 @@ +r''' +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +class TlsNotEnoughDataError(Exception): + """Error in TLS parsing where the TLS record is so far valid but incomplete""" + pass + +class TlsRecordIncompleteError(TlsNotEnoughDataError): + """Error for when a TLS Record appears valid but is not enough data is present to parse + the record""" + def __init__(self, data_available, record_size): + self.data_available = data_available + self.record_size = record_size + +class TlsMessageFragmentedError(TlsNotEnoughDataError): + """Error for when not enough data is present to parse a TLS message because of + fragmentation""" + def __init__(self, fragment_data, data_consumed): + self.fragment_data = fragment_data + self.data_consumed = data_consumed diff --git a/nogotofail/mitm/util/tls/types/handshake.py b/nogotofail/mitm/util/tls/types/handshake.py index 0eff37b0..627abf67 100644 --- a/nogotofail/mitm/util/tls/types/handshake.py +++ b/nogotofail/mitm/util/tls/types/handshake.py @@ -16,6 +16,7 @@ from nogotofail.mitm.util import Constants from nogotofail.mitm.util.tls.types import parse from nogotofail.mitm.util.tls.types import Cipher, Extension, Version, Random, CompressionMethod +from nogotofail.mitm.util.tls.types import TlsNotEnoughDataError import base64 import struct @@ -140,7 +141,7 @@ def _parse_certificate(buf): length = struct.unpack_from("!I", "\x00" + buf[:3])[0] data = buf[3:3+length] if len(data) != length: - raise ValueError("Not enough data in buffer to parse certificate need %d bytes but read %d" % (length, len(data))) + raise ValueError("Not enough data to parse certificate") return data, length + 3 @staticmethod @@ -294,9 +295,12 @@ def from_stream(body): # Parse The Handshake. Length is 24bits which struct doesn't support # well. msg_type, length = struct.unpack("!BI", body[0] + "\x00" + body[1:4]) + # sanity check + if msg_type not in name_map: + raise ValueError("Unknown HanshakeMessage type %d" % msg_type) body = body[4:4 + length] if length != len(body): - raise ValueError("Not enough data in body") + raise TlsNotEnoughDataError() # Check this is a supported type type = HandshakeMessage.type_map.get(msg_type, OpaqueMessage) obj, size = type.from_stream(body) diff --git a/nogotofail/mitm/util/tls/types/record.py b/nogotofail/mitm/util/tls/types/record.py index dc7959f0..ea9553ab 100644 --- a/nogotofail/mitm/util/tls/types/record.py +++ b/nogotofail/mitm/util/tls/types/record.py @@ -16,6 +16,7 @@ from nogotofail.mitm.util import Constants from nogotofail.mitm.util.tls.types import parse from nogotofail.mitm.util.tls.types import HandshakeMessage, Version, ChangeCipherSpec, Alert +from nogotofail.mitm.util.tls.types import TlsRecordIncompleteError, TlsMessageFragmentedError, TlsNotEnoughDataError import base64 import struct @@ -46,34 +47,50 @@ def __init__(self, content_type, version, messages): self.messages = messages @staticmethod - def from_stream(body): + def from_stream(body, previous_fragment_data=""): # Parse the TLS Record content_type, version_major, version_minor, length = ( struct.unpack_from("!BBBH", body, 0)) + # Sanity checks + if version_major != 3 or version_minor > 3: + raise ValueError("Bad TLS Version for SSL3-TLS1.2 parsing") + if content_type not in name_map: + raise ValueError("Unknown content type %d" % content_type) + fragment = body[5:5 + length] - # Sanity check - if length != len(fragment): - raise ValueError("Not enough data in fragment") - # Check this is a Handshake message + original_fragment = fragment + # Merge in any old fragmented data + fragment = previous_fragment_data + fragment + if len(fragment) < length: + raise TlsRecordIncompleteError(len(fragment), length) + # Start parsing the objects from the record type = TlsRecord.type_map.get(content_type, OpaqueFragment) objs = [] - if fragment == "": - obj, size = type.from_stream(fragment) - objs.append(obj) - - while fragment != "": - obj, size = type.from_stream(fragment) - objs.append(obj) - fragment = fragment[size:] + try: + if fragment == "": + obj, size = type.from_stream(fragment) + objs.append(obj) + + while fragment != "": + obj, size = type.from_stream(fragment) + objs.append(obj) + fragment = fragment[size:] + except TlsNotEnoughDataError: + # In the event of not enough data throw what we have up to a higher + # level + raise TlsMessageFragmentedError(original_fragment, 5 + length) return TlsRecord(content_type, Version(version_major, version_minor), objs), 5 + length - def to_bytes(self): + def to_bytes(self, max_fragment_size = 2 ** 12): bytes = "".join([message.to_bytes() for message in self.messages]) - return (struct.pack("B", self.content_type) - + self.version.to_bytes() - + struct.pack("!H", len(bytes)) - + bytes) + # Fragment the record as needed + num_fragments = len(bytes)/max_fragment_size + fragments = [bytes[i: i + max_fragment_size] for i in range (0, len(bytes), max_fragment_size)] + return "".join([(struct.pack("B", self.content_type) + + self.version.to_bytes() + + struct.pack("!H", len(fragment)) + + fragment) for fragment in fragments]) def __str__(self): return ("TLS Record %s %s (%d)" diff --git a/nogotofail/test/__init__.py b/nogotofail/test/__init__.py new file mode 100644 index 00000000..2106176a --- /dev/null +++ b/nogotofail/test/__init__.py @@ -0,0 +1,15 @@ +r''' +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' diff --git a/nogotofail/test/mitm/__init__.py b/nogotofail/test/mitm/__init__.py new file mode 100644 index 00000000..2106176a --- /dev/null +++ b/nogotofail/test/mitm/__init__.py @@ -0,0 +1,15 @@ +r''' +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' diff --git a/nogotofail/test/mitm/util/__init__.py b/nogotofail/test/mitm/util/__init__.py new file mode 100644 index 00000000..2106176a --- /dev/null +++ b/nogotofail/test/mitm/util/__init__.py @@ -0,0 +1,15 @@ +r''' +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' diff --git a/nogotofail/test/mitm/util/tls/__init__.py b/nogotofail/test/mitm/util/tls/__init__.py new file mode 100644 index 00000000..2106176a --- /dev/null +++ b/nogotofail/test/mitm/util/tls/__init__.py @@ -0,0 +1,15 @@ +r''' +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' diff --git a/nogotofail/test/mitm/util/tls/test_handshake.py b/nogotofail/test/mitm/util/tls/test_handshake.py new file mode 100644 index 00000000..5cacdad0 --- /dev/null +++ b/nogotofail/test/mitm/util/tls/test_handshake.py @@ -0,0 +1,104 @@ +r''' +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' +import unittest +from nogotofail.mitm.util import tls + +# Basic client hello from `openssl s_client -tls1_2 -cipher HIGH` +BASIC_HELLO = ("FgMBASIBAAEeAwOVqPajcw+kjqqu6OY0g/k/UqYSjwdXHdiYwhfn+ZpNPgAAiMAwwCzAKMAkwBTA\n" + "CgCjAJ8AawBqADkAOACIAIfAGQCnAG0AOgCJwDLALsAqwCbAD8AFAJ0APQA1AITAEsAIABYAE8AX\n" + "ABvADcADAArAL8ArwCfAI8ATwAkAogCeAGcAQAAzADIARQBEwBgApgBsADQARsAxwC3AKcAlwA7A\n" + "BACcADwALwBBAP8BAABtAAsABAMAAQIACgA0ADIADgANABkACwAMABgACQAKABYAFwAIAAYABwAU\n" + "ABUABAAFABIAEwABAAIAAwAPABAAEQAjAAAADQAgAB4GAQYCBgMFAQUCBQMEAQQCBAMDAQMCAwMC\n" + "AQICAgMADwABAQ==\n").decode("base64") + + +# BASIC_HELLO split into two fragments +FRAGMENTED_BASIC_HELLO = ( + "FgMBADIBAAEeAwOVqPajcw+kjqqu6OY0g/k/UqYSjwdXHdiYwhfn+ZpNPgAAiMAwwCzAKMAkwBYD\n" + "AQDwFMAKAKMAnwBrAGoAOQA4AIgAh8AZAKcAbQA6AInAMsAuwCrAJsAPwAUAnQA9ADUAhMASwAgA\n" + "FgATwBcAG8ANwAMACsAvwCvAJ8AjwBPACQCiAJ4AZwBAADMAMgBFAETAGACmAGwANABGwDHALcAp\n" + "wCXADsAEAJwAPAAvAEEA/wEAAG0ACwAEAwABAgAKADQAMgAOAA0AGQALAAwAGAAJAAoAFgAXAAgA\n" + "BgAHABQAFQAEAAUAEgATAAEAAgADAA8AEAARACMAAAANACAAHgYBBgIGAwUBBQIFAwQBBAIEAwMB\n" + "AwIDAwIBAgICAwAPAAEB\n").decode("base64") + +class TestClientHelloParsing(unittest.TestCase): + + def check_basic_hello_record(self, record): + # Verify parsing was successful + self.assertIsNotNone(record, "parse_tls failed") + + # Check the record is sane + self.assertEqual(record.version.major, 3) + self.assertEqual(record.version.minor, 1) + self.assertEqual(record.content_type, record.CONTENT_TYPE.HANDSHAKE) + self.assertEqual(len(record.messages), 1) + + # Check the HandshakeMessage + message = record.messages[0] + self.assertIsInstance(message, tls.types.HandshakeMessage) + self.assertEqual(message.type, message.TYPE.CLIENT_HELLO) + + # Check the ClientHello itself + hello = message.obj + self.assertIsInstance(hello, tls.types.ClientHello) + self.assertEqual(hello.version.major, 3) + self.assertEqual(hello.version.minor, 3) + self.assertEqual(hello.session_id, []) + self.assertEqual(hello.random.bytes, + "730fa48eaaaee8e63483f93f52a6128f07571dd898c217e7f99a4d3e".decode("hex")) + # TODO: Check that the contents are correct/correct order + self.assertEqual(len(hello.ciphers), 68) + self.assertEqual(len(hello.extensions), 5) + for key in [35, 10, 11, 13, 15]: + self.assertTrue(key in hello.extensions) + + def test_parse_basic_hello(self): + record, remaining = tls.parse_tls(BASIC_HELLO) + self.assertIsNotNone(record) + self.assertEquals(remaining, "") + self.check_basic_hello_record(record) + + def test_parse_basic_hello_extra(self): + extra_bytes = "\x11\x22\x33\x44" + record, remaining = tls.parse_tls(BASIC_HELLO + extra_bytes) + self.assertIsNotNone(record) + self.assertEquals(remaining, extra_bytes) + self.check_basic_hello_record(record) + + def test_fragmented_basic_hello(self): + record, remaining = tls.parse_tls(FRAGMENTED_BASIC_HELLO) + self.assertIsNotNone(record) + self.assertEquals(remaining, "") + self.check_basic_hello_record(record) + + def test_fragmenting(self): + record, remaining = tls.parse_tls(BASIC_HELLO) + self.assertIsNotNone(record) + self.assertEquals(remaining, "") + # Fragment on purpose + bytes = record.to_bytes(max_fragment_size=50) + # sanity check this parses back out + parsed_rec, remaining = tls.parse_tls(bytes) + self.assertIsNotNone(parsed_rec) + self.assertEqual(remaining, "") + self.check_basic_hello_record(parsed_rec) + + # Now check that they got split by parsing for only one record + with self.assertRaises(tls.types.TlsNotEnoughDataError): + tls.types.TlsRecord.from_stream(bytes) + +if __name__ == "__main__": + unittest.main() From e57fb4ece76bf95a4bb756d80609c3087e7f91f0 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Thu, 20 Aug 2015 23:38:49 -0700 Subject: [PATCH 03/21] Add TlsRecordHandler base data handler TlsRecordHandler handles buffering of TlsRecords in a connection, handlers that want to do things based on records (eithe passive or replacing records) can extend this class and implement on_tls_request and on_tls_response. In active TlsRecordHandlers these two methods return the _bytes_ to sent on the wire in the place of the provided record. This allows you to send completely different records, inject your own, or modify the incomin record arbitrarily. --- .../mitm/connection/handlers/data/ssl.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/nogotofail/mitm/connection/handlers/data/ssl.py b/nogotofail/mitm/connection/handlers/data/ssl.py index b3df9dcc..30e3a74e 100644 --- a/nogotofail/mitm/connection/handlers/data/ssl.py +++ b/nogotofail/mitm/connection/handlers/data/ssl.py @@ -21,6 +21,88 @@ from nogotofail.mitm.event import connection from nogotofail.mitm.util import ssl2, tls, vuln +class _TlsRecordHandler(DataHandler): + """Base class for a handler that acts on TlsRecords in a Tls connection. + + Handlers should subclass this and implement on_tls_request and on_tls_response + to handle TlsRecords in the connection. + + This class handles buffering and dealing with multiple records in one message + and can be used for active or passive handlers as needed. + """ + ssl = False + class _TlsRecordBuffer(): + MAX_BUFFER = 65536 + def __init__(self): + self.buffer = "" + self.should_buffer = True + client_buffer = None + server_buffer = None + + def on_tls_request(self, record): + """Called when the client sends a tls record to the server + + record: tls.types.TlsRecord the client sent + Returns the bytes to be sent in place of the record + """ + return record.to_bytes() + + def on_tls_response(self, record): + """Called when the server sends a tls record to the client + + record: tls.types.TlsRecord the client sent + Returns the bytes to be sent in place of the record + """ + return record.to_bytes() + + def on_ssl(self, client_hello): + self.ssl = True + self.client_buffer = _TlsRecordHandler._TlsRecordBuffer() + self.server_buffer = _TlsRecordHandler._TlsRecordBuffer() + + def _handle_message(self, message, buffer, record_fn): + """Build a response calling record_fn on all the TlsRecords in message + + message: bytes to parse as TlsRecords + record_fn: one of on_tls_request, on_tls_response to handle the record + Returns tuple containing the bytes to send for all the records handled and any remaining unparsed data + """ + out = "" + message = buffer.buffer + message + buffer.buffer = "" + remaining = message + while remaining: + record = None + try: + record, remaining = tls.parse_tls(remaining, throw_on_incomplete=True) + except tls.types.TlsNotEnoughDataError: + if buffer.should_buffer: + buffer.buffer = remaining + if len(buffer.buffer) >= buffer.MAX_BUFFER: + buffer.buffer = "" + return out + if not record: + return out + record_bytes = record_fn(record) + # In a passive handler on_tls_* could return None, so make sure not to cause an error + # out doesn't matter on a passive handler. + if record_bytes: + out += record_bytes + # Once we read a CHANGE_CIPHER_SPEC stop trying to buffer, its probably encrypted + if record.content_type == record.CONTENT_TYPE.CHANGE_CIPHER_SPEC: + buffer.should_buffer = False + return out + + def on_request(self, request): + if not self.ssl: + return request + return self._handle_message(request, self.client_buffer, self.on_tls_request) + + def on_response(self, response): + if not self.ssl: + return response + return self._handle_message(response, self.server_buffer, self.on_tls_response) + @handler.passive(handlers) class InsecureCipherDetectionHandler(DataHandler): name = "insecurecipherdetection" From 64d6ff51a17ae2f9aee824902494138c1fd97654 Mon Sep 17 00:00:00 2001 From: yzninja Date: Mon, 21 Sep 2015 23:15:48 +1000 Subject: [PATCH 04/21] Migration of the Android client project from Eclipse to Android Studio. This migration included a change to the Gradle build system and modifications to the project file structure. A summary of the changes in migrating from Eclipse ADT to Android Studio is described at: https://developer.android.com/tools/studio/eclipse-transition-guide.html --- nogotofail/clients/android/.classpath | 9 ----- nogotofail/clients/android/.gitignore | 27 ++++++++++++++ nogotofail/clients/android/.project | 35 ------------------ nogotofail/clients/android/BUILD | 6 +-- nogotofail/clients/android/app/build.gradle | 19 ++++++++++ .../{ => app/src/main}/AndroidManifest.xml | 0 .../AdvancedPreferenceFragment.java | 0 .../nogotofail/AttacksPreferenceFragment.java | 0 .../CertWhitelistDialogFragment.java | 0 .../CertWhitelistPromptActivity.java | 0 .../main/java}/net/nogotofail/Closeables.java | 0 .../net/nogotofail/FeedbackReporter.java | 0 .../java}/net/nogotofail/HexEncoding.java | 0 .../nogotofail/MuteNotificationsReceiver.java | 0 .../net/nogotofail/NoGotoFailApplication.java | 0 .../NotificationsPreferenceFragment.java | 0 ...ferencesBackedPinningX509TrustManager.java | 0 .../nogotofail/RouterConnectionHandler.java | 0 .../net/nogotofail/RouterSocketClient.java | 0 .../net/nogotofail/RouterSocketService.java | 0 .../net/nogotofail/SettingsActivity.java | 0 .../java}/net/nogotofail/StatusActivity.java | 0 .../TcpClientInfoCommandHandler.java | 0 .../src/main/java}/net/nogotofail/Utils.java | 0 .../nogotofail/VulnNotifyCommandHandler.java | 0 .../res/drawable-xhdpi/ic_notify_default.png | Bin .../res/drawable-xhdpi/ic_notify_vuln.png | Bin .../main}/res/drawable-xxhdpi/ic_launcher.png | Bin .../res/drawable-xxxhdpi/ic_launcher.png | Bin .../src/main}/res/layout/status_activity.xml | 0 .../res/menu/status_activity_actions.xml | 0 .../src/main}/res/values/constants.xml | 0 .../{ => app/src/main}/res/values/strings.xml | 0 .../{ => app/src/main}/res/values/themes.xml | 0 .../src/main}/res/xml/advanced_settings.xml | 0 .../src/main}/res/xml/attacks_settings.xml | 0 .../main}/res/xml/notifications_settings.xml | 0 .../src/main}/res/xml/preference_headers.xml | 0 .../clients/android/art/ic_launcher.xcf | Bin 39509 -> 0 bytes nogotofail/clients/android/build.gradle | 19 ++++++++++ nogotofail/clients/android/project.properties | 14 ------- nogotofail/clients/android/settings.gradle | 1 + 42 files changed, 69 insertions(+), 61 deletions(-) delete mode 100644 nogotofail/clients/android/.classpath delete mode 100644 nogotofail/clients/android/.project create mode 100644 nogotofail/clients/android/app/build.gradle rename nogotofail/clients/android/{ => app/src/main}/AndroidManifest.xml (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/AdvancedPreferenceFragment.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/AttacksPreferenceFragment.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/CertWhitelistDialogFragment.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/CertWhitelistPromptActivity.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/Closeables.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/FeedbackReporter.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/HexEncoding.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/MuteNotificationsReceiver.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/NoGotoFailApplication.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/NotificationsPreferenceFragment.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/PreferencesBackedPinningX509TrustManager.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/RouterConnectionHandler.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/RouterSocketClient.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/RouterSocketService.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/SettingsActivity.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/StatusActivity.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/TcpClientInfoCommandHandler.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/Utils.java (100%) rename nogotofail/clients/android/{src => app/src/main/java}/net/nogotofail/VulnNotifyCommandHandler.java (100%) rename nogotofail/clients/android/{ => app/src/main}/res/drawable-xhdpi/ic_notify_default.png (100%) rename nogotofail/clients/android/{ => app/src/main}/res/drawable-xhdpi/ic_notify_vuln.png (100%) rename nogotofail/clients/android/{ => app/src/main}/res/drawable-xxhdpi/ic_launcher.png (100%) rename nogotofail/clients/android/{ => app/src/main}/res/drawable-xxxhdpi/ic_launcher.png (100%) rename nogotofail/clients/android/{ => app/src/main}/res/layout/status_activity.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/menu/status_activity_actions.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/values/constants.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/values/strings.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/values/themes.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/xml/advanced_settings.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/xml/attacks_settings.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/xml/notifications_settings.xml (100%) rename nogotofail/clients/android/{ => app/src/main}/res/xml/preference_headers.xml (100%) delete mode 100644 nogotofail/clients/android/art/ic_launcher.xcf create mode 100644 nogotofail/clients/android/build.gradle delete mode 100644 nogotofail/clients/android/project.properties create mode 100644 nogotofail/clients/android/settings.gradle diff --git a/nogotofail/clients/android/.classpath b/nogotofail/clients/android/.classpath deleted file mode 100644 index 7bc01d9a..00000000 --- a/nogotofail/clients/android/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/nogotofail/clients/android/.gitignore b/nogotofail/clients/android/.gitignore index d97b4807..67495841 100644 --- a/nogotofail/clients/android/.gitignore +++ b/nogotofail/clients/android/.gitignore @@ -4,3 +4,30 @@ gen # Bazel bazel-* WORKSPACE + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ +gradle/ +gradlew* + +# Local configuration file (sdk path, etc) +local.properties + +# Mac OS X clutter +*.DS_Store + +# Windows clutter +Thumbs.db + +# Android Studio project files (see https://intellij-support.jetbrains.com/entries/23393067) +*.iml +.idea/ +*.iws + +# Android Studio Navigation editor temp files +.navigation/ diff --git a/nogotofail/clients/android/.project b/nogotofail/clients/android/.project deleted file mode 100644 index bd856ba5..00000000 --- a/nogotofail/clients/android/.project +++ /dev/null @@ -1,35 +0,0 @@ - - - nogotofail - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - - - diff --git a/nogotofail/clients/android/BUILD b/nogotofail/clients/android/BUILD index a432a7d9..a2860898 100644 --- a/nogotofail/clients/android/BUILD +++ b/nogotofail/clients/android/BUILD @@ -2,10 +2,10 @@ android_binary( name = "nogotofail", custom_package = "net.nogotofail", srcs = glob([ - "src/**/*.java", + "app/src/main/java/**/.java", ]), resource_files = glob([ - "res/**/*", + "app/src/main/res/**/*", ]), - manifest = "AndroidManifest.xml", + manifest = "app/src/main/AndroidManifest.xml", ) diff --git a/nogotofail/clients/android/app/build.gradle b/nogotofail/clients/android/app/build.gradle new file mode 100644 index 00000000..ebe635bf --- /dev/null +++ b/nogotofail/clients/android/app/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "net.nogotofail" + minSdkVersion 14 + targetSdkVersion 21 + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} diff --git a/nogotofail/clients/android/AndroidManifest.xml b/nogotofail/clients/android/app/src/main/AndroidManifest.xml similarity index 100% rename from nogotofail/clients/android/AndroidManifest.xml rename to nogotofail/clients/android/app/src/main/AndroidManifest.xml diff --git a/nogotofail/clients/android/src/net/nogotofail/AdvancedPreferenceFragment.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/AdvancedPreferenceFragment.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/AdvancedPreferenceFragment.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/AdvancedPreferenceFragment.java diff --git a/nogotofail/clients/android/src/net/nogotofail/AttacksPreferenceFragment.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/AttacksPreferenceFragment.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/AttacksPreferenceFragment.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/AttacksPreferenceFragment.java diff --git a/nogotofail/clients/android/src/net/nogotofail/CertWhitelistDialogFragment.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/CertWhitelistDialogFragment.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/CertWhitelistDialogFragment.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/CertWhitelistDialogFragment.java diff --git a/nogotofail/clients/android/src/net/nogotofail/CertWhitelistPromptActivity.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/CertWhitelistPromptActivity.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/CertWhitelistPromptActivity.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/CertWhitelistPromptActivity.java diff --git a/nogotofail/clients/android/src/net/nogotofail/Closeables.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/Closeables.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/Closeables.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/Closeables.java diff --git a/nogotofail/clients/android/src/net/nogotofail/FeedbackReporter.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/FeedbackReporter.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/FeedbackReporter.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/FeedbackReporter.java diff --git a/nogotofail/clients/android/src/net/nogotofail/HexEncoding.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/HexEncoding.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/HexEncoding.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/HexEncoding.java diff --git a/nogotofail/clients/android/src/net/nogotofail/MuteNotificationsReceiver.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/MuteNotificationsReceiver.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/MuteNotificationsReceiver.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/MuteNotificationsReceiver.java diff --git a/nogotofail/clients/android/src/net/nogotofail/NoGotoFailApplication.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/NoGotoFailApplication.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/NoGotoFailApplication.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/NoGotoFailApplication.java diff --git a/nogotofail/clients/android/src/net/nogotofail/NotificationsPreferenceFragment.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/NotificationsPreferenceFragment.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/NotificationsPreferenceFragment.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/NotificationsPreferenceFragment.java diff --git a/nogotofail/clients/android/src/net/nogotofail/PreferencesBackedPinningX509TrustManager.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/PreferencesBackedPinningX509TrustManager.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/PreferencesBackedPinningX509TrustManager.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/PreferencesBackedPinningX509TrustManager.java diff --git a/nogotofail/clients/android/src/net/nogotofail/RouterConnectionHandler.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/RouterConnectionHandler.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/RouterConnectionHandler.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/RouterConnectionHandler.java diff --git a/nogotofail/clients/android/src/net/nogotofail/RouterSocketClient.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/RouterSocketClient.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/RouterSocketClient.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/RouterSocketClient.java diff --git a/nogotofail/clients/android/src/net/nogotofail/RouterSocketService.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/RouterSocketService.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/RouterSocketService.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/RouterSocketService.java diff --git a/nogotofail/clients/android/src/net/nogotofail/SettingsActivity.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/SettingsActivity.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/SettingsActivity.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/SettingsActivity.java diff --git a/nogotofail/clients/android/src/net/nogotofail/StatusActivity.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/StatusActivity.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/StatusActivity.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/StatusActivity.java diff --git a/nogotofail/clients/android/src/net/nogotofail/TcpClientInfoCommandHandler.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/TcpClientInfoCommandHandler.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/TcpClientInfoCommandHandler.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/TcpClientInfoCommandHandler.java diff --git a/nogotofail/clients/android/src/net/nogotofail/Utils.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/Utils.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/Utils.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/Utils.java diff --git a/nogotofail/clients/android/src/net/nogotofail/VulnNotifyCommandHandler.java b/nogotofail/clients/android/app/src/main/java/net/nogotofail/VulnNotifyCommandHandler.java similarity index 100% rename from nogotofail/clients/android/src/net/nogotofail/VulnNotifyCommandHandler.java rename to nogotofail/clients/android/app/src/main/java/net/nogotofail/VulnNotifyCommandHandler.java diff --git a/nogotofail/clients/android/res/drawable-xhdpi/ic_notify_default.png b/nogotofail/clients/android/app/src/main/res/drawable-xhdpi/ic_notify_default.png similarity index 100% rename from nogotofail/clients/android/res/drawable-xhdpi/ic_notify_default.png rename to nogotofail/clients/android/app/src/main/res/drawable-xhdpi/ic_notify_default.png diff --git a/nogotofail/clients/android/res/drawable-xhdpi/ic_notify_vuln.png b/nogotofail/clients/android/app/src/main/res/drawable-xhdpi/ic_notify_vuln.png similarity index 100% rename from nogotofail/clients/android/res/drawable-xhdpi/ic_notify_vuln.png rename to nogotofail/clients/android/app/src/main/res/drawable-xhdpi/ic_notify_vuln.png diff --git a/nogotofail/clients/android/res/drawable-xxhdpi/ic_launcher.png b/nogotofail/clients/android/app/src/main/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from nogotofail/clients/android/res/drawable-xxhdpi/ic_launcher.png rename to nogotofail/clients/android/app/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/nogotofail/clients/android/res/drawable-xxxhdpi/ic_launcher.png b/nogotofail/clients/android/app/src/main/res/drawable-xxxhdpi/ic_launcher.png similarity index 100% rename from nogotofail/clients/android/res/drawable-xxxhdpi/ic_launcher.png rename to nogotofail/clients/android/app/src/main/res/drawable-xxxhdpi/ic_launcher.png diff --git a/nogotofail/clients/android/res/layout/status_activity.xml b/nogotofail/clients/android/app/src/main/res/layout/status_activity.xml similarity index 100% rename from nogotofail/clients/android/res/layout/status_activity.xml rename to nogotofail/clients/android/app/src/main/res/layout/status_activity.xml diff --git a/nogotofail/clients/android/res/menu/status_activity_actions.xml b/nogotofail/clients/android/app/src/main/res/menu/status_activity_actions.xml similarity index 100% rename from nogotofail/clients/android/res/menu/status_activity_actions.xml rename to nogotofail/clients/android/app/src/main/res/menu/status_activity_actions.xml diff --git a/nogotofail/clients/android/res/values/constants.xml b/nogotofail/clients/android/app/src/main/res/values/constants.xml similarity index 100% rename from nogotofail/clients/android/res/values/constants.xml rename to nogotofail/clients/android/app/src/main/res/values/constants.xml diff --git a/nogotofail/clients/android/res/values/strings.xml b/nogotofail/clients/android/app/src/main/res/values/strings.xml similarity index 100% rename from nogotofail/clients/android/res/values/strings.xml rename to nogotofail/clients/android/app/src/main/res/values/strings.xml diff --git a/nogotofail/clients/android/res/values/themes.xml b/nogotofail/clients/android/app/src/main/res/values/themes.xml similarity index 100% rename from nogotofail/clients/android/res/values/themes.xml rename to nogotofail/clients/android/app/src/main/res/values/themes.xml diff --git a/nogotofail/clients/android/res/xml/advanced_settings.xml b/nogotofail/clients/android/app/src/main/res/xml/advanced_settings.xml similarity index 100% rename from nogotofail/clients/android/res/xml/advanced_settings.xml rename to nogotofail/clients/android/app/src/main/res/xml/advanced_settings.xml diff --git a/nogotofail/clients/android/res/xml/attacks_settings.xml b/nogotofail/clients/android/app/src/main/res/xml/attacks_settings.xml similarity index 100% rename from nogotofail/clients/android/res/xml/attacks_settings.xml rename to nogotofail/clients/android/app/src/main/res/xml/attacks_settings.xml diff --git a/nogotofail/clients/android/res/xml/notifications_settings.xml b/nogotofail/clients/android/app/src/main/res/xml/notifications_settings.xml similarity index 100% rename from nogotofail/clients/android/res/xml/notifications_settings.xml rename to nogotofail/clients/android/app/src/main/res/xml/notifications_settings.xml diff --git a/nogotofail/clients/android/res/xml/preference_headers.xml b/nogotofail/clients/android/app/src/main/res/xml/preference_headers.xml similarity index 100% rename from nogotofail/clients/android/res/xml/preference_headers.xml rename to nogotofail/clients/android/app/src/main/res/xml/preference_headers.xml diff --git a/nogotofail/clients/android/art/ic_launcher.xcf b/nogotofail/clients/android/art/ic_launcher.xcf deleted file mode 100644 index 37c86a5431855f90747a0cbb6b4373d04b48caa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39509 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 Z;4o}rh*?arrzwfQR(w*TKd+wH{|BPRMgjl; diff --git a/nogotofail/clients/android/build.gradle b/nogotofail/clients/android/build.gradle new file mode 100644 index 00000000..05abe952 --- /dev/null +++ b/nogotofail/clients/android/build.gradle @@ -0,0 +1,19 @@ +task wrapper(type: Wrapper) { + gradleVersion = '2.4' +} + +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.3.0' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/nogotofail/clients/android/project.properties b/nogotofail/clients/android/project.properties deleted file mode 100644 index 6e18427a..00000000 --- a/nogotofail/clients/android/project.properties +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-21 diff --git a/nogotofail/clients/android/settings.gradle b/nogotofail/clients/android/settings.gradle new file mode 100644 index 00000000..e7b4def4 --- /dev/null +++ b/nogotofail/clients/android/settings.gradle @@ -0,0 +1 @@ +include ':app' From 6e53ade82b1fc31a5af68e8fce36aa2f083a1544 Mon Sep 17 00:00:00 2001 From: yzninja Date: Tue, 29 Sep 2015 22:28:13 +1000 Subject: [PATCH 05/21] Migrated the Android test application project from Eclipse to Android Studio. --- nogotofail/test/android/.classpath | 9 ----- nogotofail/test/android/.gitignore | 32 ++++++++++++++++- nogotofail/test/android/.project | 35 ------------------- nogotofail/test/android/app/build.gradle | 19 ++++++++++ .../{ => app/src/main}/AndroidManifest.xml | 0 .../nogotofail/mitmtester/BackgroundTest.java | 0 .../nogotofail/mitmtester/MainActivity.java | 0 .../nogotofail/mitmtester/TestActivity.java | 0 .../http/CleartextHttpCredentialsTest.java | 0 .../mitmtester/http/HttpTestActivity.java | 0 .../tls/DefaultConfigTlsHandshakeTest.java | 0 ...NoSslCertificateChainOfTrustCheckTest.java | 0 ...slCertificateHostnameVerificationTest.java | 0 .../NoTlsServerAutenticationRequiredTest.java | 0 .../mitmtester/tls/TlsTestActivity.java | 0 .../util/AcceptAllX509TrustManager.java | 0 .../nogotofail/mitmtester/util/TlsUtils.java | 0 .../main}/res/layout/http_test_activity.xml | 0 .../src/main}/res/layout/main_activity.xml | 0 .../main}/res/layout/tls_test_activity.xml | 0 .../{ => app/src/main}/res/values/strings.xml | 0 nogotofail/test/android/build.gradle | 19 ++++++++++ nogotofail/test/android/project.properties | 14 -------- nogotofail/test/android/settings.gradle | 1 + 24 files changed, 70 insertions(+), 59 deletions(-) delete mode 100644 nogotofail/test/android/.classpath delete mode 100644 nogotofail/test/android/.project create mode 100644 nogotofail/test/android/app/build.gradle rename nogotofail/test/android/{ => app/src/main}/AndroidManifest.xml (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/BackgroundTest.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/MainActivity.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/TestActivity.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/http/CleartextHttpCredentialsTest.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/http/HttpTestActivity.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/tls/DefaultConfigTlsHandshakeTest.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/tls/NoSslCertificateChainOfTrustCheckTest.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/tls/NoSslCertificateHostnameVerificationTest.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/tls/NoTlsServerAutenticationRequiredTest.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/tls/TlsTestActivity.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/util/AcceptAllX509TrustManager.java (100%) rename nogotofail/test/android/{src => app/src/main/java}/net/nogotofail/mitmtester/util/TlsUtils.java (100%) rename nogotofail/test/android/{ => app/src/main}/res/layout/http_test_activity.xml (100%) rename nogotofail/test/android/{ => app/src/main}/res/layout/main_activity.xml (100%) rename nogotofail/test/android/{ => app/src/main}/res/layout/tls_test_activity.xml (100%) rename nogotofail/test/android/{ => app/src/main}/res/values/strings.xml (100%) create mode 100644 nogotofail/test/android/build.gradle delete mode 100644 nogotofail/test/android/project.properties create mode 100644 nogotofail/test/android/settings.gradle diff --git a/nogotofail/test/android/.classpath b/nogotofail/test/android/.classpath deleted file mode 100644 index 7bc01d9a..00000000 --- a/nogotofail/test/android/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/nogotofail/test/android/.gitignore b/nogotofail/test/android/.gitignore index 9a0e78f0..67495841 100644 --- a/nogotofail/test/android/.gitignore +++ b/nogotofail/test/android/.gitignore @@ -1,3 +1,33 @@ -.settings bin gen + +# Bazel +bazel-* +WORKSPACE + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ +gradle/ +gradlew* + +# Local configuration file (sdk path, etc) +local.properties + +# Mac OS X clutter +*.DS_Store + +# Windows clutter +Thumbs.db + +# Android Studio project files (see https://intellij-support.jetbrains.com/entries/23393067) +*.iml +.idea/ +*.iws + +# Android Studio Navigation editor temp files +.navigation/ diff --git a/nogotofail/test/android/.project b/nogotofail/test/android/.project deleted file mode 100644 index 26962c9b..00000000 --- a/nogotofail/test/android/.project +++ /dev/null @@ -1,35 +0,0 @@ - - - nogotofail-mitm-tester - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - - - diff --git a/nogotofail/test/android/app/build.gradle b/nogotofail/test/android/app/build.gradle new file mode 100644 index 00000000..9a83f1c0 --- /dev/null +++ b/nogotofail/test/android/app/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "net.nogotofail.mitmtester" + minSdkVersion 14 + targetSdkVersion 21 + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} diff --git a/nogotofail/test/android/AndroidManifest.xml b/nogotofail/test/android/app/src/main/AndroidManifest.xml similarity index 100% rename from nogotofail/test/android/AndroidManifest.xml rename to nogotofail/test/android/app/src/main/AndroidManifest.xml diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/BackgroundTest.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/BackgroundTest.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/BackgroundTest.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/BackgroundTest.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/MainActivity.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/MainActivity.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/MainActivity.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/MainActivity.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/TestActivity.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/TestActivity.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/TestActivity.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/TestActivity.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/http/CleartextHttpCredentialsTest.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/http/CleartextHttpCredentialsTest.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/http/CleartextHttpCredentialsTest.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/http/CleartextHttpCredentialsTest.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/http/HttpTestActivity.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/http/HttpTestActivity.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/http/HttpTestActivity.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/http/HttpTestActivity.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/tls/DefaultConfigTlsHandshakeTest.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/DefaultConfigTlsHandshakeTest.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/tls/DefaultConfigTlsHandshakeTest.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/DefaultConfigTlsHandshakeTest.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/tls/NoSslCertificateChainOfTrustCheckTest.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/NoSslCertificateChainOfTrustCheckTest.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/tls/NoSslCertificateChainOfTrustCheckTest.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/NoSslCertificateChainOfTrustCheckTest.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/tls/NoSslCertificateHostnameVerificationTest.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/NoSslCertificateHostnameVerificationTest.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/tls/NoSslCertificateHostnameVerificationTest.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/NoSslCertificateHostnameVerificationTest.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/tls/NoTlsServerAutenticationRequiredTest.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/NoTlsServerAutenticationRequiredTest.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/tls/NoTlsServerAutenticationRequiredTest.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/NoTlsServerAutenticationRequiredTest.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/tls/TlsTestActivity.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/TlsTestActivity.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/tls/TlsTestActivity.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/tls/TlsTestActivity.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/util/AcceptAllX509TrustManager.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/util/AcceptAllX509TrustManager.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/util/AcceptAllX509TrustManager.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/util/AcceptAllX509TrustManager.java diff --git a/nogotofail/test/android/src/net/nogotofail/mitmtester/util/TlsUtils.java b/nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/util/TlsUtils.java similarity index 100% rename from nogotofail/test/android/src/net/nogotofail/mitmtester/util/TlsUtils.java rename to nogotofail/test/android/app/src/main/java/net/nogotofail/mitmtester/util/TlsUtils.java diff --git a/nogotofail/test/android/res/layout/http_test_activity.xml b/nogotofail/test/android/app/src/main/res/layout/http_test_activity.xml similarity index 100% rename from nogotofail/test/android/res/layout/http_test_activity.xml rename to nogotofail/test/android/app/src/main/res/layout/http_test_activity.xml diff --git a/nogotofail/test/android/res/layout/main_activity.xml b/nogotofail/test/android/app/src/main/res/layout/main_activity.xml similarity index 100% rename from nogotofail/test/android/res/layout/main_activity.xml rename to nogotofail/test/android/app/src/main/res/layout/main_activity.xml diff --git a/nogotofail/test/android/res/layout/tls_test_activity.xml b/nogotofail/test/android/app/src/main/res/layout/tls_test_activity.xml similarity index 100% rename from nogotofail/test/android/res/layout/tls_test_activity.xml rename to nogotofail/test/android/app/src/main/res/layout/tls_test_activity.xml diff --git a/nogotofail/test/android/res/values/strings.xml b/nogotofail/test/android/app/src/main/res/values/strings.xml similarity index 100% rename from nogotofail/test/android/res/values/strings.xml rename to nogotofail/test/android/app/src/main/res/values/strings.xml diff --git a/nogotofail/test/android/build.gradle b/nogotofail/test/android/build.gradle new file mode 100644 index 00000000..05abe952 --- /dev/null +++ b/nogotofail/test/android/build.gradle @@ -0,0 +1,19 @@ +task wrapper(type: Wrapper) { + gradleVersion = '2.4' +} + +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.3.0' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/nogotofail/test/android/project.properties b/nogotofail/test/android/project.properties deleted file mode 100644 index 6e18427a..00000000 --- a/nogotofail/test/android/project.properties +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-21 diff --git a/nogotofail/test/android/settings.gradle b/nogotofail/test/android/settings.gradle new file mode 100644 index 00000000..e7b4def4 --- /dev/null +++ b/nogotofail/test/android/settings.gradle @@ -0,0 +1 @@ +include ':app' From d22949304e6fe4b09e7149041765cefa155f8213 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Tue, 8 Mar 2016 14:42:21 -0800 Subject: [PATCH 06/21] Fix typo in comment. --- .../mitm/connection/handlers/connection/serverkeyreplace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py b/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py index e24b94d3..28258eaa 100644 --- a/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py +++ b/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py @@ -69,7 +69,7 @@ def on_request(self, request): if ext.type == Extension.TYPE.SESSIONTICKET: ext.raw_data = [] # Retain in ClientHello only cipher suites which require the - # server to send a ServerKeyExchange message: emphemeral (EC)DH + # server to send a ServerKeyExchange message: ephemeral (EC)DH # and RSA_EXPORT cipher suites. Also retain pseudo/signalling # cipher suites because they don't affect this attack/test. hello.ciphers = [c for c in hello.ciphers From beac2d9c3ecd58f161fec34380faeabafa2f2172 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 5 May 2016 10:08:57 -0700 Subject: [PATCH 07/21] Add reverse proxy mode Adds a reverse proxy mode whereby clients connect directly to the reverse proxy, and the proxy forwards the connection to a target host and port specified on the command line or config. For example: $ python -m nogotofail.mitm -A invalidhostname \ --mode reverse --port 443 --serverssl server.crt \ --target_addr www.example.com --target_port 443 The reverse proxy mode makes it trivial to test a client that always connects to a specific server port, as it doesn't require socks or transparent proxy. That said, the mode has some limitations. It cannot be used in cases where the client needs to connect to multiple servers/ports. Also, it doesn't rewrite HTTP headers, e.g. Host, so the values may be incorrect when received by the real server, and when responses are received by the client. --- nogotofail/mitm/__main__.py | 13 ++++++++++- nogotofail/mitm/connection/__init__.py | 2 +- nogotofail/mitm/connection/connection.py | 29 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/nogotofail/mitm/__main__.py b/nogotofail/mitm/__main__.py index b6550331..d9b927ca 100644 --- a/nogotofail/mitm/__main__.py +++ b/nogotofail/mitm/__main__.py @@ -30,7 +30,7 @@ import sys from nogotofail.mitm.blame import Server as AppBlameServer -from nogotofail.mitm.connection import Server, RedirectConnection, SocksConnection, TproxyConnection +from nogotofail.mitm.connection import Server, RedirectConnection, SocksConnection, TproxyConnection, ReverseProxyConnection from nogotofail.mitm.connection import handlers from nogotofail.mitm.connection.handlers import preconditions from nogotofail.mitm.looper import MitmLoop @@ -127,6 +127,10 @@ def set_tproxy_rules(args): routing.enable_tproxy_rules(port, ipv6=ipv6) atexit.register(routing.disable_tproxy_rules, ipv6=ipv6) +def set_reverse_rules(args): + ReverseProxyConnection.target_addr = args.target_addr + ReverseProxyConnection.target_port = args.target_port + # Traffic capture modes Mode = collections.namedtuple("Mode", ["cls", "setup", "description"]) modes = { @@ -139,6 +143,9 @@ def set_tproxy_rules(args): "socks": Mode(SocksConnection, None, "Listen as a SOCKS server to route traffic"), + "reverse": Mode(ReverseProxyConnection, + set_reverse_rules, + "Listen as a reverse proxy and forward to target host"), } default_mode = "tproxy" @@ -211,6 +218,10 @@ def parse_args(): parser.add_argument( "--cport", help="Port to listen for nogotofail clients on", action="store", type=int, default=8443) + parser.add_argument( + "--target_addr", help="Address to forward reverse proxy connections to", action="store") + parser.add_argument( + "--target_port", help="Port to forward reverse proxy connections to", action="store", type=int) parser.add_argument( "-6", "--ipv6", help=("Route IPv6 traffic. " diff --git a/nogotofail/mitm/connection/__init__.py b/nogotofail/mitm/connection/__init__.py index 93163dc8..5b74c24f 100644 --- a/nogotofail/mitm/connection/__init__.py +++ b/nogotofail/mitm/connection/__init__.py @@ -13,6 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. ''' -from connection import RedirectConnection, SocksConnection, TproxyConnection +from connection import RedirectConnection, SocksConnection, TproxyConnection, ReverseProxyConnection from server import Server import handlers diff --git a/nogotofail/mitm/connection/connection.py b/nogotofail/mitm/connection/connection.py index 16100483..65c0b753 100644 --- a/nogotofail/mitm/connection/connection.py +++ b/nogotofail/mitm/connection/connection.py @@ -661,6 +661,35 @@ def inject_response(self, response): break self.client_socket.sendall(response) +class ReverseProxyConnection(BaseConnection): + """Connection based on reverse proxy""" + + def start(self): + """Setup the remote end of the connection and client connection + to be ready to start bridging traffic. + + This method should be implemented based on how connections are routed to nogotofail.mitm + such as iptables redirect or proxies. + + This should call handler.on_select and handler.on_establish when appropriate + and set server_addr and server_port to the remote endpoint's address.""" + self.server_addr, self.server_port = ReverseProxyConnection.target_addr, ReverseProxyConnection.target_port + + self.handler.on_select() + for handler in self.data_handlers: + handler.on_select() + try: + self._start_server_connect_nonblocking() + except socket.error: + return False + return True + + def _get_client_remote_name(self): + """Get the addr, port of the what the client thinks is their remote + This is used for blame, so this should correspond to some tcp connection + on the client""" + return self.client_socket.getsockname()[:2] + class RedirectConnection(BaseConnection): """Connection based on getting traffic from iptables redirect rules""" From ff9a6b667061a8c25d1a6e9b83d316fb725cd5e1 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 13 May 2016 11:25:12 -0700 Subject: [PATCH 08/21] Validate target addr and port Target addr and port are required when using reverse mode. Print an error to stderr if they are omitted or incorrect and exit with 2, similar to what happens when the type of argument doesn't match. --- nogotofail/mitm/__main__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/nogotofail/mitm/__main__.py b/nogotofail/mitm/__main__.py index d9b927ca..5c7717e4 100644 --- a/nogotofail/mitm/__main__.py +++ b/nogotofail/mitm/__main__.py @@ -128,6 +128,11 @@ def set_tproxy_rules(args): atexit.register(routing.disable_tproxy_rules, ipv6=ipv6) def set_reverse_rules(args): + if args.target_addr is None: + raise ValueError("Target address is required") + if args.target_port < 1 or args.target_port > 65535: + raise ValueError("Target port must be between 1 and 65535") + ReverseProxyConnection.target_addr = args.target_addr ReverseProxyConnection.target_port = args.target_port @@ -320,7 +325,12 @@ def run(): signal.signal(signal.SIGTERM, sigterm_handler) mode = modes[args.mode] if mode.setup: - mode.setup(args) + try: + mode.setup(args) + except ValueError as e: + print("Error: %s" % e.message) + sys.exit(2) + blame = ( build_blame( args.cport, args.serverssl, args.probability, attack_cls, From a2ab7c1550dae9ddcbd8c6d704ded30179f6e3eb Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 17 May 2016 14:36:24 -0700 Subject: [PATCH 09/21] Log the export ciphers in the attack event There was a copy/paste error where if the client enabled EXPORT ciphers we would log the `integ_ciphers` from the previous check in the attack event. Change to `export_ciphers` instead. --- nogotofail/mitm/connection/handlers/data/ssl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nogotofail/mitm/connection/handlers/data/ssl.py b/nogotofail/mitm/connection/handlers/data/ssl.py index 30e3a74e..98ef1d90 100644 --- a/nogotofail/mitm/connection/handlers/data/ssl.py +++ b/nogotofail/mitm/connection/handlers/data/ssl.py @@ -140,7 +140,7 @@ def on_ssl(self, client_hello): # Check for export ciphers since they're horribly weak export_ciphers = [str(c) for c in client_hello.ciphers if "EXPORT" in str(c)] if export_ciphers: - self._handle_bad_ciphers(integ_ciphers, + self._handle_bad_ciphers(export_ciphers, "Client enabled export TLS/SSL cipher suites %s" % (", ".join(export_ciphers))) From 79bd1a2b0d15abe9eebb711329271d022ee09f4b Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 6 Jun 2016 14:07:18 -0700 Subject: [PATCH 10/21] Include seconds in notAfter date Nogotofail omitted the seconds portion of the notAfter date, which causes openjdk 1.8 to fail to parse the string as a GeneralizedTime[1]: java.io.IOException: Parse Generalized time, invalid offset This can lead to false negatives in otherwise vulnerable SSL clients. Since notBefore includes seconds, include them for notAfter for greater compatibility. This affects both the selfsigned and invalidhostname attacks. [1] https://github.com/openjdk-mirror/jdk/blob/c5294eda494101e62dc4c0eaa946ebe9ce60cd6b/src/share/classes/sun/security/util/DerInputBuffer.java#L392-L396 --- nogotofail/mitm/util/ca.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nogotofail/mitm/util/ca.py b/nogotofail/mitm/util/ca.py index 16ae3db6..c7e47545 100644 --- a/nogotofail/mitm/util/ca.py +++ b/nogotofail/mitm/util/ca.py @@ -46,7 +46,7 @@ def _generate_ca(self): self.cert.set_serial_number(1) self.cert.get_subject().CN = 'ca.nogotofail' self.cert.set_notBefore("19300101000000+0000") - self.cert.set_notAfter("203012310000+0000") + self.cert.set_notAfter("20301231000000+0000") self.cert.set_issuer(self.cert.get_subject()) self.cert.set_pubkey(self.key) self.cert.add_extensions([ @@ -95,7 +95,7 @@ def _generate_cert(self, cn, san, path): cert.set_serial_number(random.randint(0, 2**20)) # Use a huge range so we dont have to worry about bad clocks cert.set_notBefore("19300101000000+0000") - cert.set_notAfter("203012310000+0000") + cert.set_notAfter("20301231000000+0000") cert.set_issuer(self.cert.get_subject()) if san: cert.add_extensions([san]) From 52355dbe102c17481aec99acbb8f4a4471708c87 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Wed, 13 Jul 2016 12:51:19 -0700 Subject: [PATCH 11/21] Fix record building in serverkeyreplace In the refactor of TLSHandlers the logic for splitting uneeded records out in on_response was done incorrect and so would add an entire copy of the TLS record for each message in the record before the ServerKeyExchange Fixes #107 --- .../mitm/connection/handlers/connection/serverkeyreplace.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py b/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py index 28258eaa..88a4e146 100644 --- a/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py +++ b/nogotofail/mitm/connection/handlers/connection/serverkeyreplace.py @@ -141,8 +141,7 @@ def on_response(self, response): remaining) self.signature_tampered = True return response - else: - new_response += record.to_bytes() + new_response += record.to_bytes() except tls.types.TlsNotEnoughDataError: # Failed to parse TLS, this is probably due to a short read of a TLS From e23db1b2438f211ff96cdf3b51bf07825ad7be16 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Thu, 1 Dec 2016 15:21:19 -0800 Subject: [PATCH 12/21] Harden OpenVPN configuration This switches the OpenVPN tunnel from Blowfish with SHA-1 HMAC to 128-bit AES with SHA-256 HMAC. Moreover, client configuration now requires that server certificate is permitted to be used for server authentication (as signalled by the certificate's Key Usage and Extended Key Usage extensions). This is to prevent one client of the server from being able to use its client certificate to MiTM other clients of the server. This is needed because in the current setup server and client certs are issued from the same CA. --- docs/gce/nogotofail.ovpn.template | 9 +++++++++ docs/gce/openvpn-client-cert-extfile.cfg | 2 ++ docs/gce/openvpn-server-cert-extfile.cfg | 2 ++ docs/gce/openvpn.conf | 8 ++++++++ docs/gce/setup_openvpn.sh | 4 ++-- 5 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 docs/gce/openvpn-client-cert-extfile.cfg create mode 100644 docs/gce/openvpn-server-cert-extfile.cfg diff --git a/docs/gce/nogotofail.ovpn.template b/docs/gce/nogotofail.ovpn.template index ac37d5bc..65f1239e 100644 --- a/docs/gce/nogotofail.ovpn.template +++ b/docs/gce/nogotofail.ovpn.template @@ -14,3 +14,12 @@ persist-tun persist-key comp-lzo + +# Accept only server certificates which are whitelisted (via Key Usage and Extended Key Usage) for +# server authentication +remote-cert-tls server + +# Symmetric tunnel crypto config +auth SHA256 +cipher AES-128-CBC +keysize 128 diff --git a/docs/gce/openvpn-client-cert-extfile.cfg b/docs/gce/openvpn-client-cert-extfile.cfg new file mode 100644 index 00000000..34390879 --- /dev/null +++ b/docs/gce/openvpn-client-cert-extfile.cfg @@ -0,0 +1,2 @@ +keyUsage = digitalSignature, keyAgreement +extendedKeyUsage = clientAuth diff --git a/docs/gce/openvpn-server-cert-extfile.cfg b/docs/gce/openvpn-server-cert-extfile.cfg new file mode 100644 index 00000000..1b4b1a4f --- /dev/null +++ b/docs/gce/openvpn-server-cert-extfile.cfg @@ -0,0 +1,2 @@ +keyUsage = digitalSignature, keyAgreement +extendedKeyUsage = serverAuth diff --git a/docs/gce/openvpn.conf b/docs/gce/openvpn.conf index 7fa18b13..900aa9fa 100644 --- a/docs/gce/openvpn.conf +++ b/docs/gce/openvpn.conf @@ -58,3 +58,11 @@ cert /etc/openvpn/server_cert.pem key /etc/openvpn/server_key.pem dh /etc/openvpn/dhparam2048.pem + +# ECDHE isn't yet supported by OpenVPN (as of 2.3)... +tls-cipher DHE-RSA-AES128-GCM-SHA256,DHE-RSA-AES128-SHA256,DHE-RSA-AES128-SHA + +# Symmetric tunnel crypto config +auth SHA256 +cipher AES-128-CBC +keysize 128 diff --git a/docs/gce/setup_openvpn.sh b/docs/gce/setup_openvpn.sh index 6b9b621c..3d6a6a94 100755 --- a/docs/gce/setup_openvpn.sh +++ b/docs/gce/setup_openvpn.sh @@ -20,13 +20,13 @@ echo "Generating server public key pair and certificate..." openssl genrsa -out $CONFIG_DIR/server_key.pem 2048 openssl req -new -key $CONFIG_DIR/server_key.pem -out $CONFIG_DIR/server_csr.pem -subj '/CN=server.vpn.nogotofail' chmod 600 $CONFIG_DIR/server_key.pem -openssl x509 -req -in $CONFIG_DIR/server_csr.pem -CA $CONFIG_DIR/ca_cert.pem -CAkey $CONFIG_DIR/ca_key.pem -CAcreateserial -out $CONFIG_DIR/server_cert.pem -sha256 -days 365 +openssl x509 -req -in $CONFIG_DIR/server_csr.pem -CA $CONFIG_DIR/ca_cert.pem -CAkey $CONFIG_DIR/ca_key.pem -CAcreateserial -out $CONFIG_DIR/server_cert.pem -sha256 -days 365 -extfile openvpn-server-cert-extfile.cfg rm $CONFIG_DIR/server_csr.pem echo "Generating client public key pair and certificate..." openssl genrsa -out $CONFIG_DIR/client_key.pem 2048 openssl req -new -key $CONFIG_DIR/client_key.pem -out $CONFIG_DIR/client_csr.pem -subj '/CN=client.vpn.nogotofail' -openssl x509 -req -in $CONFIG_DIR/client_csr.pem -CA $CONFIG_DIR/ca_cert.pem -CAkey $CONFIG_DIR/ca_key.pem -CAcreateserial -out $CONFIG_DIR/client_cert.pem -sha256 -days 365 +openssl x509 -req -in $CONFIG_DIR/client_csr.pem -CA $CONFIG_DIR/ca_cert.pem -CAkey $CONFIG_DIR/ca_key.pem -CAcreateserial -out $CONFIG_DIR/client_cert.pem -sha256 -days 365 -extfile openvpn-client-cert-extfile.cfg rm $CONFIG_DIR/client_csr.pem openssl dhparam 2048 > $CONFIG_DIR/dhparam2048.pem From 694440c73cd96540794fbd6940988968c791d6df Mon Sep 17 00:00:00 2001 From: Santiago Castro Date: Tue, 18 Apr 2017 05:14:51 -0300 Subject: [PATCH 13/21] Fix broken Markdown headings --- README.md | 10 +++++----- docs/design.md | 16 ++++++++-------- docs/gce/readme.md | 2 +- docs/getting_started.md | 24 ++++++++++++------------ docs/mitm.md | 24 ++++++++++++------------ 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index ec4cbe14..c137514d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#nogotofail +# nogotofail Nogotofail is a network security testing tool designed to help developers and @@ -7,18 +7,18 @@ cleartext traffic on devices and applications in a flexible, scalable, powerful It includes testing for common SSL certificate verification issues, HTTPS and TLS/SSL library bugs, SSL and STARTTLS stripping issues, cleartext issues, and more. -##Design +## Design Nogotofail is composed of an on-path network MiTM and optional clients for the devices being tested. See [docs/design.md](docs/design.md) for the overview and design goals of nogotofail. -##Dependencies +## Dependencies Nogotofail depends only on Python 2.7 and pyOpenSSL>=0.13. The MiTM is designed to work on Linux machines and the transparent traffic capture modes are Linux specific and require iptables as well. Additionally the Linux client depends on [psutil](https://pypi.python.org/pypi/psutil). -##Getting started +## Getting started See [docs/getting_started.md](docs/getting_started.md) for setup and a walkthrough of nogotofail. -##Discussion +## Discussion For discussion please use our [nogotofail Google Group](https://groups.google.com/forum/#!forum/nogotofail). diff --git a/docs/design.md b/docs/design.md index 648f463f..6aea3adb 100644 --- a/docs/design.md +++ b/docs/design.md @@ -1,4 +1,4 @@ -#Design Goals +# Design Goals Nogotofail was designed to be an automated, powerful, flexible and scalable tool @@ -20,13 +20,13 @@ such that it does not get in the way of using devices as normal. Tests that are destructive are by default run only when necessary and with low probability. -##The building blocks of nogotofail +## The building blocks of nogotofail Nogotofail is centered around an on path man in the middle tool written in python with an optional client application to provide additional attribution and configuration support. -###Man in The Middle +### Man in The Middle The core of nogotofail is the on path network MiTM named nogotofail.mitm that intercepts TCP traffic. It is designed to primarily run on path and centers @@ -36,7 +36,7 @@ nogotofail is completely port agnostic and instead detects vulnerable traffic using DPI instead of based on port numbers. Additionally, because it uses DPI, it is capable of testing TLS/SSL traffic in protocols that use STARTTLS. -####Why attack probabilistically? +#### Why attack probabilistically? Nogotofail does not destructively attack all TLS/SSL connections it sees because such attacks lead to non-vulnerable clients aborting attacked connections. If @@ -56,15 +56,15 @@ devices we’ve seen tend to work as usual. Of course, if you want to test a specific connection aggressively you can push the probability up to 100%. -####Protocol sensing +#### Protocol sensing Protocol sensing for a TLS/SSL testing tool is critical because only attacking traffic on port 443 has two flaws. First, it misses TLS/SSL traffic on non-standard ports, and second, it fails to test protocols that use STARTTLS. -###Client *(optional)* +### Client *(optional)* -####Why have a client? +#### Why have a client? When testing on real devices it can be very difficult to determine what component or app made a vulnerable connection. Even seeing the contents and the destination isn’t always @@ -83,7 +83,7 @@ Finally, the client receives notifications of vulnerabilities from the MiTM. Thi were issues, and it helps you understand exactly what action triggered the vulnerability. -####What the client does +#### What the client does The client exists to provide additional details about connections, allow the client to configure attack settings, and to be notified when vulnerabilities are diff --git a/docs/gce/readme.md b/docs/gce/readme.md index 7d65f752..11cb9704 100644 --- a/docs/gce/readme.md +++ b/docs/gce/readme.md @@ -1,4 +1,4 @@ -#Nogotofail MiTM on Google Compute Engine VM instance +# Nogotofail MiTM on Google Compute Engine VM instance ## Overview In this setup, traffic from clients to be MiTM'd is routed through a Google diff --git a/docs/getting_started.md b/docs/getting_started.md index f6cf590e..3dd75670 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -1,10 +1,10 @@ -#Getting Started -##Files you’ll need to provide +# Getting Started +## Files you’ll need to provide Before running nogotofail there are some files you’ll need to create or provide. -###MiTM Server certificate +### MiTM Server certificate The connection between clients and the MiTM is protected by a self-signed @@ -16,7 +16,7 @@ For example the OpenSSL command to generate such a certificate is: $ openssl req -x509 -newkey rsa:2048 -sha256 -subj "/CN=mitm.nogotofail/" -nodes -keyout server.crt -out server.crt -###Invalid Hostname Certificate +### Invalid Hostname Certificate The Invalid hostname attack attempts a MiTM by presenting a trusted certificate for another domain name. For example a trusted certificate for evil.com being @@ -44,13 +44,13 @@ To verify the chain is correct $ openssl verify -CApath /etc/ssl/certs/ -untrusted trusted-cert.pem trusted-cert.pem You should see OK as the output. -###ImageReplace Image +### ImageReplace Image If you decide to use the image replacement data attack you’ll need to provide an image to replace with in the form of replace.png in nogotofail.mitm’s working directory. We recommend something noticeable that scales well. -##Example Walkthrough +## Example Walkthrough Here is a quick walkthrough of running and testing the MiTM locally. @@ -171,7 +171,7 @@ attack for later analysis. 6. The connection closes -###Getting on path +### Getting on path Now that you’ve set up nogotofail and seen how it runs the next step is to put @@ -199,7 +199,7 @@ OpenVPN as there is lots of documentation for how to set up an OpenVPN server. Our main setup has been OpenVPN running on a Google Compute Engine instance. See instructions in [gce/readme.md](gce/readme.md). -####Testing Android +#### Testing Android For testing Android devices we have included our [Android client](/nogotofail/clients/android) ready to be imported into Eclipse. You will have to build the app and install it on your test device. @@ -207,7 +207,7 @@ For testing you can use the access point nogotofail setups or on devices >=JB y the OpenVPN setup and a third party VPN application to route your traffic. -#####Getting on path on a Linux machine +##### Getting on path on a Linux machine On a Linux machine with the following example topology: @@ -237,7 +237,7 @@ Now traffic will be flowing through the MiTM box from the test device to the Internet. -###Now you’re on path +### Now you’re on path By default clients connect to the MiTM using hostname mitm.nogotofail @@ -255,7 +255,7 @@ in [example.conf](example.conf), and run it with `python -m nogotofail.mitm -c < If you’re running in an iptables mode you’ll also need to run nogotofail.mitm as root so it can set up the routing rules to intercept traffic. -####Useful arguments +#### Useful arguments @@ -286,7 +286,7 @@ important ones you’ll want to tweak. You can see all the options by running `python -m nogotofail.mitm --help`. -#####Logging +##### Logging Additionally, you will probably want to log to files in addition to stdout. diff --git a/docs/mitm.md b/docs/mitm.md index 22226ded..4870cfeb 100644 --- a/docs/mitm.md +++ b/docs/mitm.md @@ -1,5 +1,5 @@ -#MiTM Details -##How MiTM intercepts traffic +# MiTM Details +## How MiTM intercepts traffic Nogotofail currently supports three traffic interception modes that can be selected using the --mode option. The tproxy and redirect modes are @@ -7,49 +7,49 @@ transparent to the client and destination, and are for running nogotofail on-pat Note they require the MiTM to be run as root in order to create the iptables and routing rules to capture traffic. -###tproxy +### tproxy Tproxy mode uses iptables tproxy and ip mark routing rules to route all traffic passing through the device to nogotofail. -###redirect +### redirect Redirect mode uses iptables nat redirection to route all traffic passing through the device to nogotofail. Redirect is the older and more tested way of routing traffic to nogotofail but has poor IPv6 support, requiring bleeding edge iptables and Linux kernel >= 3.7. -###socks +### socks Socks mode has nogotofail listening as a SOCKS5 proxy. Unlike the iptables rules this doesn’t require nogotofail to be on path or have root access, but it does lose the transparency of those modes. It is also useful when testing changes to nogotofail locally, as it requires minimal setup. -#Architecture Overview +# Architecture Overview -##Connections +## Connections Every TCP connection is routed to a nogotofail connection which is responsible for bridging traffic, detecting TLS/SSL traffic and sending events to handlers which implement attacks and detection. -##Handlers +## Handlers All the actual vulnerability detection is done in small event handlers. In nogotofail there are two types of handlers, connection handlers and data handlers. You can see all the events handlers receive and their documentation in (nogotofail/mitm/connection/handlers/base.py)[nogotofail/mitm/connection/handlers/base.py] -###Connection Handlers +### Connection Handlers Each connection in nogotofail has only one connection handler which is responsible for doing connection level testing on TLS/SSL. These are used for vulnerabilities like accepting self-signed certificates or heartbleed. -###Data Handlers +### Data Handlers Data handlers are responsible for detecting issues in traffic or modifying traffic to test for vulnerabilities. Unlike Connection Handlers each connection can have multiple data handlers whose outputs are chained together. These are used for vulnerabilities like detecting auth tokens in cleartext or attempting ssl stripping attacks. -##Life of a connection +## Life of a connection 1. When a connection is first created it selects the initial connection handler and the list of data handler. 2. Each handler’s on_select method is called. 3. Once the connection to the remote is successful each handler’s on_establish is called @@ -58,7 +58,7 @@ ssl stripping attacks. 6. If a TLS/SSL Client Hello is detected by the connection the connection checks if it should MiTM’d. See ‘on TLS Client Hello’. 7. When the connection is closed each handler’s on_close is called -###On TLS/SSL Client Hello +### On TLS/SSL Client Hello 1. When a TLS/SSL client hello is detected the connection selects a new connection handler for the TLS/SSL connection. 2. If handler.on_ssl returns False the connection will continue simply bridging traffic as before 3. Otherwise the connection to the server is wrapped with TLS/SSL and handler.on_certificate is called with the cert presented by the server to generate a certificate to present to the client From 0128b243590db05ab98466d6629bbac76eacf642 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Mon, 24 Apr 2017 15:27:36 -0700 Subject: [PATCH 14/21] Fix fd leak in blame server Due to poor variable naming old clients from the same IP address were not getting removed as designed. This also cleans up the blame server code a bit, which has been untouched since nogotofail was a prototype and enables pruning of old clients. --- nogotofail/mitm/blame/app_blame.py | 47 +++++++++++++++++------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/nogotofail/mitm/blame/app_blame.py b/nogotofail/mitm/blame/app_blame.py index 87d3c2dd..eb7622ff 100644 --- a/nogotofail/mitm/blame/app_blame.py +++ b/nogotofail/mitm/blame/app_blame.py @@ -76,7 +76,7 @@ def on_select(self): def check_timeouts(self): """Returns if the connection or any of its callbacks have timed out.""" - now = time.time + now = time.time() if now - self.last_used > self.CLIENT_TIMEOUT: return False for callback in self.queries.values(): @@ -141,7 +141,7 @@ def _generate_on_vuln_notify_fn(self, callback): def on_vuln_notify(success, data=None): if not success: callback(False) - self.server.remove_client(self.address) + self.server.remove_client(self) return callback(True, data == "OK") return on_vuln_notify @@ -150,7 +150,7 @@ def _generate_on_get_applications_fn(self, callback): def on_get_applications(success, data=None): if not success: callback(False) - self.server.remove_client(self.address) + self.server.remove_client(self) return platform_info = self.info.get( @@ -323,6 +323,7 @@ def __init__(self, port, cert, default_prob, default_attacks, default_data): self.fd_map = {} self.logger = logging.getLogger("nogotofail.mitm") self.server_socket = None + self.last_prune = time.time() def start_listening(self): self.server_socket = socket.socket() @@ -349,18 +350,18 @@ def _on_server_socket_select(self): old_client = self.clients.get(client_addr, None) if old_client: - self.remove_client(client_address) - self.fd_map[client_socket] = client_addr - self.clients[client_addr] = Client(client_socket, self) + self.remove_client(old_client) + client = Client(client_socket, self) + self.fd_map[client_socket] = client + self.clients[client_addr] = client def _on_socket_select(self, sock): if sock is self.server_socket: self._on_server_socket_select() return - client_addr = self.fd_map[sock] - client = self.clients[client_addr] + client = self.fd_map[sock] if not client.on_select(): - self.remove_client(client_addr) + self.remove_client(client) def client_available(self, client_addr): """Returns if the app blame client is running on client_addr. @@ -388,7 +389,7 @@ def fn(success, platform_info=None, applications=None) return False if not self.clients[client_addr].get_applications_async(client_port, server_addr, server_port, callback, timeout): - self.remove_client(client_addr) + self.remove_client(self.clients[client_addr]) return False return True @@ -416,24 +417,24 @@ def callback(success, result=False) result = self.clients[client_addr].vuln_notify_async(server_addr, server_port, id, type, applications, callback, timeout) if not result: - self.remove_client(client_addr) + self.remove_client(self.clients[client_addr]) return result - def remove_client(self, client_addr): + def remove_client(self, client): """Remove and close a blame client.""" - if client_addr not in self.clients: + if client.address not in self.clients: return - client = self.clients[client_addr] - del self.clients[client_addr] + del self.clients[client.address] del self.fd_map[client.socket] client.close() - def check_timeouts(self): - """Check the timeouts on all clients and remove those that have timed out.""" - for client_addr in self.clients.keys(): - if not self.clients[client_addr].check_timeouts(): - self.logger.info("Blame: Client %s timed out", client_addr) - self.remove_client(client_addr) + def prune_old_clients(self): + """Prune old clients that have not been heard from in a long time""" + for client in self.clients.values(): + if not client.check_timeouts(): + self.logger.info("Blame: Client %s timed out", client.address) + self.remove_client(client) + @property def select_fds(self): @@ -446,6 +447,10 @@ def on_select(self, r, w, x): provided by select_fds.""" for fd in set(r + w + x): self._on_socket_select(fd) + # Prune old clients + if time.time() - self.last_prune > 3600: + self.prune_old_clients() + self.last_prune = time.time() def shutdown(self): """Shutdown the Blame server. The server should not be used after this point.""" From a94ef9445ee26eab5dd8b53f4491c3fa9156c1de Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Fri, 16 Jun 2017 09:20:49 -0700 Subject: [PATCH 15/21] Upgrade to OpenVPN 2.4.2 This enables us to: * Abandon the floating client patch because floating is a standard feature in OpenVPN 2.4. * Switch from DHE to ECDHE for the TLS connection and avoid wasting large amounts of time during setup to generate 2048-bit DH parameters. * Switch data channel packet encryption/authentication from AES-CBC with SHA-256 HMAC to AES-GCM. --- docs/gce/build_openvpn.sh | 15 +++---- docs/gce/nogotofail.ovpn.template | 4 +- docs/gce/openvpn-pgp-key.asc | 65 ++++++++++++++++++------------- docs/gce/openvpn.conf | 9 ++--- docs/gce/setup_openvpn.sh | 2 - 5 files changed, 47 insertions(+), 48 deletions(-) diff --git a/docs/gce/build_openvpn.sh b/docs/gce/build_openvpn.sh index 67772e71..1694ed73 100755 --- a/docs/gce/build_openvpn.sh +++ b/docs/gce/build_openvpn.sh @@ -2,27 +2,22 @@ set -e -OPENVPN_VERSION="2.3.5" +OPENVPN_VERSION="2.4.2" # Download OpenVPN and verify the signature on the archive rm -f openvpn-$OPENVPN_VERSION.tar.gz* -wget http://swupdate.openvpn.org/community/releases/openvpn-$OPENVPN_VERSION.tar.gz -wget http://swupdate.openvpn.org/community/releases/openvpn-$OPENVPN_VERSION.tar.gz.asc +wget https://swupdate.openvpn.org/community/releases/openvpn-$OPENVPN_VERSION.tar.gz +wget https://swupdate.openvpn.org/community/releases/openvpn-$OPENVPN_VERSION.tar.gz.asc rm -f tmp.keyring* gpg --no-default-keyring --keyring ./tmp.keyring --import openvpn-pgp-key.asc gpg --no-default-keyring --keyring ./tmp.keyring --verify openvpn-$OPENVPN_VERSION.tar.gz.asc rm -f tmp.keyring* -# Download the patch for improved handling of floating clients -rm -f tlsfloat.2.patch -wget https://community.openvpn.net/openvpn/raw-attachment/ticket/49/tlsfloat.2.patch - -# Unpack, patch, build, and install. +# Unpack, build, and install. rm -Rf openvpn-$OPENVPN_VERSION tar zxvf openvpn-$OPENVPN_VERSION.tar.gz cd openvpn-$OPENVPN_VERSION -patch -p1 < ../tlsfloat.2.patch -./configure +./configure --prefix=/usr make sudo make install cd - diff --git a/docs/gce/nogotofail.ovpn.template b/docs/gce/nogotofail.ovpn.template index 65f1239e..d637a97e 100644 --- a/docs/gce/nogotofail.ovpn.template +++ b/docs/gce/nogotofail.ovpn.template @@ -20,6 +20,4 @@ comp-lzo remote-cert-tls server # Symmetric tunnel crypto config -auth SHA256 -cipher AES-128-CBC -keysize 128 +cipher AES-128-GCM diff --git a/docs/gce/openvpn-pgp-key.asc b/docs/gce/openvpn-pgp-key.asc index c5135814..66d89877 100644 --- a/docs/gce/openvpn-pgp-key.asc +++ b/docs/gce/openvpn-pgp-key.asc @@ -1,30 +1,41 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.4.9 (GNU/Linux) +Version: GnuPG v1 -mQGiBEsHuu4RBACnPwEKcLYmlwe8v2e8xizlO1fCeqOA7zj6tU/T/1+YTJhrVbgW -PiRYSNKAmAq0uLFLQ14KpIDsrtdi5ySeUTf64kJtDrBa2si6h0HUyNHf9EX6rUVC -g/CTpsfYEkqlfMoBH7w7L5O2yidwWA+F4RGWhruzP7i1z+bBsIguSxiBzwCg5qPh -pgkFGeWArp/OUBHkaqmPZ00D/08dmkrez9d7C/PoR/cFq0nQBqL3zmsRxv66I6fM -TUqwaRpweWHh9P6XR+pTJjBglVSvk9kLv+PYCvk7yxbT3M6OA/GrSEp/53itlzOU -MPkv/OF6BmbRbYJK5HAsZgHGbuZxUHUqm4qJ+t4+WZaz9i8WtYbOM6T9aNWQrVUW -dUMqA/4tZlHJzCrd1NbfEetQVeso9rzzWWWmDAusbvkowfrFHXJGUjfL0hBmxj/9 -JmZtwU+i8G+MKQS0w9rCVLEMLoHLLxPH+Jiknz3Y2xE6CbiSvL+8cvOolgADz/06 -MniHKOZb4tPFPw7ObESeAGp4T9FgT53fJ14AMjGLyHv6EXbfvbQsU2FtdWxpIFNl -cHDDpG5lbiA8c2FtdWxpLnNlcHBhbmVuQGdtYWlsLmNvbT6IYAQTEQIAIAUCSwe6 -7gIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEMKdl+0ZjSKjsfsAoK6khXtq -w2xDtNBv/UhRhuVH0NQOAKCDWiB2zYNvHWLYnuIpAlE1sAnGPrkCDQRLB7ruEAgA -jwSEfTWLJsIW5qlKNEhySIjmRmcVgqB/NTaZ+Nd/r++stYSan1qb7qlQ3B3w48p1 -gB0SPfwKRmMkiYsHNcbRr5KIHWTnYrMI/5OAjPIiz/2j294wRnObzrbJK3T+qJSL -A2mEXXPPK7i0hUwH12ZJej/h98lPQA+NiDgDOaq4asyq4pcHrR2T2NyiiR2+Xi/L -2Lz1zKj4iQ3f5g0ktmAEdGcDtV7tI0xZeXWEtesRXeXmqPmjLskJozUoZP1GXXAz -80PySK2HsEQ/846q1Ybl5KYwbSH+l8jLIyqMDTQnCYG+Ft1moCk3HLyc4c1ALVov -1Rvom8u3dM5tUtpuZMwcJwADBQf+MCohqLqGJmEdiTEnmggsiKSoZTIBJhcujRaL -pxPpBlXz6P2bvlprUedBs+zxEEI+Q/CqIlyYaN+Kca1FK4YG9iQoHmb9IIVHf4C/ -lyWSx1xK+BnIk7SEfMjpGAjofNzNc34NmebnosHfP/g3ruLo6EgtjQ68iUty9PgX -Q1bZQ/SeXk16b8Nn0xQa9S+hg5LAxA+DuSvXbMqU5q2p8JlPgGEFVKzaVcxPhppB -Kcv/2CxjsqXj/6sW3nFSw+8Jd4SWL1+cPZ1v1WHG3SUMFoLAjSmVj3X8roG5EiLi -QxSGOUz8uVtvumfKyd25MYmgHMELL7fxhrZcw2OVdo977lt2fIhJBBgRAgAJBQJL -B7ruAhsMAAoJEMKdl+0ZjSKjgrAAoLeln17YxSQA7RUHwTbquOA92odMAKDiq7c8 -p2hUs3rZaXY1aMmExyB0gQ== -=l5lk +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 +XuwT7RpGDZ6jBfphYQ== +=FKLE -----END PGP PUBLIC KEY BLOCK----- diff --git a/docs/gce/openvpn.conf b/docs/gce/openvpn.conf index 900aa9fa..0d47c659 100644 --- a/docs/gce/openvpn.conf +++ b/docs/gce/openvpn.conf @@ -57,12 +57,9 @@ ca /etc/openvpn/ca_cert.pem cert /etc/openvpn/server_cert.pem key /etc/openvpn/server_key.pem -dh /etc/openvpn/dhparam2048.pem +dh none -# ECDHE isn't yet supported by OpenVPN (as of 2.3)... -tls-cipher DHE-RSA-AES128-GCM-SHA256,DHE-RSA-AES128-SHA256,DHE-RSA-AES128-SHA +tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384 # Symmetric tunnel crypto config -auth SHA256 -cipher AES-128-CBC -keysize 128 +cipher AES-256-GCM diff --git a/docs/gce/setup_openvpn.sh b/docs/gce/setup_openvpn.sh index 3d6a6a94..bf313b24 100755 --- a/docs/gce/setup_openvpn.sh +++ b/docs/gce/setup_openvpn.sh @@ -29,8 +29,6 @@ openssl req -new -key $CONFIG_DIR/client_key.pem -out $CONFIG_DIR/client_csr.pem openssl x509 -req -in $CONFIG_DIR/client_csr.pem -CA $CONFIG_DIR/ca_cert.pem -CAkey $CONFIG_DIR/ca_key.pem -CAcreateserial -out $CONFIG_DIR/client_cert.pem -sha256 -days 365 -extfile openvpn-client-cert-extfile.cfg rm $CONFIG_DIR/client_csr.pem -openssl dhparam 2048 > $CONFIG_DIR/dhparam2048.pem - cp "$SRC_DIR/openvpn.conf" $CONFIG_DIR/ # Determine external IP address of this host. From 824ca9267081f057155a5c5b2062cc5821dfd724 Mon Sep 17 00:00:00 2001 From: klyubin Date: Mon, 26 Jun 2017 11:06:42 -0700 Subject: [PATCH 16/21] Simplify IPv6 config (#112) Now that we switched to OpenVPN 2.4, we no longer need the client-side IPv6 blackholing trick. Remove! --- docs/gce/nogotofail.ovpn.template | 3 +-- docs/gce/openvpn.conf | 11 +---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/docs/gce/nogotofail.ovpn.template b/docs/gce/nogotofail.ovpn.template index d637a97e..b3ad1c7a 100644 --- a/docs/gce/nogotofail.ovpn.template +++ b/docs/gce/nogotofail.ovpn.template @@ -3,8 +3,7 @@ proto udp client verb 4 dev tun -tun-ipv6 -redirect-gateway +redirect-gateway ipv6 # keepalive 60 120 ping 60 diff --git a/docs/gce/openvpn.conf b/docs/gce/openvpn.conf index 0d47c659..e2a540fb 100644 --- a/docs/gce/openvpn.conf +++ b/docs/gce/openvpn.conf @@ -3,6 +3,7 @@ proto udp dev tun server 10.8.0.0 255.255.255.0 +server-ipv6 fd12:3456:789a:bcde::/64 topology subnet # Because GCE doesn't support IPv6 we @@ -11,16 +12,6 @@ topology subnet # This forces clients to switch to using IPv4 addresses. push "dhcp-option DNS 10.8.0.1" -# Blackhole IPv6 traffic because GCE does not support IPv6. -# This is achieved by making OpenVPN server have a fake IPv6 address -# (otherwise OpenVPN server will not push IPv6 information to -# client) and pushing a route to the client to blackhole IPv6 traffic -# client-side. -server-ipv6 2001:db8:123::/64 -# OpenVPN 2.3 doesn't work well with IPv6. Push a route to client -# to blackhole IPv6 traffic on the client. -push "route-ipv6 2000::/3" - # Enabling floating mode to work around the issue where # some clients's source IP+port may change mid-session because # of NAT. From 8e15a1c919c9978d53240fa3465211ec29683637 Mon Sep 17 00:00:00 2001 From: Igor Ganapolsky Date: Thu, 6 Jul 2017 15:04:22 -0400 Subject: [PATCH 17/21] grammar fix --- docs/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 3dd75670..334730c7 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -21,7 +21,7 @@ For example the OpenSSL command to generate such a certificate is: The Invalid hostname attack attempts a MiTM by presenting a trusted certificate for another domain name. For example a trusted certificate for evil.com being presented for a connection to example.com. If the application does not do -hostname verification correctly it will incorrect trust the MiTM. This has +hostname verification correctly, it will incorrectly trust the MiTM. This has historically been one of the common SSL issues besides not checking chain of trust of SSL certificates. To test for this issue you will need to provide a trusted certificate chain for an arbitrary domain. You have two options for how From c73070ca3098faab2994a83a9249d7c973151aa4 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Tue, 18 Jul 2017 12:32:33 -0700 Subject: [PATCH 18/21] Fix issue with ipv6 sockets and SO_ORIGINAL_DST Fixes issue #114 --- nogotofail/mitm/connection/connection.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nogotofail/mitm/connection/connection.py b/nogotofail/mitm/connection/connection.py index 65c0b753..3b94d026 100644 --- a/nogotofail/mitm/connection/connection.py +++ b/nogotofail/mitm/connection/connection.py @@ -719,13 +719,16 @@ def start(self): return True def _get_original_dest(self, sock): + family = sock.family SO_ORIGINAL_DST = 80 - dst = sock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 28) - family = struct.unpack_from("H", dst)[0] # Parse the raw_ip and raw port from the struct sockaddr_in/in6 if family == socket.AF_INET: + dst = sock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 28) raw_port, raw_ip = struct.unpack_from("!2xH4s", dst) elif family == socket.AF_INET6: + # From socket.h + SOL_IPV6 = 41 + dst = sock.getsockopt(SOL_IPV6, SO_ORIGINAL_DST, 64) raw_port, raw_ip = struct.unpack_from("!2xH4x16s", dst) else: raise ValueError("Unsupported sa_family_t %d" % family) From 7c683b066c78ed5c3cd3766de842619d8828b620 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Thu, 16 Jan 2020 00:33:09 -0800 Subject: [PATCH 19/21] Add detection for CVE-2020-0601 This is based on the private key = 1 with G = Q in a new group attack. The Q used was the public key of one of the trusted CAs who had an EC key. Test: Locally tested with Windows device with and without fixes deployed. --- explicit_curve.pem | 35 +++++++++++++++++++ .../handlers/connection/selfsigned.py | 8 +++++ nogotofail/mitm/util/vuln.py | 1 + 3 files changed, 44 insertions(+) create mode 100644 explicit_curve.pem diff --git a/explicit_curve.pem b/explicit_curve.pem new file mode 100644 index 00000000..2b63fc34 --- /dev/null +++ b/explicit_curve.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIDpTCCAyygAwIBAgIUaM8IdFkj075azIaOuFQjCezhxsAwCgYIKoZIzj0EAwIw +XjELMAkGA1UEBhMCTkExEzARBgNVBAgMCk5vZ290b2ZhaWwxEzARBgNVBAoMCk5v +Z290b2ZhaWwxDTALBgNVBAsMBE1pVE0xFjAUBgNVBAMMDUNWRS0yMDIwLTA2MDEw +HhcNMjAwMTE2MDcwNzQ4WhcNMjIxMTA1MDcwNzQ4WjBeMQswCQYDVQQGEwJOQTET +MBEGA1UECAwKTm9nb3RvZmFpbDETMBEGA1UECgwKTm9nb3RvZmFpbDENMAsGA1UE +CwwETWlUTTEWMBQGA1UEAwwNQ1ZFLTIwMjAtMDYwMTCCAcwwggFkBgcqhkjOPQIB +MIIBVwIBATA8BgcqhkjOPQEBAjEA//////////////////////////////////// +//////7/////AAAAAAAAAAD/////MHsEMP////////////////////////////// +///////////+/////wAAAAAAAAAA/////AQwszEvp+I+5+SYjgVr4/gtGRgdnG7+ +gUESAxQIj1ATh1rGVjmNii7RnSqFyO3T7CrvAxUAozWSaqMZonodAIlqZ3OkgnrN +rHMEYQQarFRaqfloI+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO +7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ +ijTjeXkCMQD////////////////////////////////HY02B9Dct31gaDbJIsKd6 +7OwZaszFKXMCAQEDYgAEGqxUWqn5aCPnetUkb1PGWthLq8bVttHmc3Gu3ZzWDGH9 +26CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8ZncJZFjq38wo7Rw4sehM5zz +vy5cU7Ffs30yf4o043l5o1MwUTAdBgNVHQ4EFgQUOuEJhtTPGcKWdnRJdtzgNcZj +Y5owHwYDVR0jBBgwFoAUOuEJhtTPGcKWdnRJdtzgNcZjY5owDwYDVR0TAQH/BAUw +AwEB/zAKBggqhkjOPQQDAgNnADBkAjB23iiHtHfRRQIEDKxoy5h9ZMf4Ki37Shxs +vCmTk4lvMAUDYlE+ls43g2yDsFh77gMCMAavhDOlDlysDs0+T/KF9KirXVxWVJu1 +iW4LXegLiRSRPOw07z+SoTluCyhXX6igZA== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICDAIBADCCAWQGByqGSM49AgEwggFXAgEBMDwGByqGSM49AQECMQD///////// +/////////////////////////////////v////8AAAAAAAAAAP////8wewQw//// +//////////////////////////////////////7/////AAAAAAAAAAD////8BDCz +MS+n4j7n5JiOBWvj+C0ZGB2cbv6BQRIDFAiPUBOHWsZWOY2KLtGdKoXI7dPsKu8D +FQCjNZJqoxmieh0AiWpnc6SCes2scwRhBBqsVFqp+Wgj53rVJG9TxlrYS6vG1bbR +5nNxrt2c1gxh/dugiQO4BRTsV87uXT/iIbPO99SKeeCjg34tl9BhxPGZ3CWRY6t/ +MKO0cOLHoTOc878uXFOxX7N9Mn+KNON5eQIxAP////////////////////////// +/////8djTYH0Ny3fWBoNskiwp3rs7BlqzMUpcwIBAQSBnjCBmwIBAQQwAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoWQDYgAE +GqxUWqn5aCPnetUkb1PGWthLq8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ih +s8731Ip54KODfi2X0GHE8ZncJZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5 +-----END PRIVATE KEY----- diff --git a/nogotofail/mitm/connection/handlers/connection/selfsigned.py b/nogotofail/mitm/connection/handlers/connection/selfsigned.py index a3bb54a3..6fab212c 100644 --- a/nogotofail/mitm/connection/handlers/connection/selfsigned.py +++ b/nogotofail/mitm/connection/handlers/connection/selfsigned.py @@ -93,3 +93,11 @@ class SuperFishMITM(SelfSignedMITM): description = "Attempt a MiTM using the compromised superfish MITM CA" ca = util.CertificateAuthority("superfish.pem") vuln = util.vuln.VULN_TLS_SUPERFISH_TRUSTED + +@handler(handlers, default=True) +@preconditions.requires_files(files=["explicit_curve.pem"]) +class SuperFishMITM(SelfSignedMITM): + name = "explicitcurvemitm" + description = "Attempt a MiTM exploiting CVE-2020-0601" + ca = util.CertificateAuthority("explicit_curve.pem") + vuln = util.vuln.VULN_TLS_EXPLICIT_CURVE diff --git a/nogotofail/mitm/util/vuln.py b/nogotofail/mitm/util/vuln.py index 2de2f0f4..d85068f0 100644 --- a/nogotofail/mitm/util/vuln.py +++ b/nogotofail/mitm/util/vuln.py @@ -30,3 +30,4 @@ VULN_WEAK_TLS_VERSION = "weaktlsversion" VULN_TLS_SERVER_KEY_REPLACEMENT = "serverkeyreplace" VULN_TLS_SUPERFISH_TRUSTED = "superfishca" +VULN_TLS_EXPLICIT_CURVE = "explicitcurve" From 46af0e3862f2f981df94bbfe5865aee06c511659 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Thu, 16 Jan 2020 01:38:11 -0800 Subject: [PATCH 20/21] Fix ca code 1) Move to sha256 to avoid sha1 deprecation 2) Fix date usage to avoid parse errors --- nogotofail/mitm/util/ca.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/nogotofail/mitm/util/ca.py b/nogotofail/mitm/util/ca.py index c7e47545..fbb19d1c 100644 --- a/nogotofail/mitm/util/ca.py +++ b/nogotofail/mitm/util/ca.py @@ -15,6 +15,8 @@ ''' import tempfile import OpenSSL.crypto +import atexit +import shutil import os import random from nogotofail.mitm.util import extras @@ -22,8 +24,11 @@ class CertificateAuthority(object): """Simple CA for generating certs based on CNs and sans.""" - def __init__(self, ca_file='ca.pem', cert_dir=tempfile.gettempdir()): + def __init__(self, ca_file='ca.pem', cert_dir=None): self.ca_file = ca_file + if cert_dir is None: + cert_dir = tempfile.mkdtemp(prefix="ngtf_ca_" + ca_file) + atexit.register(shutil.rmtree, cert_dir) self.cert_dir = cert_dir self._loaded = False @@ -45,8 +50,8 @@ def _generate_ca(self): self.cert.set_version(2) self.cert.set_serial_number(1) self.cert.get_subject().CN = 'ca.nogotofail' - self.cert.set_notBefore("19300101000000+0000") - self.cert.set_notAfter("20301231000000+0000") + cert.set_notBefore("20200101000000Z") + cert.set_notAfter("20210101000000Z") self.cert.set_issuer(self.cert.get_subject()) self.cert.set_pubkey(self.key) self.cert.add_extensions([ @@ -63,7 +68,7 @@ def _generate_ca(self): False, 'hash', subject=self.cert)]) - self.cert.sign(self.key, 'sha1') + self.cert.sign(self.key, 'sha256') with open(self.ca_file, 'w') as f: f.write( @@ -94,12 +99,12 @@ def _generate_cert(self, cn, san, path): cert.set_pubkey(key) cert.set_serial_number(random.randint(0, 2**20)) # Use a huge range so we dont have to worry about bad clocks - cert.set_notBefore("19300101000000+0000") - cert.set_notAfter("20301231000000+0000") + cert.set_notBefore("20200101000000Z") + cert.set_notAfter("20210101000000Z") cert.set_issuer(self.cert.get_subject()) if san: cert.add_extensions([san]) - cert.sign(self.key, 'sha1') + cert.sign(self.key, 'sha256') with open(path, 'w') as f: f.write( From 003c068f5febb4709cc70ad7ce516c3240eb4e41 Mon Sep 17 00:00:00 2001 From: Chad Brubaker Date: Thu, 16 Jan 2020 01:56:24 -0800 Subject: [PATCH 21/21] Fix classname of ExplicitCurveMiTM --- nogotofail/mitm/connection/handlers/connection/selfsigned.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nogotofail/mitm/connection/handlers/connection/selfsigned.py b/nogotofail/mitm/connection/handlers/connection/selfsigned.py index 6fab212c..57af7bef 100644 --- a/nogotofail/mitm/connection/handlers/connection/selfsigned.py +++ b/nogotofail/mitm/connection/handlers/connection/selfsigned.py @@ -96,7 +96,7 @@ class SuperFishMITM(SelfSignedMITM): @handler(handlers, default=True) @preconditions.requires_files(files=["explicit_curve.pem"]) -class SuperFishMITM(SelfSignedMITM): +class ExplicitCurveMiTM(SelfSignedMITM): name = "explicitcurvemitm" description = "Attempt a MiTM exploiting CVE-2020-0601" ca = util.CertificateAuthority("explicit_curve.pem")