mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 08:11:00 +00:00
Docs.
This commit is contained in:
parent
e3fd0e838d
commit
ca9c60d2eb
@ -67,49 +67,17 @@ flow of requests and responses are completely opaque to the proxy.
|
|||||||
|
|
||||||
## The MITM in mitmproxy
|
## The MITM in mitmproxy
|
||||||
|
|
||||||
This is where mitmproxy's fundamental trick comes into play. The MITM in its
|
This is where mitmproxy's fundamental trick comes into play. The MITM in its name stands for Man-In-The-Middle - a reference to the process we use to intercept and interfere with these theoretially opaque data streams. The basic idea is to pretend to be the server to the client, and pretend to be the client to the server, while we sit in the middle decoding traffic from both sides. The tricky part is that the [Certificate Authority](http://en.wikipedia.org/wiki/Certificate_authority) system is designed to prevent exactly this attack, by allowing a trusted third-party to cryptographically sign a server's SSL certificates to verify that they are legit. If this signature doesn't match or is from a non-trusted party, a secure client will simply drop the connection and refuse to proceed. Despite the many shortcomings of the CA system as it exists today, this is usually fatal to attempts to MITM an SSL connection for analysis. Our answer to this conundrum is to become a trusted Certificate Authority ourselves. Mitmproxy includes a full CA implementation that generates interception certificates on the fly. To get the client to trust these certificates, we [register mitmproxy as a trusted CA with the device manually](@!urlTo("ssl.html")!@).
|
||||||
name stands for Man-In-The-Middle - a reference to the process we use to
|
|
||||||
intercept and interfere with these theoretially opaque data streams. The basic
|
|
||||||
idea is to pretend to be the server to the client, and pretend to be the client
|
|
||||||
to the server, while we sit in the middle decoding traffic from both sides. The
|
|
||||||
tricky part is that the [Certificate
|
|
||||||
Authority](http://en.wikipedia.org/wiki/Certificate_authority) system is
|
|
||||||
designed to prevent exactly this attack, by allowing a trusted third-party to
|
|
||||||
cryptographically sign a server's SSL certificates to verify that they are
|
|
||||||
legit. If this signature is from a non-trusted party, a secure client will
|
|
||||||
simply drop the connection and refuse to proceed. Despite the many shortcomings
|
|
||||||
of the CA system as it exists today, this is usually fatal to attempts to MITM
|
|
||||||
an SSL connection for analysis.
|
|
||||||
|
|
||||||
Our answer to this conundrum is to become a trusted Certificate Authority
|
|
||||||
ourselves. Mitmproxy includes a full CA implementation that generates
|
|
||||||
interception certificates on the fly. To get the client to trust these
|
|
||||||
certificates, we [register mitmproxy as a trusted CA with the device
|
|
||||||
manually](@!urlTo("ssl.html")!@).
|
|
||||||
|
|
||||||
## Complication 1: What's the remote hostname?
|
## Complication 1: What's the remote hostname?
|
||||||
|
|
||||||
To proceed with this plan, we need to know the domain name to use in the
|
To proceed with this plan, we need to know the domain name to use in the interception certificate - the client will verify that the certificate is for the domain it's connecting to, and abort if this is not the case. At first blush, it seems that the CONNECT request above gives us all we need - in this example, both of these values are "example.com". But what if the client had initiated the connection as follows:
|
||||||
interception certificate - the client will verify that the certificate is for
|
|
||||||
the domain it's connecting to, and abort if this is not the case. At first
|
|
||||||
blush, it seems that the CONNECT request above gives us all we need - in this
|
|
||||||
example, both of these values are "example.com". But what if the client had
|
|
||||||
initiated the connection as follows:
|
|
||||||
|
|
||||||
<pre>CONNECT 10.1.1.1:443 HTTP/1.1</pre>
|
<pre>CONNECT 10.1.1.1:443 HTTP/1.1</pre>
|
||||||
|
|
||||||
Using the IP address is perfectly legitimate because it gives us enough
|
Using the IP address is perfectly legitimate because it gives us enough information to initiate the pipe, even though it doesn't reveal the remote hostname.
|
||||||
information to initiate the pipe, even though it doesn't reveal the remote
|
|
||||||
hostname.
|
|
||||||
|
|
||||||
Mitmproxy has a cunning mechanism that smooths this over - [upstream
|
Mitmproxy has a cunning mechanism that smooths this over - [upstream certificate sniffing](@!urlTo("features/upstreamcerts.html")!@). As soon as we see the CONNECT request, we pause the client part of the conversation, and initiate a simultaneous connection to the server. We complete the SSL handshake with the server, and inspect the certificates it used. Now, we use the Common Name in the upstream SSL certificates to generate the dummy certificate for the client. Voila, we have the correct hostname to present to the client, even if it was never specified.
|
||||||
certificate sniffing](@!urlTo("features/upstreamcerts.html")!@). As soon as we
|
|
||||||
see the CONNECT request, we pause the client part of the conversation, and
|
|
||||||
initiate a simultaneous connection to the server. We complete the SSL handshake
|
|
||||||
with the server, and inspect the certificates it used. Now, we use the Common
|
|
||||||
Name in the upstream SSL certificates to generate the dummy certificate for the
|
|
||||||
client. Voila, we have the correct hostname to present to the client, even if
|
|
||||||
it was never specified.
|
|
||||||
|
|
||||||
|
|
||||||
## Complication 2: Subject Alternative Name
|
## Complication 2: Subject Alternative Name
|
||||||
@ -127,34 +95,11 @@ them to the generated dummy certificate.
|
|||||||
|
|
||||||
## Complication 3: Server Name Indication
|
## Complication 3: Server Name Indication
|
||||||
|
|
||||||
One of the big limitations of conventional SSL is that each certificate
|
One of the big limitations of vanilla SSL is that each certificate requires its own IP address. This means that you couldn't do virtual hosting where multiple domains with independent certificates share the same IP address. In a world with a rapidly shrinking IPv4 address pool this is a problem, and we have a solution in the form of the [Server Name Indication](http://en.wikipedia.org/wiki/Server_Name_Indication) extension to the SSL and TLS protocols. This lets the client specify the remote server name at the start of the SSL handshake, which then lets the server select the right certificate to complete the process.
|
||||||
requires its own IP address. This means that you couldn't do virtual hosting
|
|
||||||
where multiple domains with independent certificates share the same IP address.
|
|
||||||
In a world with a rapidly shrinking IPv4 address pool this is a problem, and we
|
|
||||||
have a solution in the form of the [Server Name
|
|
||||||
Indication](http://en.wikipedia.org/wiki/Server_Name_Indication) extension to
|
|
||||||
the SSL and TLS protocols. This lets the client specify the remote server name
|
|
||||||
at the start of the SSL handshake, which then lets the server select the right
|
|
||||||
certificate to complete the process.
|
|
||||||
|
|
||||||
SNI breaks our upstream certificate sniffing process, because when we connect
|
SNI breaks our upstream certificate sniffing process, because when we connect without using SNI, we get served a default certificate that may have nothing to do with the certificate expected by the client. The solution is another tricky complication to the client connection process. After the client connects, we allow the SSL handshake to continue until just _after_ the SNI value has been passed to us. Now we can pause the conversation, and initiate an upstream connection using the correct SNI value, which then serves us the correct upstream certificate, from which we can extract the expected CN and SANs.
|
||||||
without using SNI, we get served a default certificate that may have nothing to
|
|
||||||
do with the certificate expected by the client. The solution is another tricky
|
|
||||||
complication to the client connection process. After the client connects, we
|
|
||||||
allow the SSL handshake to continue until just _after_ the SNI value has been
|
|
||||||
passed to us. Now we can pause the conversation, and initiate an upstream
|
|
||||||
connection using the correct SNI value, which then serves us the correct
|
|
||||||
upstream certificate, from which we can extract the expected CN and SANs.
|
|
||||||
|
|
||||||
There's another wrinkle here. Due to a limitation of the SSL library mitmproxy
|
|
||||||
uses, we can't detect that a connection _hasn't_ sent an SNI request until it's
|
|
||||||
too late for upstream certificate sniffing. In practice, we therefore make a
|
|
||||||
vanilla SSL connection upstream to sniff non-SNI certificates, and then discard
|
|
||||||
the connection if the client sends an SNI notification. If you're watching your
|
|
||||||
traffic with a packet sniffer, you'll see two connections to the server when an
|
|
||||||
SNI request is made, the first of which is immediately closed after the SSL
|
|
||||||
handshake. Luckily, this is almost never an issue in practice.
|
|
||||||
|
|
||||||
|
There's another wrinkle here. Due to a limitation of the SSL library mitmproxy uses, we can't detect that a connection _hasn't_ sent an SNI request until it's too late for upstream certificate sniffing. In practice, we therefore make a vanilla SSL connection upstream to sniff non-SNI certificates, and then discard the connection if the client sends an SNI notification. If you're watching your traffic with a packet sniffer, you'll see two connections to the server when an SNI request is made, the first of which is immediately closed after the SSL handshake. Luckily, this is almost never an issue in practice.
|
||||||
|
|
||||||
## Putting it all together
|
## Putting it all together
|
||||||
|
|
||||||
@ -234,7 +179,7 @@ redirection mechanism that transparently reroutes a TCP connection destined for
|
|||||||
a server on the Internet to a listening proxy server. This usually takes the
|
a server on the Internet to a listening proxy server. This usually takes the
|
||||||
form of a firewall on the same host as the proxy server -
|
form of a firewall on the same host as the proxy server -
|
||||||
[iptables](http://www.netfilter.org/) on Linux or
|
[iptables](http://www.netfilter.org/) on Linux or
|
||||||
[pf](http://en.wikipedia.org/wiki/PF_(firewall)) on OSX. Once the client has
|
[pf](http://en.wikipedia.org/wiki/PF_\(firewall\)) on OSX. Once the client has
|
||||||
initiated the connection, it makes a vanilla HTTP request, which might look
|
initiated the connection, it makes a vanilla HTTP request, which might look
|
||||||
something like this:
|
something like this:
|
||||||
|
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
When a transparent proxy is used, traffic is redirected into a proxy at the network layer, without
|
||||||
|
any client configuration being required. This makes transparent proxying ideal for those situations
|
||||||
|
where you can't change client behaviour - proxy-oblivious Android applications being a common
|
||||||
|
example.
|
||||||
|
|
||||||
|
To set up transparent proxying, we need two new components. The first is a
|
||||||
|
redirection mechanism that transparently reroutes a TCP connection destined for
|
||||||
|
a server on the Internet to a listening proxy server. This usually takes the
|
||||||
|
form of a firewall on the same host as the proxy server -
|
||||||
|
[iptables](http://www.netfilter.org/) on Linux or
|
||||||
|
[pf](http://en.wikipedia.org/wiki/PF_\(firewall\)) on OSX. When the proxy receives a redirected connection, it sees a vanilla HTTP request, without a host specification. This is where the second new component comes in - a host module that allows us to query the redirector for the original destination of the TCP connection.
|
||||||
|
|
||||||
|
At the moment, mitmproxy supports transparent proxying on OSX Lion and above, and all current flavors of Linux.kkkkk
|
@ -40,9 +40,6 @@ rdr on en2 inet proto tcp to any port 443 -> 127.0.0.1 port 8080
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li> Configure your test device to use the host on which mitmproxy is
|
|
||||||
running as the default gateway.</li>
|
|
||||||
|
|
||||||
<li> Configure sudoers to allow mitmproxy to access pfctl. Edit the file
|
<li> Configure sudoers to allow mitmproxy to access pfctl. Edit the file
|
||||||
<b>/etc/sudoers</b> on your system as root. Add the following line to the end
|
<b>/etc/sudoers</b> on your system as root. Add the following line to the end
|
||||||
of the file:
|
of the file:
|
||||||
@ -55,7 +52,7 @@ rdr on en2 inet proto tcp to any port 443 -> 127.0.0.1 port 8080
|
|||||||
you're special feel free to tighten the restriction up to the user running
|
you're special feel free to tighten the restriction up to the user running
|
||||||
mitmproxy.</li>
|
mitmproxy.</li>
|
||||||
|
|
||||||
<li> Finally, fire up mitmproxy. You probably want a command like this:
|
<li> Fire up mitmproxy. You probably want a command like this:
|
||||||
|
|
||||||
<pre class="terminal">mitmproxy -T --host</pre>
|
<pre class="terminal">mitmproxy -T --host</pre>
|
||||||
|
|
||||||
@ -65,4 +62,8 @@ rdr on en2 inet proto tcp to any port 443 -> 127.0.0.1 port 8080
|
|||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li> Finally, configure your test device to use the host on which mitmproxy is
|
||||||
|
running as the default gateway.</li>
|
||||||
|
|
||||||
|
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -5,4 +5,3 @@ mapp = flask.Flask(__name__)
|
|||||||
@mapp.route("/")
|
@mapp.route("/")
|
||||||
def hello():
|
def hello():
|
||||||
return "mitmproxy"
|
return "mitmproxy"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user