From 7450bef615436d39bcd2a0d2a8892b8f42beea6f Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 31 Aug 2015 13:43:30 +0200 Subject: [PATCH] fix dns_spoofing example, avoid connecting to itself --- examples/dns_spoofing.py | 47 ++++++++++++++++++++----------- libmproxy/models/connections.py | 2 +- libmproxy/models/flow.py | 2 +- libmproxy/protocol/base.py | 21 +++++++++++++- libmproxy/protocol/http_replay.py | 1 + libmproxy/proxy/config.py | 2 +- libmproxy/proxy/server.py | 2 +- 7 files changed, 55 insertions(+), 22 deletions(-) diff --git a/examples/dns_spoofing.py b/examples/dns_spoofing.py index dddf172cc..98495d45b 100644 --- a/examples/dns_spoofing.py +++ b/examples/dns_spoofing.py @@ -8,30 +8,43 @@ Similarly, if there's no Host header or a spoofed Host header, we're out of luck Using transparent mode is the better option most of the time. Usage: - mitmproxy - -p 80 - -R http://example.com/ // Used as the target location if no Host header is present mitmproxy -p 443 - -R https://example.com/ // Used as the target locaction if neither SNI nor host header are present. + -s dns_spoofing.py + # Used as the target location if neither SNI nor host header are present. + -R http://example.com/ + mitmdump + -p 80 + -R http://localhost:443/ -mitmproxy will always connect to the default location first, so it must be reachable. -As a workaround, you can spawn an arbitrary HTTP server and use that for both endpoints, e.g. -mitmproxy -p 80 -R http://localhost:8000 -mitmproxy -p 443 -R https2http://localhost:8000 + (Setting up a single proxy instance and using iptables to redirect to it + works as well) """ +import re + + +# This regex extracts splits the host header into host and port. +# Handles the edge case of IPv6 addresses containing colons. +# https://bugzilla.mozilla.org/show_bug.cgi?id=45891 +parse_host_header = re.compile(r"^(?P[^:]+|\[.+\])(?::(?P\d+))?$") def request(context, flow): if flow.client_conn.ssl_established: - # TLS SNI or Host header - flow.request.host = flow.client_conn.connection.get_servername( - ) or flow.request.pretty_host(hostheader=True) - - # If you use a https2http location as default destination, these - # attributes need to be corrected as well: - flow.request.port = 443 flow.request.scheme = "https" + sni = flow.client_conn.connection.get_servername() + port = 443 else: - # Host header - flow.request.host = flow.request.pretty_host(hostheader=True) + flow.request.scheme = "http" + sni = None + port = 80 + + host_header = flow.request.pretty_host(hostheader=True) + m = parse_host_header.match(host_header) + if m: + host_header = m.group("host").strip("[]") + if m.group("port"): + port = int(m.group("port")) + + flow.request.host = sni or host_header + flow.request.port = port \ No newline at end of file diff --git a/libmproxy/models/connections.py b/libmproxy/models/connections.py index 98bae3cc2..f1e10de9f 100644 --- a/libmproxy/models/connections.py +++ b/libmproxy/models/connections.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import (absolute_import, print_function, division) import copy import os diff --git a/libmproxy/models/flow.py b/libmproxy/models/flow.py index 58287e5b8..8eff18f4a 100644 --- a/libmproxy/models/flow.py +++ b/libmproxy/models/flow.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import (absolute_import, print_function, division) import copy import uuid diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py index 3440cb018..d1af547fe 100644 --- a/libmproxy/protocol/base.py +++ b/libmproxy/protocol/base.py @@ -103,6 +103,7 @@ class ServerConnectionMixin(object): def __init__(self, server_address=None): super(ServerConnectionMixin, self).__init__() self.server_conn = ServerConnection(server_address) + self._check_self_connect() def reconnect(self): address = self.server_conn.address @@ -110,12 +111,30 @@ class ServerConnectionMixin(object): self.server_conn.address = address self.connect() + def _check_self_connect(self): + """ + We try to protect the proxy from _accidentally_ connecting to itself, + e.g. because of a failed transparent lookup or an invalid configuration. + """ + address = self.server_conn.address + if address: + self_connect = ( + address.port == self.config.port and + address.host in ("localhost", "127.0.0.1", "::1") + ) + if self_connect: + raise ProtocolException( + "Invalid server address: {}\r\n" + "The proxy shall not connect to itself.".format(repr(address)) + ) + def set_server(self, address, server_tls=None, sni=None, depth=1): if depth == 1: if self.server_conn: self._disconnect() self.log("Set new server address: " + repr(address), "debug") self.server_conn.address = address + self._check_self_connect() if server_tls: raise ProtocolException( "Cannot upgrade to TLS, no TLS layer on the protocol stack." @@ -141,7 +160,7 @@ class ServerConnectionMixin(object): self.server_conn.connect() except tcp.NetLibError as e: raise ProtocolException( - "Server connection to '%s' failed: %s" % (self.server_conn.address, e), e) + "Server connection to %s failed: %s" % (repr(self.server_conn.address), e), e) class Log(object): diff --git a/libmproxy/protocol/http_replay.py b/libmproxy/protocol/http_replay.py index e0144c933..c37fd131d 100644 --- a/libmproxy/protocol/http_replay.py +++ b/libmproxy/protocol/http_replay.py @@ -1,3 +1,4 @@ +from __future__ import (absolute_import, print_function, division) import threading from netlib.http import HttpError diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py index 650290872..8d2a286dd 100644 --- a/libmproxy/proxy/config.py +++ b/libmproxy/proxy/config.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import (absolute_import, print_function, division) import collections import os import re diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index 2a451ba19..b565ef860 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, print_function, division) import traceback import sys