remove context from all scripts

This commit is contained in:
Maximilian Hils 2016-07-07 18:37:33 -07:00
parent 7a5b21556b
commit c048ae1d5b
33 changed files with 137 additions and 112 deletions

View File

@ -1,2 +1,2 @@
def response(context, flow): def response(flow):
flow.response.headers["newheader"] = "foo" flow.response.headers["newheader"] = "foo"

View File

@ -14,7 +14,7 @@ def proxy_address(flow):
return ("localhost", 8081) return ("localhost", 8081)
def request(context, flow): def request(flow):
if flow.request.method == "CONNECT": if flow.request.method == "CONNECT":
# If the decision is done by domain, one could also modify the server address here. # If the decision is done by domain, one could also modify the server address here.
# We do it after CONNECT here to have the request data available as well. # We do it after CONNECT here to have the request data available as well.

View File

@ -62,9 +62,9 @@ class ViewPigLatin(contentviews.View):
pig_view = ViewPigLatin() pig_view = ViewPigLatin()
def start(context): def start():
context.add_contentview(pig_view) contentviews.add(pig_view)
def done(context): def done():
context.remove_contentview(pig_view) contentviews.remove(pig_view)

View File

@ -28,7 +28,7 @@ import re
parse_host_header = re.compile(r"^(?P<host>[^:]+|\[.+\])(?::(?P<port>\d+))?$") parse_host_header = re.compile(r"^(?P<host>[^:]+|\[.+\])(?::(?P<port>\d+))?$")
def request(context, flow): def request(flow):
if flow.client_conn.ssl_established: if flow.client_conn.ssl_established:
flow.request.scheme = "https" flow.request.scheme = "https"
sni = flow.client_conn.connection.get_servername() sni = flow.client_conn.connection.get_servername()

View File

@ -1,4 +1,7 @@
def request(context, flow): from mitmproxy import master
f = context.duplicate_flow(flow)
def request(flow):
f = master.duplicate_flow(flow)
f.request.path = "/changed" f.request.path = "/changed"
context.replay_request(f) master.replay_request(f, block=True, run_scripthooks=False)

View File

@ -1,3 +1,3 @@
def response(context, flow): def response(flow):
flow.response.status_code = 500 flow.response.status_code = 500
flow.response.content = b"" flow.response.content = b""

View File

@ -3,14 +3,16 @@
import sys import sys
from mitmproxy import filt from mitmproxy import filt
state = {}
def start(context):
def start():
if len(sys.argv) != 2: if len(sys.argv) != 2:
raise ValueError("Usage: -s 'filt.py FILTER'") raise ValueError("Usage: -s 'filt.py FILTER'")
context.filter = filt.parse(sys.argv[1]) state["filter"] = filt.parse(sys.argv[1])
def response(context, flow): def response(flow):
if flow.match(context.filter): if flow.match(state["filter"]):
print("Flow matches filter:") print("Flow matches filter:")
print(flow) print(flow)

View File

@ -3,8 +3,10 @@ import sys
from mitmproxy.flow import FlowWriter from mitmproxy.flow import FlowWriter
state = {}
def start(context):
def start():
if len(sys.argv) != 2: if len(sys.argv) != 2:
raise ValueError('Usage: -s "flowriter.py filename"') raise ValueError('Usage: -s "flowriter.py filename"')
@ -12,9 +14,9 @@ def start(context):
f = sys.stdout f = sys.stdout
else: else:
f = open(sys.argv[1], "wb") f = open(sys.argv[1], "wb")
context.flow_writer = FlowWriter(f) state["flow_writer"] = FlowWriter(f)
def response(context, flow): def response(flow):
if random.choice([True, False]): if random.choice([True, False]):
context.flow_writer.add(flow) state["flow_writer"].add(flow)

View File

@ -54,7 +54,13 @@ class _HARLog(HAR.log):
return self.__page_list__ return self.__page_list__
def start(context): class Context(object):
pass
context = Context()
def start():
""" """
On start we create a HARLog instance. You will have to adapt this to On start we create a HARLog instance. You will have to adapt this to
suit your actual needs of HAR generation. As it will probably be suit your actual needs of HAR generation. As it will probably be
@ -79,7 +85,7 @@ def start(context):
context.seen_server = set() context.seen_server = set()
def response(context, flow): def response(flow):
""" """
Called when a server response has been received. At the time of this Called when a server response has been received. At the time of this
message both a request and a response are present and completely done. message both a request and a response are present and completely done.
@ -201,7 +207,7 @@ def response(context, flow):
context.HARLog.add(entry) context.HARLog.add(entry)
def done(context): def done():
""" """
Called once on script shutdown, after any other events. Called once on script shutdown, after any other events.
""" """

