aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/README4
-rw-r--r--examples/add_header.py2
-rw-r--r--examples/change_upstream_proxy.py12
-rwxr-xr-xexamples/flowbasic7
-rw-r--r--examples/har_extractor.py10
-rw-r--r--examples/iframe_injector.py2
-rw-r--r--examples/modify_form.py4
-rw-r--r--examples/modify_response_body.py2
-rwxr-xr-xexamples/read_dumpfile1
-rw-r--r--examples/redirect_requests.py8
-rwxr-xr-xexamples/stickycookies10
-rw-r--r--examples/stream_modify.py6
-rw-r--r--examples/stub.py16
-rw-r--r--examples/tls_passthrough.py136
-rw-r--r--examples/upsidedownternet.py6
15 files changed, 174 insertions, 52 deletions
diff --git a/examples/README b/examples/README
index adfcd0f2..b4dec8e5 100644
--- a/examples/README
+++ b/examples/README
@@ -1,3 +1,7 @@
+Some inline scripts may require additional dependencies, which can be installed using
+`pip install mitmproxy[examples]`.
+
+
# inline script examples
add_header.py Simple script that just adds a header to every request.
change_upstream_proxy.py Dynamically change the upstream proxy
diff --git a/examples/add_header.py b/examples/add_header.py
index 0c0593d1..cf1b53cc 100644
--- a/examples/add_header.py
+++ b/examples/add_header.py
@@ -1,2 +1,2 @@
def response(context, flow):
- flow.response.headers["newheader"] = ["foo"]
+ flow.response.headers["newheader"] = "foo"
diff --git a/examples/change_upstream_proxy.py b/examples/change_upstream_proxy.py
index 8f58e1f2..9c454897 100644
--- a/examples/change_upstream_proxy.py
+++ b/examples/change_upstream_proxy.py
@@ -4,7 +4,6 @@
# Usage: mitmdump -U http://default-upstream-proxy.local:8080/ -s change_upstream_proxy.py
#
# If you want to change the target server, you should modify flow.request.host and flow.request.port
-# flow.live.set_server should only be used by inline scripts to change the upstream proxy.
def proxy_address(flow):
@@ -22,13 +21,4 @@ def request(context, flow):
return
address = proxy_address(flow)
if flow.live:
- if flow.request.scheme == "http":
- # For a normal HTTP request, we just change the proxy server and we're done!
- if address != flow.live.server_conn.address:
- flow.live.set_server(address, depth=1)
- else:
- # If we have CONNECTed (and thereby established "destination state"), the story is
- # a bit more complex. Now we don't want to change the top level address (which is
- # the connect destination) but the address below that. (Notice the `.via` and depth=2).
- if address != flow.live.server_conn.via.address:
- flow.live.set_server(address, depth=2)
+ flow.live.change_upstream_proxy_server(address) \ No newline at end of file
diff --git a/examples/flowbasic b/examples/flowbasic
index 6663dc46..78b9eff7 100755
--- a/examples/flowbasic
+++ b/examples/flowbasic
@@ -8,9 +8,8 @@
Note that request and response messages are not automatically replied to,
so we need to implement handlers to do this.
"""
-import os
-from libmproxy import flow, proxy
-from libmproxy.proxy.server import ProxyServer
+from libmproxy import flow
+from libmproxy.proxy import ProxyServer, ProxyConfig
class MyMaster(flow.FlowMaster):
@@ -34,7 +33,7 @@ class MyMaster(flow.FlowMaster):
return f
-config = proxy.ProxyConfig(
+config = ProxyConfig(
port=8080,
# use ~/.mitmproxy/mitmproxy-ca.pem as default CA file.
cadir="~/.mitmproxy/"
diff --git a/examples/har_extractor.py b/examples/har_extractor.py
index f06efec3..bc784dc4 100644
--- a/examples/har_extractor.py
+++ b/examples/har_extractor.py
@@ -147,8 +147,8 @@ def response(context, flow):
response_body_size = len(flow.response.content)
response_body_decoded_size = len(flow.response.get_decoded_content())
response_body_compression = response_body_decoded_size - response_body_size
- response_mime_type = flow.response.headers.get_first('Content-Type', '')
- response_redirect_url = flow.response.headers.get_first('Location', '')
+ response_mime_type = flow.response.headers.get('Content-Type', '')
+ response_redirect_url = flow.response.headers.get('Location', '')
entry = HAR.entries(
{
@@ -201,12 +201,12 @@ def response(context, flow):
# Lookup the referer in the page_ref of context.HARLog to point this entries
# pageref attribute to the right pages object, then set it as a new
# reference to build a reference tree.
- elif context.HARLog.get_page_ref(flow.request.headers.get('Referer', (None, ))[0]) is not None:
+ elif context.HARLog.get_page_ref(flow.request.headers.get('Referer')) is not None:
entry['pageref'] = context.HARLog.get_page_ref(
- flow.request.headers['Referer'][0]
+ flow.request.headers['Referer']
)
context.HARLog.set_page_ref(
- flow.request.headers['Referer'][0], entry['pageref']
+ flow.request.headers['Referer'], entry['pageref']
)
context.HARLog.add(entry)
diff --git a/examples/iframe_injector.py b/examples/iframe_injector.py
index b2fa2d26..29de9b63 100644
--- a/examples/iframe_injector.py
+++ b/examples/iframe_injector.py
@@ -1,7 +1,7 @@
# Usage: mitmdump -s "iframe_injector.py url"
# (this script works best with --anticache)
from bs4 import BeautifulSoup
-from libmproxy.protocol.http import decoded
+from libmproxy.models import decoded
def start(context, argv):
diff --git a/examples/modify_form.py b/examples/modify_form.py
index 37ba2fac..3e9d15c0 100644
--- a/examples/modify_form.py
+++ b/examples/modify_form.py
@@ -1,7 +1,5 @@
-
def request(context, flow):
- if "application/x-www-form-urlencoded" in flow.request.headers[
- "content-type"]:
+ if "application/x-www-form-urlencoded" in flow.request.headers.get("content-type", ""):
form = flow.request.get_form_urlencoded()
form["mitmproxy"] = ["rocks"]
flow.request.set_form_urlencoded(form)
diff --git a/examples/modify_response_body.py b/examples/modify_response_body.py
index 68d3d4ab..a35e1525 100644
--- a/examples/modify_response_body.py
+++ b/examples/modify_response_body.py
@@ -1,6 +1,6 @@
# Usage: mitmdump -s "modify_response_body.py mitmproxy bananas"
# (this script works best with --anticache)
-from libmproxy.protocol.http import decoded
+from libmproxy.models import decoded
def start(context, argv):
diff --git a/examples/read_dumpfile b/examples/read_dumpfile
index eb1c93bb..b329c0e1 100755
--- a/examples/read_dumpfile
+++ b/examples/read_dumpfile
@@ -4,7 +4,6 @@
#
from libmproxy import flow
-import json
import pprint
import sys
diff --git a/examples/redirect_requests.py b/examples/redirect_requests.py
index 48512f1b..ca24c42a 100644
--- a/examples/redirect_requests.py
+++ b/examples/redirect_requests.py
@@ -1,10 +1,8 @@
-from libmproxy.protocol.http import HTTPResponse
-from netlib.odict import ODictCaseless
-
"""
This example shows two ways to redirect flows to other destinations.
"""
-
+from libmproxy.models import HTTPResponse
+from netlib.http import Headers
def request(context, flow):
# pretty_host(hostheader=True) takes the Host: header of the request into account,
@@ -15,7 +13,7 @@ def request(context, flow):
if flow.request.pretty_host(hostheader=True).endswith("example.com"):
resp = HTTPResponse(
[1, 1], 200, "OK",
- ODictCaseless([["Content-Type", "text/html"]]),
+ Headers(Content_Type="text/html"),
"helloworld")
flow.reply(resp)
diff --git a/examples/stickycookies b/examples/stickycookies
index 67b31da1..7e84f71c 100755
--- a/examples/stickycookies
+++ b/examples/stickycookies
@@ -23,16 +23,16 @@ class StickyMaster(controller.Master):
def handle_request(self, flow):
hid = (flow.request.host, flow.request.port)
- if flow.request.headers["cookie"]:
- self.stickyhosts[hid] = flow.request.headers["cookie"]
+ if "cookie" in flow.request.headers:
+ self.stickyhosts[hid] = flow.request.headers.get_all("cookie")
elif hid in self.stickyhosts:
- flow.request.headers["cookie"] = self.stickyhosts[hid]
+ flow.request.headers.set_all("cookie", self.stickyhosts[hid])
flow.reply()
def handle_response(self, flow):
hid = (flow.request.host, flow.request.port)
- if flow.response.headers["set-cookie"]:
- self.stickyhosts[hid] = flow.response.headers["set-cookie"]
+ if "set-cookie" in flow.response.headers:
+ self.stickyhosts[hid] = flow.response.headers.get_all("set-cookie")
flow.reply()
diff --git a/examples/stream_modify.py b/examples/stream_modify.py
index e3f1f3cf..aa395c03 100644
--- a/examples/stream_modify.py
+++ b/examples/stream_modify.py
@@ -11,11 +11,9 @@ Be aware that content replacement isn't trivial:
def modify(chunks):
"""
chunks is a generator that can be used to iterate over all chunks.
- Each chunk is a (prefix, content, suffix) tuple.
- For example, in the case of chunked transfer encoding: ("3\r\n","foo","\r\n")
"""
- for prefix, content, suffix in chunks:
- yield prefix, content.replace("foo", "bar"), suffix
+ for chunk in chunks:
+ yield chunk.replace("foo", "bar")
def responseheaders(context, flow):
diff --git a/examples/stub.py b/examples/stub.py
index bd3e7cd0..516b71a5 100644
--- a/examples/stub.py
+++ b/examples/stub.py
@@ -18,19 +18,19 @@ def clientconnect(context, root_layer):
context.log("clientconnect")
-def serverconnect(context, server_connection):
+def request(context, flow):
"""
- Called when the proxy initiates a connection to the target server. Note that a
- connection can correspond to multiple HTTP requests
+ Called when a client request has been received.
"""
- context.log("serverconnect")
+ context.log("request")
-def request(context, flow):
+def serverconnect(context, server_conn):
"""
- Called when a client request has been received.
+ Called when the proxy initiates a connection to the target server. Note that a
+ connection can correspond to multiple HTTP requests
"""
- context.log("request")
+ context.log("serverconnect")
def responseheaders(context, flow):
@@ -58,7 +58,7 @@ def error(context, flow):
context.log("error")
-def serverdisconnect(context, server_connection):
+def serverdisconnect(context, server_conn):
"""
Called when the proxy closes the connection to the target server.
"""
diff --git a/examples/tls_passthrough.py b/examples/tls_passthrough.py
new file mode 100644
index 00000000..7b4dec62
--- /dev/null
+++ b/examples/tls_passthrough.py
@@ -0,0 +1,136 @@
+"""
+This inline script allows conditional TLS Interception based
+on a user-defined strategy.
+
+Example:
+
+ > mitmdump -s tls_passthrough.py
+
+ 1. curl --proxy http://localhost:8080 https://example.com --insecure
+ // works - we'll also see the contents in mitmproxy
+
+ 2. curl --proxy http://localhost:8080 https://example.com --insecure
+ // still works - we'll also see the contents in mitmproxy
+
+ 3. curl --proxy http://localhost:8080 https://example.com
+ // fails with a certificate error, which we will also see in mitmproxy
+
+ 4. curl --proxy http://localhost:8080 https://example.com
+ // works again, but mitmproxy does not intercept and we do *not* see the contents
+
+Authors: Maximilian Hils, Matthew Tuusberg
+"""
+from __future__ import (absolute_import, print_function, division)
+import collections
+import random
+
+from enum import Enum
+
+from libmproxy.exceptions import TlsException
+from libmproxy.protocol import TlsLayer, RawTCPLayer
+
+
+class InterceptionResult(Enum):
+ success = True
+ failure = False
+ skipped = None
+
+
+class _TlsStrategy(object):
+ """
+ Abstract base class for interception strategies.
+ """
+ def __init__(self):
+ # A server_address -> interception results mapping
+ self.history = collections.defaultdict(lambda: collections.deque(maxlen=200))
+
+ def should_intercept(self, server_address):
+ """
+ Returns:
+ True, if we should attempt to intercept the connection.
+ False, if we want to employ pass-through instead.
+ """
+ raise NotImplementedError()
+
+ def record_success(self, server_address):
+ self.history[server_address].append(InterceptionResult.success)
+
+ def record_failure(self, server_address):
+ self.history[server_address].append(InterceptionResult.failure)
+
+ def record_skipped(self, server_address):
+ self.history[server_address].append(InterceptionResult.skipped)
+
+
+class ConservativeStrategy(_TlsStrategy):
+ """
+ Conservative Interception Strategy - only intercept if there haven't been any failed attempts
+ in the history.
+ """
+
+ def should_intercept(self, server_address):
+ if InterceptionResult.failure in self.history[server_address]:
+ return False
+ return True
+
+
+class ProbabilisticStrategy(_TlsStrategy):
+ """
+ Fixed probability that we intercept a given connection.
+ """
+ def __init__(self, p):
+ self.p = p
+ super(ProbabilisticStrategy, self).__init__()
+
+ def should_intercept(self, server_address):
+ return random.uniform(0, 1) < self.p
+
+
+class TlsFeedback(TlsLayer):
+ """
+ Monkey-patch _establish_tls_with_client to get feedback if TLS could be established
+ successfully on the client connection (which may fail due to cert pinning).
+ """
+
+ def _establish_tls_with_client(self):
+ server_address = self.server_conn.address
+ tls_strategy = self.script_context.tls_strategy
+
+ try:
+ super(TlsFeedback, self)._establish_tls_with_client()
+ except TlsException as e:
+ tls_strategy.record_failure(server_address)
+ raise e
+ else:
+ tls_strategy.record_success(server_address)
+
+
+# inline script hooks below.
+
+
+def start(context, argv):
+ if len(argv) == 2:
+ context.tls_strategy = ProbabilisticStrategy(float(argv[1]))
+ else:
+ context.tls_strategy = ConservativeStrategy()
+
+
+def next_layer(context, next_layer):
+ """
+ This hook does the actual magic - if the next layer is planned to be a TLS layer,
+ we check if we want to enter pass-through mode instead.
+ """
+ if isinstance(next_layer, TlsLayer) and next_layer._client_tls:
+ server_address = next_layer.server_conn.address
+
+ if context.tls_strategy.should_intercept(server_address):
+ # We try to intercept.
+ # Monkey-Patch the layer to get feedback from the TLSLayer if interception worked.
+ next_layer.__class__ = TlsFeedback
+ next_layer.script_context = context
+ else:
+ # We don't intercept - reply with a pass-through layer and add a "skipped" entry.
+ context.log("TLS passthrough for %s" % repr(next_layer.server_conn.address), "info")
+ next_layer_replacement = RawTCPLayer(next_layer.ctx, logging=False)
+ next_layer.reply(next_layer_replacement)
+ context.tls_strategy.record_skipped(server_address)
diff --git a/examples/upsidedownternet.py b/examples/upsidedownternet.py
index a6de97e4..f2e73047 100644
--- a/examples/upsidedownternet.py
+++ b/examples/upsidedownternet.py
@@ -1,10 +1,10 @@
import cStringIO
from PIL import Image
-from libmproxy.protocol.http import decoded
+from libmproxy.models import decoded
def response(context, flow):
- if flow.response.headers.get_first("content-type", "").startswith("image"):
+ if flow.response.headers.get("content-type", "").startswith("image"):
with decoded(flow.response): # automatically decode gzipped responses.
try:
s = cStringIO.StringIO(flow.response.content)
@@ -12,6 +12,6 @@ def response(context, flow):
s2 = cStringIO.StringIO()
img.save(s2, "png")
flow.response.content = s2.getvalue()
- flow.response.headers["content-type"] = ["image/png"]
+ flow.response.headers["content-type"] = "image/png"
except: # Unknown image types etc.
pass