From a6e0c7e8f0c20276f2f7cb2d9332a806e8493c18 Mon Sep 17 00:00:00 2001 From: smill Date: Sat, 3 Sep 2016 12:22:09 +0000 Subject: [PATCH 01/14] Introduced the capability to spoof the source address of outgoing sessions + an accompanying shim loader. --- mitmproxy/cmdline.py | 7 ++- mitmproxy/contrib/mitmproxy_shim.c | 71 ++++++++++++++++++++++++++++++ mitmproxy/models/connections.py | 4 +- mitmproxy/options.py | 2 + mitmproxy/protocol/base.py | 16 +++++-- netlib/tcp.py | 8 +++- 6 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 mitmproxy/contrib/mitmproxy_shim.c diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py index d888b93f0..2191cd952 100644 --- a/mitmproxy/cmdline.py +++ b/mitmproxy/cmdline.py @@ -255,6 +255,7 @@ def get_common_options(args): listen_port = args.port, mode = mode, no_upstream_cert = args.no_upstream_cert, + spoof_source_address = args.spoof_source_address, rawtcp = args.rawtcp, upstream_server = upstream_server, upstream_auth = args.upstream_auth, @@ -474,7 +475,11 @@ def proxy_options(parser): "Disabled by default. " "Default value will change in a future version." ) - + group.add_argument( + "--spoof-source-address", + action="store_true", dest="spoof_source_address", + help="Use client's IP for the server-side connection" + ) def proxy_ssl_options(parser): # TODO: Agree to consistently either use "upstream" or "server". diff --git a/mitmproxy/contrib/mitmproxy_shim.c b/mitmproxy/contrib/mitmproxy_shim.c new file mode 100644 index 000000000..b90be5662 --- /dev/null +++ b/mitmproxy/contrib/mitmproxy_shim.c @@ -0,0 +1,71 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +int set_caps(cap_t cap_struct, cap_value_t *caps, int len) { + if (cap_set_flag(cap_struct, CAP_PERMITTED, len, caps, CAP_SET) || + cap_set_flag(cap_struct, CAP_EFFECTIVE, len, caps, CAP_SET) || + cap_set_flag(cap_struct, CAP_INHERITABLE, len, caps, CAP_SET)) { + if (len < 2) { + fprintf(stderr, "Cannot manipulate capability data structure as user: %s.\n", strerror(errno)); + } else { + fprintf(stderr, "Cannot manipulate capability data structure as root: %s.\n", strerror(errno)); + } + + return 7; + } + + if (len < 2) { + if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0)) { + fprintf(stderr, "Failed to add CAP_NET_RAW to the ambient set: %s.\n", strerror(errno)); + return 88; + } + } + + if (cap_set_proc(cap_struct)) { + if (len < 2) { + fprintf(stderr, "Cannot set capabilities as user: %s.\n", strerror(errno)); + } else { + fprintf(stderr, "Cannot set capabilities as root: %s.\n", strerror(errno)); + } + return 1; + } + + if (len > 1) { + if (prctl(PR_SET_KEEPCAPS, 1L)) { + fprintf(stderr, "Cannot keep capabilities after dropping privileges: %s.\n", strerror(errno)); + return 4; + } + if (cap_clear(cap_struct)) { + fprintf(stderr, "Cannot clear capability data structure: %s.\n", strerror(errno)); + return 6; + } + } +} + +int main(int argc, char **argv, char **envp) { + cap_t cap_struct = cap_init(); + cap_value_t root_caps[2] = { CAP_NET_RAW, CAP_SETUID }; + cap_value_t user_caps[1] = { CAP_NET_RAW }; + uid_t user = getuid(); + + if (setresuid(0, 0, 0)) { + fprintf(stderr, "Cannot switch to root: %s.\n", strerror(errno)); + return 1; + } + + set_caps(cap_struct, root_caps, 2); + if (setresuid(user, user, user)) { + fprintf(stderr, "Cannot drop root privileges: %s.\n", strerror(errno)); + return 5; + } + set_caps(cap_struct, user_caps, 1); + + if (execve(argv[1], argv + 1, envp)) + perror("Cannot exec"); +} diff --git a/mitmproxy/models/connections.py b/mitmproxy/models/connections.py index 570e89a9a..2cab6e4af 100644 --- a/mitmproxy/models/connections.py +++ b/mitmproxy/models/connections.py @@ -123,8 +123,8 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject): timestamp_end: Connection end timestamp """ - def __init__(self, address, source_address=None): - tcp.TCPClient.__init__(self, address, source_address) + def __init__(self, address, source_address=None, spoof_source_address=None): + tcp.TCPClient.__init__(self, address, source_address, spoof_source_address) self.via = None self.timestamp_start = None diff --git a/mitmproxy/options.py b/mitmproxy/options.py index 75798381d..9ef1f108d 100644 --- a/mitmproxy/options.py +++ b/mitmproxy/options.py @@ -69,6 +69,7 @@ class Options(optmanager.OptManager): mode = "regular", # type: str no_upstream_cert = False, # type: bool rawtcp = False, # type: bool + spoof_source_address = False, # type: bool upstream_server = "", # type: str upstream_auth = "", # type: str ssl_version_client="secure", # type: str @@ -126,6 +127,7 @@ class Options(optmanager.OptManager): self.mode = mode self.no_upstream_cert = no_upstream_cert self.rawtcp = rawtcp + self.spoof_source_address = spoof_source_address self.upstream_server = upstream_server self.upstream_auth = upstream_auth self.ssl_version_client = ssl_version_client diff --git a/mitmproxy/protocol/base.py b/mitmproxy/protocol/base.py index bf0cbbae7..eed0b292e 100644 --- a/mitmproxy/protocol/base.py +++ b/mitmproxy/protocol/base.py @@ -114,7 +114,13 @@ class ServerConnectionMixin(object): def __init__(self, server_address=None): super(ServerConnectionMixin, self).__init__() - self.server_conn = models.ServerConnection(server_address, (self.config.options.listen_host, 0)) + + self.server_conn = None + if self.config.options.spoof_source_address: + self.server_conn = models.ServerConnection(server_address, (self.ctx.client_conn.address.host, 0), True) + else: + self.server_conn = models.ServerConnection(server_address, (self.config.options.listen_host, 0)) + self.__check_self_connect() def __check_self_connect(self): @@ -151,11 +157,15 @@ class ServerConnectionMixin(object): """ self.log("serverdisconnect", "debug", [repr(self.server_conn.address)]) address = self.server_conn.address - source_address = self.server_conn.source_address self.server_conn.finish() self.server_conn.close() self.channel.tell("serverdisconnect", self.server_conn) - self.server_conn = models.ServerConnection(address, (source_address.host, 0)) + + if self.config.options.spoof_source_address: + self.server_conn = models.ServerConnection(address, (self.ctx.client_conn.address.host, 0), True) + else: + self.server_conn = models.ServerConnection(address, (self.server_conn.source_address.host, 0)) + def connect(self): """ diff --git a/netlib/tcp.py b/netlib/tcp.py index e5c841655..aaea9459e 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -605,7 +605,7 @@ class ConnectionCloser(object): class TCPClient(_Connection): - def __init__(self, address, source_address=None): + def __init__(self, address, source_address=None, spoof_source_address=None): super(TCPClient, self).__init__(None) self.address = address self.source_address = source_address @@ -613,6 +613,7 @@ class TCPClient(_Connection): self.server_certs = [] self.ssl_verification_error = None # type: Optional[exceptions.InvalidCertificateException] self.sni = None + self.spoof_source_address = spoof_source_address @property def address(self): @@ -729,6 +730,11 @@ class TCPClient(_Connection): def connect(self): try: connection = socket.socket(self.address.family, socket.SOCK_STREAM) + if self.spoof_source_address: + if os.geteuid() != 0: + raise RuntimeError("Insufficient privileges to set socket option") + else: + connection.setsockopt(socket.SOL_IP, 19, 1) if self.source_address: connection.bind(self.source_address()) connection.connect(self.address()) From 377921fa99e5c602ff04ed412be76072abc1d1c0 Mon Sep 17 00:00:00 2001 From: smill Date: Sat, 3 Sep 2016 13:34:17 +0000 Subject: [PATCH 02/14] Introduced some minor improvements to the shim loader. --- mitmproxy/contrib/mitmproxy_shim.c | 46 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/mitmproxy/contrib/mitmproxy_shim.c b/mitmproxy/contrib/mitmproxy_shim.c index b90be5662..9688bb415 100644 --- a/mitmproxy/contrib/mitmproxy_shim.c +++ b/mitmproxy/contrib/mitmproxy_shim.c @@ -7,43 +7,44 @@ #include #include -int set_caps(cap_t cap_struct, cap_value_t *caps, int len) { - if (cap_set_flag(cap_struct, CAP_PERMITTED, len, caps, CAP_SET) || - cap_set_flag(cap_struct, CAP_EFFECTIVE, len, caps, CAP_SET) || - cap_set_flag(cap_struct, CAP_INHERITABLE, len, caps, CAP_SET)) { - if (len < 2) { +int set_caps(cap_t cap_struct, cap_value_t *cap_list, size_t bufsize) { + int cap_count = bufsize / sizeof(cap_list[0]); + + if (cap_set_flag(cap_struct, CAP_PERMITTED, cap_count, cap_list, CAP_SET) || + cap_set_flag(cap_struct, CAP_EFFECTIVE, cap_count, cap_list, CAP_SET) || + cap_set_flag(cap_struct, CAP_INHERITABLE, cap_count, cap_list, CAP_SET)) { + if (cap_count < 2) { fprintf(stderr, "Cannot manipulate capability data structure as user: %s.\n", strerror(errno)); } else { fprintf(stderr, "Cannot manipulate capability data structure as root: %s.\n", strerror(errno)); } - - return 7; + return -1; } - if (len < 2) { + if (cap_count < 2) { if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0)) { fprintf(stderr, "Failed to add CAP_NET_RAW to the ambient set: %s.\n", strerror(errno)); - return 88; + return -2; } } if (cap_set_proc(cap_struct)) { - if (len < 2) { + if (cap_count < 2) { fprintf(stderr, "Cannot set capabilities as user: %s.\n", strerror(errno)); } else { fprintf(stderr, "Cannot set capabilities as root: %s.\n", strerror(errno)); } - return 1; + return -3; } - if (len > 1) { + if (cap_count > 1) { if (prctl(PR_SET_KEEPCAPS, 1L)) { fprintf(stderr, "Cannot keep capabilities after dropping privileges: %s.\n", strerror(errno)); - return 4; + return -4; } if (cap_clear(cap_struct)) { fprintf(stderr, "Cannot clear capability data structure: %s.\n", strerror(errno)); - return 6; + return -5; } } } @@ -53,19 +54,26 @@ int main(int argc, char **argv, char **envp) { cap_value_t root_caps[2] = { CAP_NET_RAW, CAP_SETUID }; cap_value_t user_caps[1] = { CAP_NET_RAW }; uid_t user = getuid(); + int res; if (setresuid(0, 0, 0)) { fprintf(stderr, "Cannot switch to root: %s.\n", strerror(errno)); return 1; } - set_caps(cap_struct, root_caps, 2); + if (res = set_caps(cap_struct, root_caps, sizeof(root_caps))) + return res; + if (setresuid(user, user, user)) { fprintf(stderr, "Cannot drop root privileges: %s.\n", strerror(errno)); - return 5; + return 2; } - set_caps(cap_struct, user_caps, 1); - if (execve(argv[1], argv + 1, envp)) - perror("Cannot exec"); + if (res = set_caps(cap_struct, user_caps, sizeof(user_caps))) + return res; + + if (execve(argv[1], argv + 1, envp)) { + fprintf(stderr, "Failed to execute %s: %s\n", argv[1], strerror(errno)); + return 3; + } } From fbfedbdc8f02bc36191d3fbf0f5cb7756331c89d Mon Sep 17 00:00:00 2001 From: smill Date: Sun, 4 Sep 2016 01:30:27 +0000 Subject: [PATCH 03/14] Improved error-handling / supplemented documention. --- docs/transparent.rst | 16 ++++++++++++++++ {mitmproxy/contrib => examples}/mitmproxy_shim.c | 0 mitmproxy/cmdline.py | 2 +- mitmproxy/protocol/base.py | 13 ++++++++----- netlib/exceptions.py | 3 +++ netlib/tcp.py | 8 +++++--- 6 files changed, 33 insertions(+), 9 deletions(-) rename {mitmproxy/contrib => examples}/mitmproxy_shim.c (100%) diff --git a/docs/transparent.rst b/docs/transparent.rst index eb77c76cb..dc41f40f3 100644 --- a/docs/transparent.rst +++ b/docs/transparent.rst @@ -1,5 +1,6 @@ .. _transparent: +==================== Transparent Proxying ==================== @@ -20,5 +21,20 @@ destination of the TCP connection. At the moment, mitmproxy supports transparent proxying on OSX Lion and above, and all current flavors of Linux. +Fully transparent mode +======= +By default mitmproxy will use its own local ip address for its server-side connections. +In case this isn't desired, the --spoof-source-address argument can be used to +use the client's ip address for server-side connections. + +This mode does require root privileges though. There's a wrapper in the examples directory +called 'mitmproxy_shim.c', which will enable you to use this mode with dropped priviliges. +It can be used as follows: + +gcc examples/mitmproxy_shim.c -o mitmproxy_shim -lcap +sudo chown root:root mitmproxy_shim +sudo chmod u+s mitmproxy_shim +./mitmproxy_shim $(which mitmproxy) -T --spoof-source-address + .. _iptables: http://www.netfilter.org/ .. _pf: https://en.wikipedia.org/wiki/PF_\(firewall\) diff --git a/mitmproxy/contrib/mitmproxy_shim.c b/examples/mitmproxy_shim.c similarity index 100% rename from mitmproxy/contrib/mitmproxy_shim.c rename to examples/mitmproxy_shim.c diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py index 2191cd952..09866f5b6 100644 --- a/mitmproxy/cmdline.py +++ b/mitmproxy/cmdline.py @@ -478,7 +478,7 @@ def proxy_options(parser): group.add_argument( "--spoof-source-address", action="store_true", dest="spoof_source_address", - help="Use client's IP for the server-side connection" + help="Use the client's IP for server-side connections" ) def proxy_ssl_options(parser): diff --git a/mitmproxy/protocol/base.py b/mitmproxy/protocol/base.py index eed0b292e..206999ef4 100644 --- a/mitmproxy/protocol/base.py +++ b/mitmproxy/protocol/base.py @@ -117,9 +117,11 @@ class ServerConnectionMixin(object): self.server_conn = None if self.config.options.spoof_source_address: - self.server_conn = models.ServerConnection(server_address, (self.ctx.client_conn.address.host, 0), True) + self.server_conn = models.ServerConnection( + server_address, (self.ctx.client_conn.address.host, 0), True) else: - self.server_conn = models.ServerConnection(server_address, (self.config.options.listen_host, 0)) + self.server_conn = models.ServerConnection( + server_address, (self.config.options.listen_host, 0)) self.__check_self_connect() @@ -162,10 +164,11 @@ class ServerConnectionMixin(object): self.channel.tell("serverdisconnect", self.server_conn) if self.config.options.spoof_source_address: - self.server_conn = models.ServerConnection(address, (self.ctx.client_conn.address.host, 0), True) + self.server_conn = models.ServerConnection( + address, (self.ctx.client_conn.address.host, 0), True) else: - self.server_conn = models.ServerConnection(address, (self.server_conn.source_address.host, 0)) - + self.server_conn = models.ServerConnection( + address, (self.server_conn.source_address.host, 0)) def connect(self): """ diff --git a/netlib/exceptions.py b/netlib/exceptions.py index dec79c22a..795926f11 100644 --- a/netlib/exceptions.py +++ b/netlib/exceptions.py @@ -58,3 +58,6 @@ class InvalidCertificateException(TlsException): class Timeout(TcpException): pass + +class ProtocolException(NetlibException): + pass diff --git a/netlib/tcp.py b/netlib/tcp.py index aaea9459e..374607439 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -731,10 +731,11 @@ class TCPClient(_Connection): try: connection = socket.socket(self.address.family, socket.SOCK_STREAM) if self.spoof_source_address: - if os.geteuid() != 0: - raise RuntimeError("Insufficient privileges to set socket option") - else: + try: connection.setsockopt(socket.SOL_IP, 19, 1) + except socket.error as e: + raise exceptions.ProtocolException( + "Failed to spoof the source address: " + e.strerror) if self.source_address: connection.bind(self.source_address()) connection.connect(self.address()) @@ -874,6 +875,7 @@ class BaseHandler(_Connection): class Counter: + def __init__(self): self._count = 0 self._lock = threading.Lock() From e278ce6455b63eb9da61f0e92d7f25cbdf881d8b Mon Sep 17 00:00:00 2001 From: smill Date: Sun, 4 Sep 2016 01:35:03 +0000 Subject: [PATCH 04/14] Removed a mistakenly inserted newline character. --- netlib/tcp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/netlib/tcp.py b/netlib/tcp.py index 374607439..1fd0164fb 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -875,7 +875,6 @@ class BaseHandler(_Connection): class Counter: - def __init__(self): self._count = 0 self._lock = threading.Lock() From 2ecd89fc51676a98c25a80857584923aae9248a1 Mon Sep 17 00:00:00 2001 From: smill Date: Mon, 5 Sep 2016 10:49:39 +0000 Subject: [PATCH 05/14] Made it possible to modify the server_conn.connection attribute, using the serverconnect stub. --- mitmproxy/models/connections.py | 2 +- netlib/tcp.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mitmproxy/models/connections.py b/mitmproxy/models/connections.py index 2cab6e4af..a98711a15 100644 --- a/mitmproxy/models/connections.py +++ b/mitmproxy/models/connections.py @@ -112,7 +112,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject): Attributes: address: Remote address. Can be both a domain or an IP address. ip_address: Resolved remote IP address. - source_address: Local IP address + source_address: Local IP address or client's source IP address. ssl_established: True if TLS is established, False otherwise cert: The certificate presented by the remote during the TLS handshake sni: Server Name Indication sent by the proxy during the TLS handshake diff --git a/netlib/tcp.py b/netlib/tcp.py index 1fd0164fb..c3b8a4076 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -729,10 +729,15 @@ class TCPClient(_Connection): def connect(self): try: - connection = socket.socket(self.address.family, socket.SOCK_STREAM) + if not self.connection: + connection = socket.socket(self.address.family, socket.SOCK_STREAM) + else: + connection = self.connection + if self.spoof_source_address: try: - connection.setsockopt(socket.SOL_IP, 19, 1) + if not connection.getsockopt(socket.SOL_IP, 19): + connection.setsockopt(socket.SOL_IP, 19, 1) except socket.error as e: raise exceptions.ProtocolException( "Failed to spoof the source address: " + e.strerror) From fad6ee6437f89c0b9e914e509b93e97471af9ed6 Mon Sep 17 00:00:00 2001 From: smill Date: Mon, 5 Sep 2016 14:19:08 +0000 Subject: [PATCH 06/14] Improved the documentation. --- docs/transparent.rst | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/transparent.rst b/docs/transparent.rst index dc41f40f3..7860238ef 100644 --- a/docs/transparent.rst +++ b/docs/transparent.rst @@ -25,16 +25,28 @@ Fully transparent mode ======= By default mitmproxy will use its own local ip address for its server-side connections. In case this isn't desired, the --spoof-source-address argument can be used to -use the client's ip address for server-side connections. +use the client's ip address for server-side connections. The following config is +required for this mode to work: + + CLIENT_NET=192.168.1.0/24 + TABLE_ID=100 + MARK=1 + + echo "$TABLE_ID mitmproxy" >> /etc/iproute2/rt_tables + iptables -t mangle -A PREROUTING -d $CLIENT_NET -j MARK --set-mark $MARK + iptables -t nat -A PREROUTING -p tcp -s $CLIENT_NET --match multiport --dports 80,443 -j REDIRECT --to-port 8080 + + ip rule add fwmark $MARK lookup $TABLE_ID + ip route add local $CLIENT_NET dev lo table $TABLE_ID This mode does require root privileges though. There's a wrapper in the examples directory called 'mitmproxy_shim.c', which will enable you to use this mode with dropped priviliges. It can be used as follows: -gcc examples/mitmproxy_shim.c -o mitmproxy_shim -lcap -sudo chown root:root mitmproxy_shim -sudo chmod u+s mitmproxy_shim -./mitmproxy_shim $(which mitmproxy) -T --spoof-source-address + gcc examples/mitmproxy_shim.c -o mitmproxy_shim -lcap + sudo chown root:root mitmproxy_shim + sudo chmod u+s mitmproxy_shim + ./mitmproxy_shim $(which mitmproxy) -T --spoof-source-address .. _iptables: http://www.netfilter.org/ .. _pf: https://en.wikipedia.org/wiki/PF_\(firewall\) From 9429f1bc7b33846c71d7c06f92506a4fc4c68049 Mon Sep 17 00:00:00 2001 From: smill Date: Wed, 14 Sep 2016 19:10:13 +0000 Subject: [PATCH 07/14] Prevent crash in case of a connection timeout. --- netlib/tcp.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/netlib/tcp.py b/netlib/tcp.py index c3b8a4076..4e988ee3a 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -484,12 +484,14 @@ class _Connection(object): if not isinstance(self.connection, SSL.Connection): if not getattr(self.wfile, "closed", False): try: - self.wfile.flush() - self.wfile.close() + if self.wfile: + self.wfile.flush() + self.wfile.close() except exceptions.TcpDisconnect: pass - self.rfile.close() + if self.rfile: + self.rfile.close() else: try: self.connection.shutdown() From 83a28021ad863d0ced37594df68c5b97381910f1 Mon Sep 17 00:00:00 2001 From: smill Date: Wed, 14 Sep 2016 19:11:57 +0000 Subject: [PATCH 08/14] Fixed travis-ci issues. --- mitmproxy/cmdline.py | 1 + mitmproxy/options.py | 2 +- netlib/exceptions.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py index 09866f5b6..b5b939b8c 100644 --- a/mitmproxy/cmdline.py +++ b/mitmproxy/cmdline.py @@ -481,6 +481,7 @@ def proxy_options(parser): help="Use the client's IP for server-side connections" ) + def proxy_ssl_options(parser): # TODO: Agree to consistently either use "upstream" or "server". group = parser.add_argument_group("SSL") diff --git a/mitmproxy/options.py b/mitmproxy/options.py index 9ef1f108d..0ac44cd8e 100644 --- a/mitmproxy/options.py +++ b/mitmproxy/options.py @@ -69,7 +69,7 @@ class Options(optmanager.OptManager): mode = "regular", # type: str no_upstream_cert = False, # type: bool rawtcp = False, # type: bool - spoof_source_address = False, # type: bool + spoof_source_address = False, # type: bool upstream_server = "", # type: str upstream_auth = "", # type: str ssl_version_client="secure", # type: str diff --git a/netlib/exceptions.py b/netlib/exceptions.py index 795926f11..376514096 100644 --- a/netlib/exceptions.py +++ b/netlib/exceptions.py @@ -59,5 +59,6 @@ class InvalidCertificateException(TlsException): class Timeout(TcpException): pass + class ProtocolException(NetlibException): pass From 7cd845644520cc52c53c2957429afae60ffaba25 Mon Sep 17 00:00:00 2001 From: smill Date: Wed, 14 Sep 2016 19:25:53 +0000 Subject: [PATCH 09/14] Added a description to the shim loader, and renamed it. --- examples/{mitmproxy_shim.c => full_transparency_shim.c} | 8 ++++++++ 1 file changed, 8 insertions(+) rename examples/{mitmproxy_shim.c => full_transparency_shim.c} (87%) diff --git a/examples/mitmproxy_shim.c b/examples/full_transparency_shim.c similarity index 87% rename from examples/mitmproxy_shim.c rename to examples/full_transparency_shim.c index 9688bb415..923eea760 100644 --- a/examples/mitmproxy_shim.c +++ b/examples/full_transparency_shim.c @@ -7,6 +7,14 @@ #include #include +/* This setuid wrapper can be used to run mitmproxy in full transparency mode, as a normal user. + * It will set the required capabilities (CAP_NET_RAW), drop privileges, and will then run argv[1] + * with the same capabilities. + * + * It can be compiled as follows: + * gcc examples/mitmproxy_shim.c -o mitmproxy_shim -lcap +*/ + int set_caps(cap_t cap_struct, cap_value_t *cap_list, size_t bufsize) { int cap_count = bufsize / sizeof(cap_list[0]); From 2d4e4eafe1545e9ac79c04fcfc48f198e85900aa Mon Sep 17 00:00:00 2001 From: smill Date: Thu, 15 Sep 2016 18:54:55 +0000 Subject: [PATCH 10/14] Fixed restructuredText error. --- docs/transparent.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/transparent.rst b/docs/transparent.rst index 7860238ef..71b485952 100644 --- a/docs/transparent.rst +++ b/docs/transparent.rst @@ -22,7 +22,8 @@ At the moment, mitmproxy supports transparent proxying on OSX Lion and above, and all current flavors of Linux. Fully transparent mode -======= +====================== + By default mitmproxy will use its own local ip address for its server-side connections. In case this isn't desired, the --spoof-source-address argument can be used to use the client's ip address for server-side connections. The following config is From 3962a11575ec6118cff8ba10ec81b9679e68faa2 Mon Sep 17 00:00:00 2001 From: "smill@cuckoo.sh" Date: Thu, 22 Sep 2016 08:15:34 +0000 Subject: [PATCH 11/14] Commented on IP_TRANSPARENT and changed an exception type. --- netlib/tcp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netlib/tcp.py b/netlib/tcp.py index 4e988ee3a..47200bed9 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -738,10 +738,11 @@ class TCPClient(_Connection): if self.spoof_source_address: try: + # 19 is `IP_TRANSPARENT`, which is only available on Python 3.3+ on some OSes if not connection.getsockopt(socket.SOL_IP, 19): connection.setsockopt(socket.SOL_IP, 19, 1) except socket.error as e: - raise exceptions.ProtocolException( + raise exceptions.TcpException( "Failed to spoof the source address: " + e.strerror) if self.source_address: connection.bind(self.source_address()) From ab546a7348740e36e7092649606fdb0df95bd2a3 Mon Sep 17 00:00:00 2001 From: "smill@cuckoo.sh" Date: Thu, 22 Sep 2016 08:29:07 +0000 Subject: [PATCH 12/14] Introduced comment regarding socket manipulation through the server_conn stub. --- netlib/tcp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netlib/tcp.py b/netlib/tcp.py index 47200bed9..2c55de85b 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -731,6 +731,7 @@ class TCPClient(_Connection): def connect(self): try: + # Allow the socket to be manipulated by using the server_conn stub. if not self.connection: connection = socket.socket(self.address.family, socket.SOCK_STREAM) else: From ec588f88221edb19137209a66431fdf4517c215c Mon Sep 17 00:00:00 2001 From: "smill@cuckoo.sh" Date: Thu, 22 Sep 2016 08:51:17 +0000 Subject: [PATCH 13/14] Removed obsolete code. --- mitmproxy/protocol/base.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mitmproxy/protocol/base.py b/mitmproxy/protocol/base.py index 206999ef4..b2caeb16a 100644 --- a/mitmproxy/protocol/base.py +++ b/mitmproxy/protocol/base.py @@ -163,12 +163,8 @@ class ServerConnectionMixin(object): self.server_conn.close() self.channel.tell("serverdisconnect", self.server_conn) - if self.config.options.spoof_source_address: - self.server_conn = models.ServerConnection( - address, (self.ctx.client_conn.address.host, 0), True) - else: - self.server_conn = models.ServerConnection( - address, (self.server_conn.source_address.host, 0)) + self.server_conn = models.ServerConnection(address, + (self.server_conn.source_address.host, 0), self.config.options.spoof_source_address) def connect(self): """ From e5b79a6d728584cceb918ffbf73c54ec55e948b5 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 22 Sep 2016 01:58:01 -0700 Subject: [PATCH 14/14] minor cleanup --- mitmproxy/protocol/base.py | 7 +++++-- netlib/exceptions.py | 4 ---- netlib/tcp.py | 17 ++++++----------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/mitmproxy/protocol/base.py b/mitmproxy/protocol/base.py index b2caeb16a..da6e83563 100644 --- a/mitmproxy/protocol/base.py +++ b/mitmproxy/protocol/base.py @@ -163,8 +163,11 @@ class ServerConnectionMixin(object): self.server_conn.close() self.channel.tell("serverdisconnect", self.server_conn) - self.server_conn = models.ServerConnection(address, - (self.server_conn.source_address.host, 0), self.config.options.spoof_source_address) + self.server_conn = models.ServerConnection( + address, + (self.server_conn.source_address.host, 0), + self.config.options.spoof_source_address + ) def connect(self): """ diff --git a/netlib/exceptions.py b/netlib/exceptions.py index 376514096..dec79c22a 100644 --- a/netlib/exceptions.py +++ b/netlib/exceptions.py @@ -58,7 +58,3 @@ class InvalidCertificateException(TlsException): class Timeout(TcpException): pass - - -class ProtocolException(NetlibException): - pass diff --git a/netlib/tcp.py b/netlib/tcp.py index 2c55de85b..eea104252 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -484,14 +484,12 @@ class _Connection(object): if not isinstance(self.connection, SSL.Connection): if not getattr(self.wfile, "closed", False): try: - if self.wfile: - self.wfile.flush() - self.wfile.close() + self.wfile.flush() + self.wfile.close() except exceptions.TcpDisconnect: pass - if self.rfile: - self.rfile.close() + self.rfile.close() else: try: self.connection.shutdown() @@ -731,11 +729,7 @@ class TCPClient(_Connection): def connect(self): try: - # Allow the socket to be manipulated by using the server_conn stub. - if not self.connection: - connection = socket.socket(self.address.family, socket.SOCK_STREAM) - else: - connection = self.connection + connection = socket.socket(self.address.family, socket.SOCK_STREAM) if self.spoof_source_address: try: @@ -744,7 +738,8 @@ class TCPClient(_Connection): connection.setsockopt(socket.SOL_IP, 19, 1) except socket.error as e: raise exceptions.TcpException( - "Failed to spoof the source address: " + e.strerror) + "Failed to spoof the source address: " + e.strerror + ) if self.source_address: connection.bind(self.source_address()) connection.connect(self.address())