View File

@ -4,25 +4,27 @@ import sys
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from mitmproxy.models import decoded from mitmproxy.models import decoded
iframe_url = None
def start(context):
def start():
if len(sys.argv) != 2: if len(sys.argv) != 2:
raise ValueError('Usage: -s "iframe_injector.py url"') raise ValueError('Usage: -s "iframe_injector.py url"')
context.iframe_url = sys.argv[1] global iframe_url
iframe_url = sys.argv[1]
def response(context, flow): def response(flow):
if flow.request.host in context.iframe_url: if flow.request.host in iframe_url:
return return
with decoded(flow.response): # Remove content encoding (gzip, ...) with decoded(flow.response): # Remove content encoding (gzip, ...)
html = BeautifulSoup(flow.response.content, "lxml") html = BeautifulSoup(flow.response.content, "lxml")
if html.body: if html.body:
iframe = html.new_tag( iframe = html.new_tag(
"iframe", "iframe",
src=context.iframe_url, src=iframe_url,
frameborder=0, frameborder=0,
height=0, height=0,
width=0) width=0)
html.body.insert(0, iframe) html.body.insert(0, iframe)
flow.response.content = str(html).encode("utf8") flow.response.content = str(html).encode("utf8")
context.log("Iframe inserted.")

View File

@ -1,4 +1,4 @@
def request(context, flow): def request(flow):
if flow.request.urlencoded_form: if flow.request.urlencoded_form:
flow.request.urlencoded_form["mitmproxy"] = "rocks" flow.request.urlencoded_form["mitmproxy"] = "rocks"
else: else:

View File

@ -1,2 +1,2 @@
def request(context, flow): def request(flow):
flow.request.query["mitmproxy"] = "rocks" flow.request.query["mitmproxy"] = "rocks"

View File

@ -5,16 +5,20 @@ import sys
from mitmproxy.models import decoded from mitmproxy.models import decoded
def start(context): state = {}
def start():
if len(sys.argv) != 3: if len(sys.argv) != 3:
raise ValueError('Usage: -s "modify_response_body.py old new"') raise ValueError('Usage: -s "modify_response_body.py old new"')
# You may want to use Python's argparse for more sophisticated argument # You may want to use Python's argparse for more sophisticated argument
# parsing. # parsing.
context.old, context.new = sys.argv[1].encode(), sys.argv[2].encode() state["old"], state["new"] = sys.argv[1].encode(), sys.argv[2].encode()
def response(context, flow): def response(flow):
with decoded(flow.response): # automatically decode gzipped responses. with decoded(flow.response): # automatically decode gzipped responses.
flow.response.content = flow.response.content.replace( flow.response.content = flow.response.content.replace(
context.old, state["old"],
context.new) state["new"]
)

View File

@ -1,9 +1,10 @@
import time import time
import mitmproxy
from mitmproxy.script import concurrent from mitmproxy.script import concurrent
@concurrent # Remove this and see what happens @concurrent # Remove this and see what happens
def request(context, flow): def request(flow):
context.log("handle request: %s%s" % (flow.request.host, flow.request.path)) mitmproxy.log("handle request: %s%s" % (flow.request.host, flow.request.path))
time.sleep(5) time.sleep(5)
context.log("start request: %s%s" % (flow.request.host, flow.request.path)) mitmproxy.log("start request: %s%s" % (flow.request.host, flow.request.path))

View File

@ -4,6 +4,7 @@ instance, we're using the Flask framework (http://flask.pocoo.org/) to expose
a single simplest-possible page. a single simplest-possible page.
""" """
from flask import Flask from flask import Flask
import mitmproxy
app = Flask("proxapp") app = Flask("proxapp")
@ -15,10 +16,10 @@ def hello_world():
# Register the app using the magic domain "proxapp" on port 80. Requests to # Register the app using the magic domain "proxapp" on port 80. Requests to
# this domain and port combination will now be routed to the WSGI app instance. # this domain and port combination will now be routed to the WSGI app instance.
def start(context): def start():
context.app_registry.add(app, "proxapp", 80) mitmproxy.master.apps.add(app, "proxapp", 80)
# SSL works too, but the magic domain needs to be resolvable from the mitmproxy machine due to mitmproxy's design. # SSL works too, but the magic domain needs to be resolvable from the mitmproxy machine due to mitmproxy's design.
# mitmproxy will connect to said domain and use serve its certificate (unless --no-upstream-cert is set) # mitmproxy will connect to said domain and use serve its certificate (unless --no-upstream-cert is set)
# but won't send any data. # but won't send any data.
context.app_registry.add(app, "example.com", 443) mitmproxy.master.apps.add(app, "example.com", 443)

