aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsmill <smill@cuckoo.sh>2016-09-03 12:22:09 +0000
committersmill <smill@cuckoo.sh>2016-09-03 12:22:09 +0000
commita6e0c7e8f0c20276f2f7cb2d9332a806e8493c18 (patch)
treef27a3db89f2697582a5a81cfb19e64248e907643
parentb476966a45a6f77b1a819867dbbc943af9a57ef0 (diff)
downloadmitmproxy-a6e0c7e8f0c20276f2f7cb2d9332a806e8493c18.tar.gz
mitmproxy-a6e0c7e8f0c20276f2f7cb2d9332a806e8493c18.tar.bz2
mitmproxy-a6e0c7e8f0c20276f2f7cb2d9332a806e8493c18.zip
Introduced the capability to spoof the source address
of outgoing sessions + an accompanying shim loader.
-rw-r--r--mitmproxy/cmdline.py7
-rw-r--r--mitmproxy/contrib/mitmproxy_shim.c71
-rw-r--r--mitmproxy/models/connections.py4
-rw-r--r--mitmproxy/options.py2
-rw-r--r--mitmproxy/protocol/base.py16
-rw-r--r--netlib/tcp.py8
6 files changed, 101 insertions, 7 deletions
diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py
index d888b93f..2191cd95 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 00000000..b90be566
--- /dev/null
+++ b/mitmproxy/contrib/mitmproxy_shim.c
@@ -0,0 +1,71 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/capability.h>
+#include <unistd.h>
+#include <errno.h>
+
+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 570e89a9..2cab6e4a 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 75798381..9ef1f108 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 bf0cbbae..eed0b292 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 e5c84165..aaea9459 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())