Skip to content

Commit 9f7958a

Browse files
committed
initial commit
0 parents  commit 9f7958a

File tree

11 files changed

+412
-0
lines changed

11 files changed

+412
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__
2+
config/

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
## mitmproxy-tools
2+
3+
List of generic mitmproxy scripts I use while working on various researches, pentests and bug bounties.
4+
5+
Previously
6+
7+
* I wrote a blog post about my [mitmproxy + openvpn](https://blog.flatt.tech/entry/mitmproxy) setup at my former workpace.
8+
* then I wrote another [mitmproxy + openvpn](https://gist.github.com/stypr/abe9ef83556759847c063ae9389fa0ae) setup to show the current setup
9+
10+
What has been changed this time
11+
12+
* Directory structures have been changed for convenient addons
13+
- `views/*` can be used to auto decrypt some of request/response data for better visuals
14+
- `addons/*` can be used to work like plugins to do actions upon send/receive.
15+
16+
* Replacing openvpn setups to wireguard setups
17+
- When upstream SOCK5 proxy only supports TCP, UDP packets have to be passed through somehow.
18+
1. Unfortunately, transparent proxy will not pass UDP packets, while the wireguard mode does support DNS and UDP packet mitm.
19+
- Setups are much simpler than typical openvpn setup.
20+
- Reference: https://mitmproxy.org/posts/wireguard-mode/
21+
- There are still some limitations like lack of handling for HTTP2 and HTTP3, but we can still use the old HTTPS.
22+
23+
Feel free to contribute if you have any interesting addons/views to share.
24+
25+
### Installations
26+
27+
#### Summary
28+
29+
Most of them are same as [the gist version](https://gist.github.com/stypr/abe9ef83556759847c063ae9389fa0ae), except that you don't have to install OpenVPN anymore.
30+
31+
1. Install `wireguard` on your system (`apt install -y wireguard`)
32+
33+
2. `bind9` is not needed anymore. Also, [mitmproxy now has its own way to handle DNS manipulations now](https://github.com/Kriechi/mitmproxy/blob/dns-addon/docs/src/content/overview-features.md#dns-manipulation).
34+
35+
2. Install [Caddy](https://caddyserver.com/docs/install)
36+
37+
3. Add passwords on [caddy/Caddyfile](caddy/Caddyfile) using `caddy hash-password`, move files to `/etc/caddy`
38+
39+
4. Install mitmproxy to latest
40+
```sh
41+
apt install -y python3-pyasn1 python3-flask python3-dev python3-urwid python3-pip libxml2-dev libxslt-dev libffi-dev
42+
pip3 install -U mitmproxy --break-system-packages
43+
mitmproxy --version
44+
```
45+
46+
5. The script proxies through upstream [WARP](https://one.one.one.one/) by default.
47+
You might want to install or make appropriate changes to the script.
48+
49+
6. Once everything is done, `screen ./run.sh`
50+
51+
#### Installing WARP on Linux
52+
53+
1. Install WARP CLI.
54+
55+
```sh
56+
curl https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
57+
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ bookworm main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
58+
sudo apt update && sudo apt install -y cloudflare-warp
59+
```
60+
61+
2. Register WARP, set proxy with appropriate ports, start proxy.
62+
63+
```sh
64+
warp-cli register
65+
warp-cli proxy port 40000
66+
warp-cli mode proxy
67+
warp-cli connect
68+
```

addons/debug.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import os
2+
import logging
3+
import tempfile
4+
import requests
5+
from mitmproxy import http
6+
7+
class Debug:
8+
"""
9+
Main debug script
10+
"""
11+
12+
def request(self, flow: http.HTTPFlow) -> None:
13+
"""
14+
Main Request Handler
15+
"""
16+
17+
match flow.request.path:
18+
case "/__ok__":
19+
flow.response = http.Response.make(200, "ok", {"Content-Type": "text/html"})
20+
case _:
21+
pass
22+
23+
def response(self, flow: http.HTTPFlow) -> None:
24+
"""
25+
Main Response Handler
26+
"""
27+
28+
match flow.request.path:
29+
case "/iam/api/login":
30+
flow.response.content = flow.response.content.replace(b"true", b"false")
31+
case _:
32+
pass

addons/no_cache.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import os
2+
import logging
3+
import tempfile
4+
import requests
5+
from mitmproxy import http
6+
7+
class NoCache:
8+
"""
9+
Disable caching completely.
10+
"""
11+
12+
def response(self, flow: http.HTTPFlow) -> None:
13+
"""
14+
Disable caching completely.
15+
"""
16+
17+
# Never ever cache responses.
18+
flow.response.headers["cache-control"] = "no-cache, no-store, must-revalidate"
19+
flow.response.headers["pragma"] = "no-cache"
20+
flow.response.headers["expires"] = "0"

addons/upstream_proxy.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import random
2+
from mitmproxy import http
3+
from mitmproxy.net.server_spec import ServerSpec
4+
from mitmproxy.connection import Server
5+
6+
class UpstreamProxy:
7+
"""
8+
Empty proxy_servers if you don't plan to use proxy at all
9+
"""
10+
proxy_servers = [
11+
("127.0.0.1", 40000)
12+
]
13+
14+
def get_proxy_address(self) -> tuple[str, int]:
15+
"""
16+
Load balancing based on random.choice
17+
"""
18+
return random.choice(self.proxy_servers) if self.proxy_servers else None
19+
20+
21+
def request(self, flow: http.HTTPFlow) -> None:
22+
"""
23+
Main Request Handler
24+
"""
25+
26+
# Forward to Upstream Proxy
27+
proxy_address = self.get_proxy_address()
28+
if proxy_address:
29+
server_connection_already_open = flow.server_conn.timestamp_start is not None
30+
if server_connection_already_open and proxy_address:
31+
flow.server_conn = Server(address=flow.server_conn.address)
32+
flow.server_conn.via = ServerSpec(("http", proxy_address))
33+

addons/web_console.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import os
2+
import logging
3+
import tempfile
4+
import requests
5+
from mitmproxy import http
6+
7+
class WebConsole:
8+
"""
9+
Adds web console for convenient mobile devtools
10+
You may use vConsole as an alternative.
11+
"""
12+
console = ""
13+
console_url = "https://cdn.jsdelivr.net/npm/eruda"
14+
15+
def load(self, loader):
16+
console_path = os.path.join(tempfile.gettempdir(), "console.js")
17+
18+
try:
19+
self.console = open(console_path).read()
20+
except:
21+
logging.warning("Dowloading Eruda to temporary directory...")
22+
self.console = requests.get(self.console_url, timeout=10).text
23+
open(console_path, "w").write(self.console)
24+
25+
def request(self, flow: http.HTTPFlow) -> None:
26+
"""
27+
Main Request Handler
28+
"""
29+
match flow.request.path:
30+
case "/__script__/__console__.js":
31+
flow.response = http.Response.make(
32+
200,
33+
self.console,
34+
{"Content-Type": "text/javascript"}
35+
)
36+
case _:
37+
pass
38+
39+
def response(self, flow: http.HTTPFlow) -> None:
40+
"""
41+
Append console script for all text/html responses
42+
"""
43+
44+
# Inject Eruda if the response is HTML
45+
is_html = False
46+
for key, value in flow.response.headers.items():
47+
# text/html; charset=utf-8
48+
if key.lower() == "content-type" and value.startswith("text/html"):
49+
is_html = True
50+
break
51+
52+
if is_html:
53+
console_loader = b"""
54+
<script async>
55+
(() => {
56+
var script3336974aaa = document.createElement('script');
57+
script3336974aaa.src="/__script__/__console__.js";
58+
document.head.append(script3336974aaa);
59+
script3336974aaa.onload = () => { eruda.init(); };
60+
})();
61+
</script></head>
62+
"""
63+
flow.response.content = flow.response.content.replace(b"</head>", console_loader)

caddy/Caddyfile

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
example.tld {
2+
# tls internal
3+
4+
log {
5+
format json {
6+
time_format rfc3339
7+
}
8+
output file /var/log/caddy/default.log {
9+
roll_size 1gb
10+
roll_keep 2
11+
roll_keep_for 2160h
12+
}
13+
}
14+
15+
header /* {
16+
-Server
17+
-X-frame-options
18+
-X-powered-by
19+
?Access-Control-Allow-Methods "POST, GET, OPTIONS"
20+
?Access-Control-Allow-Origin "*"
21+
Cache-Control: no-cache, no-store, must-revalidate
22+
defer
23+
}
24+
25+
handle_errors {
26+
header /* {
27+
-Server
28+
-X-frame-options
29+
-X-powered-by
30+
?Access-Control-Allow-Methods "POST, GET, OPTIONS"
31+
?Access-Control-Allow-Origin "*"
32+
Cache-Control: no-cache, no-store, must-revalidate
33+
defer
34+
}
35+
}
36+
37+
handle_path /* {
38+
# use `caddy hash-password` to generate credentials
39+
basicauth {
40+
admin $2a$......
41+
}
42+
reverse_proxy * http://localhost:8000 {
43+
header_up Host localhost:8000
44+
header_up Origin http://localhost:8000
45+
}
46+
}
47+
}

config/.keep

Whitespace-only changes.

main.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/python3 -u
2+
3+
"""
4+
Main script for debugging
5+
"""
6+
7+
import logging
8+
import requests
9+
10+
from mitmproxy import contentviews
11+
from views.sekai import ViewSekai
12+
from addons.upstream_proxy import UpstreamProxy
13+
from addons.web_console import WebConsole
14+
from addons.no_cache import NoCache
15+
from addons.debug import Debug
16+
17+
####
18+
19+
addons = [
20+
UpstreamProxy(),
21+
WebConsole(),
22+
NoCache(),
23+
Debug()
24+
]
25+
26+
####
27+
28+
views = [
29+
ViewProjectSekai(),
30+
]
31+
32+
####
33+
34+
def load(loader):
35+
for view in views:
36+
contentviews.add(view)
37+
38+
def done():
39+
for view in views:
40+
contentviews.remove(view)

run.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/sh
2+
3+
warp-cli disconnect
4+
warp-cli connect
5+
mitmweb \
6+
--ignore-hosts dns.google.com \
7+
--web-port 8000 \
8+
--web-host localhost \
9+
--ssl-insecure \
10+
--set stream_large_bodies=1024000 \
11+
--set connection_strategy=lazy \
12+
--set http2=false \
13+
--set http3=false \
14+
--no-web-open-browser \
15+
--set confdir=$PWD/config \
16+
--mode wireguard:$PWD/config/wireguard_server1.conf@303 \
17+
--script main.py
18+
19+
# For mulitple clients, add more configs.
20+
# Client configurations are available in the web frontend
21+
# --mode wireguard:$PWD/config/wireguard_server2.conf@304

0 commit comments

Comments
 (0)