View File

@ -5,7 +5,7 @@ from mitmproxy.models import HTTPResponse
from netlib.http import Headers from netlib.http import Headers
def request(context, flow): def request(flow):
# pretty_host takes the "Host" header of the request into account, # pretty_host takes the "Host" header of the request into account,
# which is useful in transparent mode where we usually only have the IP # which is useful in transparent mode where we usually only have the IP
# otherwise. # otherwise.

View File

@ -2,23 +2,21 @@ from netlib.http import decoded
import re import re
from six.moves import urllib from six.moves import urllib
# set of SSL/TLS capable hosts
def start(context): secure_hosts = set()
# set of SSL/TLS capable hosts
context.secure_hosts = set()
def request(context, flow): def request(flow):
flow.request.headers.pop('If-Modified-Since', None) flow.request.headers.pop('If-Modified-Since', None)
flow.request.headers.pop('Cache-Control', None) flow.request.headers.pop('Cache-Control', None)
# proxy connections to SSL-enabled hosts # proxy connections to SSL-enabled hosts
if flow.request.pretty_host in context.secure_hosts: if flow.request.pretty_host in secure_hosts:
flow.request.scheme = 'https' flow.request.scheme = 'https'
flow.request.port = 443 flow.request.port = 443
def response(context, flow): def response(flow):
with decoded(flow.response): with decoded(flow.response):
flow.request.headers.pop('Strict-Transport-Security', None) flow.request.headers.pop('Strict-Transport-Security', None)
flow.request.headers.pop('Public-Key-Pins', None) flow.request.headers.pop('Public-Key-Pins', None)
@ -31,7 +29,7 @@ def response(context, flow):
location = flow.response.headers['Location'] location = flow.response.headers['Location']
hostname = urllib.parse.urlparse(location).hostname hostname = urllib.parse.urlparse(location).hostname
if hostname: if hostname:
context.secure_hosts.add(hostname) secure_hosts.add(hostname)
flow.response.headers['Location'] = location.replace('https://', 'http://', 1) flow.response.headers['Location'] = location.replace('https://', 'http://', 1)
# strip secure flag from 'Set-Cookie' headers # strip secure flag from 'Set-Cookie' headers

View File

@ -1,4 +1,4 @@
def responseheaders(context, flow): def responseheaders(flow):
""" """
Enables streaming for all responses. Enables streaming for all responses.
""" """

View File

@ -16,5 +16,5 @@ def modify(chunks):
yield chunk.replace("foo", "bar") yield chunk.replace("foo", "bar")
def responseheaders(context, flow): def responseheaders(flow):
flow.response.stream = modify flow.response.stream = modify

View File

@ -1,79 +1,80 @@
import mitmproxy
""" """
This is a script stub, with definitions for all events. This is a script stub, with definitions for all events.
""" """
def start(context): def start():
""" """
Called once on script startup, before any other events. Called once on script startup, before any other events.
""" """
context.log("start") mitmproxy.log("start")
def clientconnect(context, root_layer): def clientconnect(root_layer):
""" """
Called when a client initiates a connection to the proxy. Note that a Called when a client initiates a connection to the proxy. Note that a
connection can correspond to multiple HTTP requests connection can correspond to multiple HTTP requests
""" """
context.log("clientconnect") mitmproxy.log("clientconnect")
def request(context, flow): def request(flow):
""" """
Called when a client request has been received. Called when a client request has been received.
""" """
context.log("request") mitmproxy.log("request")
def serverconnect(context, server_conn): def serverconnect(server_conn):
""" """
Called when the proxy initiates a connection to the target server. Note that a Called when the proxy initiates a connection to the target server. Note that a
connection can correspond to multiple HTTP requests connection can correspond to multiple HTTP requests
""" """
context.log("serverconnect") mitmproxy.log("serverconnect")
def responseheaders(context, flow): def responseheaders(flow):
""" """
Called when the response headers for a server response have been received, Called when the response headers for a server response have been received,
but the response body has not been processed yet. Can be used to tell mitmproxy but the response body has not been processed yet. Can be used to tell mitmproxy
to stream the response. to stream the response.
""" """
context.log("responseheaders") mitmproxy.log("responseheaders")
def response(context, flow): def response(flow):
""" """
Called when a server response has been received. Called when a server response has been received.
""" """
context.log("response") mitmproxy.log("response")
def error(context, flow): def error(flow):
""" """
Called when a flow error has occured, e.g. invalid server responses, or Called when a flow error has occured, e.g. invalid server responses, or
interrupted connections. This is distinct from a valid server HTTP error interrupted connections. This is distinct from a valid server HTTP error
response, which is simply a response with an HTTP error code. response, which is simply a response with an HTTP error code.
""" """
context.log("error") mitmproxy.log("error")
def serverdisconnect(context, server_conn): def serverdisconnect(server_conn):
""" """
Called when the proxy closes the connection to the target server. Called when the proxy closes the connection to the target server.
""" """
context.log("serverdisconnect") mitmproxy.log("serverdisconnect")
def clientdisconnect(context, root_layer): def clientdisconnect(root_layer):
""" """
Called when a client disconnects from the proxy. Called when a client disconnects from the proxy.
""" """
context.log("clientdisconnect") mitmproxy.log("clientdisconnect")
def done(context): def done():
""" """
Called once on script shutdown, after any other events. Called once on script shutdown, after any other events.
""" """
context.log("done") mitmproxy.log("done")

View File

@ -11,7 +11,7 @@ mitmdump -T --host --tcp ".*" -q -s examples/tcp_message.py
from netlib import strutils from netlib import strutils
def tcp_message(ctx, tcp_msg): def tcp_message(tcp_msg):
modified_msg = tcp_msg.message.replace("foo", "bar") modified_msg = tcp_msg.message.replace("foo", "bar")
is_modified = False if modified_msg == tcp_msg.message else True is_modified = False if modified_msg == tcp_msg.message else True

View File

@ -20,13 +20,14 @@ Example:
Authors: Maximilian Hils, Matthew Tuusberg Authors: Maximilian Hils, Matthew Tuusberg
""" """
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import collections import collections
import random import random
import sys import sys
from enum import Enum from enum import Enum
import mitmproxy
from mitmproxy.exceptions import TlsProtocolException from mitmproxy.exceptions import TlsProtocolException
from mitmproxy.protocol import TlsLayer, RawTCPLayer from mitmproxy.protocol import TlsLayer, RawTCPLayer
@ -97,7 +98,6 @@ class TlsFeedback(TlsLayer):
def _establish_tls_with_client(self): def _establish_tls_with_client(self):
server_address = self.server_conn.address server_address = self.server_conn.address
tls_strategy = self.script_context.tls_strategy
try: try:
super(TlsFeedback, self)._establish_tls_with_client() super(TlsFeedback, self)._establish_tls_with_client()
@ -110,15 +110,18 @@ class TlsFeedback(TlsLayer):
# inline script hooks below. # inline script hooks below.
tls_strategy = None
def start(context):
def start():
global tls_strategy
if len(sys.argv) == 2: if len(sys.argv) == 2:
context.tls_strategy = ProbabilisticStrategy(float(sys.argv[1])) tls_strategy = ProbabilisticStrategy(float(sys.argv[1]))
else: else:
context.tls_strategy = ConservativeStrategy() tls_strategy = ConservativeStrategy()
def next_layer(context, next_layer): def next_layer(next_layer):
""" """
This hook does the actual magic - if the next layer is planned to be a TLS 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. we check if we want to enter pass-through mode instead.
@ -126,14 +129,13 @@ def next_layer(context, next_layer):
if isinstance(next_layer, TlsLayer) and next_layer._client_tls: if isinstance(next_layer, TlsLayer) and next_layer._client_tls:
server_address = next_layer.server_conn.address server_address = next_layer.server_conn.address
if context.tls_strategy.should_intercept(server_address): if tls_strategy.should_intercept(server_address):
# We try to intercept. # We try to intercept.
# Monkey-Patch the layer to get feedback from the TLSLayer if interception worked. # Monkey-Patch the layer to get feedback from the TLSLayer if interception worked.
next_layer.__class__ = TlsFeedback next_layer.__class__ = TlsFeedback
next_layer.script_context = context
else: else:
# We don't intercept - reply with a pass-through layer and add a "skipped" entry. # 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") mitmproxy.log("TLS passthrough for %s" % repr(next_layer.server_conn.address), "info")
next_layer_replacement = RawTCPLayer(next_layer.ctx, logging=False) next_layer_replacement = RawTCPLayer(next_layer.ctx, ignore=True)
next_layer.reply.send(next_layer_replacement) next_layer.reply.send(next_layer_replacement)
context.tls_strategy.record_skipped(server_address) tls_strategy.record_skipped(server_address)

View File

@ -3,7 +3,7 @@ from PIL import Image
from mitmproxy.models import decoded from mitmproxy.models import decoded
def response(context, flow): def response(flow):
if flow.response.headers.get("content-type", "").startswith("image"): if flow.response.headers.get("content-type", "").startswith("image"):
with decoded(flow.response): # automatically decode gzipped responses. with decoded(flow.response): # automatically decode gzipped responses.
try: try:

View File

@ -5,12 +5,12 @@ from a_helper import parser
var = 0 var = 0
def start(ctx): def start():
global var global var
var = parser.parse_args(sys.argv[1:]).var var = parser.parse_args(sys.argv[1:]).var
def here(ctx): def here():
global var global var
var += 1 var += 1
return var return var

View File

@ -1,36 +1,37 @@
import mitmproxy
log = [] log = []
def clientconnect(ctx, cc): def clientconnect(cc):
ctx.log("XCLIENTCONNECT") mitmproxy.log("XCLIENTCONNECT")
log.append("clientconnect") log.append("clientconnect")
def serverconnect(ctx, cc): def serverconnect(cc):
ctx.log("XSERVERCONNECT") mitmproxy.log("XSERVERCONNECT")
log.append("serverconnect") log.append("serverconnect")
def request(ctx, f): def request(f):
ctx.log("XREQUEST") mitmproxy.log("XREQUEST")
log.append("request") log.append("request")
def response(ctx, f): def response(f):
ctx.log("XRESPONSE") mitmproxy.log("XRESPONSE")
log.append("response") log.append("response")
def responseheaders(ctx, f): def responseheaders(f):
ctx.log("XRESPONSEHEADERS") mitmproxy.log("XRESPONSEHEADERS")
log.append("responseheaders") log.append("responseheaders")
def clientdisconnect(ctx, cc): def clientdisconnect(cc):
ctx.log("XCLIENTDISCONNECT") mitmproxy.log("XCLIENTDISCONNECT")
log.append("clientdisconnect") log.append("clientdisconnect")
def error(ctx, cc): def error(cc):
ctx.log("XERROR") mitmproxy.log("XERROR")
log.append("error") log.append("error")

View File

@ -3,5 +3,5 @@ from mitmproxy.script import concurrent
@concurrent @concurrent
def request(context, flow): def request(flow):
time.sleep(0.1) time.sleep(0.1)

View File

@ -2,5 +2,5 @@ from mitmproxy.script import concurrent
@concurrent @concurrent
def start(context): def start():
pass pass

View File

@ -1,4 +1,6 @@
import mitmproxy
def request(ctx, f):
f = ctx.duplicate_flow(f) def request(f):
ctx.replay_request(f) f = mitmproxy.master.duplicate_flow(f)
mitmproxy.master.replay_request(f, block=True, run_scripthooks=False)

View File

@ -1,2 +1,2 @@
def request(ctx, r): def request(r):
raise ValueError raise ValueError()

View File

@ -1,3 +1,3 @@
def start(ctx): def start():
raise ValueError() raise ValueError()

View File

@ -3,5 +3,5 @@ def modify(chunks):
yield chunk.replace(b"foo", b"bar") yield chunk.replace(b"foo", b"bar")
def responseheaders(context, flow): def responseheaders(flow):
flow.response.stream = modify flow.response.stream = modify

View File

@ -1,4 +1,4 @@
def tcp_message(ctx, flow): def tcp_message(flow):
message = flow.messages[-1] message = flow.messages[-1]
if not message.from_client: if not message.from_client:
message.content = message.content.replace(b"foo", b"bar") message.content = message.content.replace(b"foo", b"bar")

View File

@ -1,2 +1,2 @@
def done(ctx): def done():
raise RuntimeError() raise RuntimeError()