mitmproxy

This commit is contained in:
Andrii Dokhniak 2025-08-30 18:27:49 +02:00
parent e3f97f5488
commit cab43f63ce
28 changed files with 610 additions and 3502 deletions

View File

@ -12,11 +12,6 @@ WORKDIR /httptoolkit-server
RUN git checkout 490b1b6f5180ad634b60997778c5f96b2f62bf0b \
&& npm i && npm run build:src
# Set up proxy_cache_thing
ADD proxy_cache_thing /proxy_cache_thing
WORKDIR /proxy_cache_thing
RUN npm i && npm run build
ADD entrypoint.sh /entrypoint.sh
ARG PROXY_PORT

View File

@ -68,44 +68,57 @@ function send_notification(socket, is_ok, context, message) {
});
}
let screenshot_in_flight = false;
let gps_setting_in_progress = false;
//maybe check output of child processes and send errors in some way
io.on("connection", (socket) => {
socket.onAny((ev, ...args) => {
console.log("server got: ", ev, ...args);
});
let screenshots_in_flight = 0;
socket.on("screenshot", async () => {
if (screenshot_in_flight) return;
screenshot_in_flight = true;
if (screenshots_in_flight > 1) return;
screenshots_in_flight++;
let screen;
try {
screen = await fetch(
"http://localhost:9987/v2/uiDevice/screenshot"
);
let body = await screen.bytes();
socket.emit("screenshot_data", body);
} catch (err) {
console.error(
"Failed to get the screenshot from culebra, the emulator probably died",
err
);
screenshot_in_flight = false;
return;
}
let body = await screen.bytes();
socket.emit("screenshot_data", body);
screenshot_in_flight = false;
screenshots_in_flight--;
});
let priv_data_requests_in_flight = 0;
socket.on("private_info_req", async () => {
if (priv_data_requests_in_flight > 3) return;
priv_data_requests_in_flight++;
await send_private_data();
priv_data_requests_in_flight--;
});
let port_requests_in_flight = 0;
socket.on("open_ports_req", async () => {
if (port_requests_in_flight > 3) return;
port_requests_in_flight++;
await send_open_ports();
port_requests_in_flight--;
});
let installed_apps_requests_in_flight = 0;
socket.on("installed_apps_req", async () => {
if (installed_apps_requests_in_flight > 3) return;
installed_apps_requests_in_flight++;
await send_installed_apps();
installed_apps_requests_in_flight--;
});
socket.on("start_frida_app", async (app_id) => {

View File

@ -7,7 +7,7 @@ apt-get install iptables -y
# configuring the pinning / unpinning scripts
perl -i -0777p -e 's|CERT_PEM = .*?;|CERT_PEM = `'"$(cat /certificates/mitmproxy-ca-cert.pem | sed -z 's/\n/\\n/g')"'`;|gsm' /frida-scripts/config.js
perl -i -0777p -e 's|const PROXY_SUPPORTS_SOCKS5 = false|const PROXY_SUPPORTS_SOCKS5 = true|gsm' /frida-scripts/config.js
perl -i -0777p -e 's|const PROXY_SUPPORTS_SOCKS5 = false|const PROXY_SUPPORTS_SOCKS5 = false|gsm' /frida-scripts/config.js
perl -i -0777p -e 's|const BLOCK_HTTP3 = true|const BLOCK_HTTP3 = true|gsm' /frida-scripts/config.js
perl -i -0777p -e 's|const PROXY_PORT = 8000|const PROXY_PORT = 8000|gsm' /frida-scripts/config.js

View File

@ -15,4 +15,8 @@ emulator -avd virtual_dev -writable-system -no-window -no-audio -memory 4096 &
adb wait-for-device
adb emu avd snapshot load configured
# set the date on the emulator for the ssl to work properly
adb shell su root "date $(date +%m%d%H%M%G.%S)"
adb shell "am broadcast -a android.intent.action.TIME_SET"
culebra_loop

View File

@ -5,7 +5,6 @@ cd /frida-scripts
frida -U \
-l ./config.js \
-l ./native-connect-hook.js \
-l ./native-tls-hook.js \
-l ./android/android-proxy-override.js \
-l ./android/android-system-certificate-injection.js \
-l ./android/android-certificate-unpinning.js \
@ -13,3 +12,6 @@ frida -U \
-l ./android/android-disable-root-detection.js \
-l ./android/android-disable-flutter-certificate-pinning.js \
-f $1
#-l ./native-tls-hook.js \
# This is broken with mitmproxy

View File

@ -1,23 +1,10 @@
#!/bin/bash
set -e
# node /proxy_cache_thing/dist/index.js &
# CACHE_PID=$!
# /httptoolkit-server/bin/run start -c /certificates &
# HTTPTOOLKIT_SERVER_PID=$!
bash /conf/docker-entrypoint.sh &
ANDROID_PID=$!
function check_dead() {
# if ! ps -p $CACHE_PID > /dev/null; then
# echo "[ERROR] The proxy cache died, exiting...";
# exit 1;
# fi
# if ! ps -p $HTTPTOOLKIT_SERVER_PID > /dev/null; then
# echo "[ERROR] The httptoolkit_server died, exiting...";
# exit 1;
# fi
if ! ps -p $ANDROID_PID > /dev/null; then
echo "[ERROR] The android emulator died, exiting...";
exit 1;

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
{
"name": "proxy_thing",
"version": "1.0.0",
"description": "",
"license": "ISC",
"author": "",
"type": "commonjs",
"main": "./lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"start": "tsc && node dist/index.js",
"lint": "tslint -c tslint.json src/**/*.ts",
"prepublish": "npm run build"
},
"dependencies": {
"express": "^4.21.2",
"mockttp": "^3.16.0",
"ts-node": "^10.9.2",
"tslint": "^5.20.1",
"ws": "^8.18.1"
},
"files": [
"./bin/*",
"./lib/*"
],
"typings": "./lib/index.d.ts",
"directories": {
"lib": "lib"
},
"devDependencies": {
"typescript": "^5.8.2"
},
"keywords": []
}

View File

@ -1,188 +0,0 @@
import { getLocal } from "mockttp";
import * as WebSock from "ws";
const mockServer = getLocal();
const wait_for_open = (socket: WebSock) => {
if (socket.readyState === socket.OPEN) return;
return new Promise((resolve, reject) => {
const maxNumberOfAttempts = 20;
const intervalTime = 100; //ms
let currentAttempt = 0;
const interval = setInterval(() => {
if (currentAttempt > maxNumberOfAttempts - 1) {
clearInterval(interval);
reject(new Error("Maximum number of attempts exceeded"));
} else if (socket.readyState === socket.OPEN) {
clearInterval(interval);
resolve(undefined);
} else if (socket.readyState == socket.CLOSED) {
console.log(socket.url);
reject(new Error("The server socket closed"));
}
currentAttempt++;
}, intervalTime);
});
};
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
function waitforhost(
url: string,
expected_status = 200,
interval = 1000,
attempts = 10
): Promise<void> {
let count = 1;
return new Promise(async (resolve, reject) => {
while (count < attempts) {
await sleep(interval);
try {
const response = await fetch(url);
if (response.status === expected_status) {
resolve();
break;
} else {
count++;
}
} catch {
count++;
console.log(`Still down, trying ${count} of ${attempts}`);
}
}
reject(new Error(`Server is down: ${count} attempts tried`));
});
}
(async () => {
const proxy_port = process.env.PROXY_PORT;
const server_port = process.env.SERVER_PORT;
const loopback_port = process.env.LOOPBACK_PORT; // internal only
const monitoring_api_port = process.env.MONITORING_API_PORT;
if (!proxy_port || !server_port || !monitoring_api_port || !loopback_port) {
throw Error("Configuration env variables not set");
}
// Expect 403 forbidden
await waitforhost("http://127.0.0.1:" + server_port, 403);
mockServer.start(+proxy_port);
let cached_resp: undefined | string = undefined;
const cors_headers = {
"access-control-allow-origin": "*",
"access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS",
"access-control-allow-headers": "Content-Type, Authorization",
"access-control-max-age": "86400",
"access-control-allow-credentials": "true",
Origin: "http://127.0.0.1:8080", // Trick the server into thinking that it is accessed through localhost
};
await mockServer.forOptions().thenReply(200, "", cors_headers);
await mockServer.forPost("/start").thenPassThrough({
beforeRequest: async (req) => {
if (cached_resp) {
return {
response: { body: cached_resp, headers: cors_headers },
};
}
// forward the request to the real server
return {
url: req.url,
headers: req.headers,
body: await req.body.getDecodedBuffer(),
};
},
beforeResponse: async (resp, _req) => {
// cache the response
cached_resp = (await resp.body.getText()) as string;
// set the cors headers, overwriting the prohibitive ones
return { headers: { ...resp.headers, ...cors_headers } };
},
});
await mockServer.forPost("/stop").thenReply(200);
// forward everything to the real server
await mockServer.forUnmatchedRequest().thenPassThrough({
beforeRequest: async (req) => {
return { url: req.url };
},
beforeResponse: async (resp, _req) => {
return { headers: { ...resp.headers, ...cors_headers } };
},
});
await mockServer
.forAnyWebSocket()
.thenForwardTo("ws://127.0.0.1:" + loopback_port);
let wss = new WebSock.Server({ port: +loopback_port });
// this is append only
let httptoolkit_server_messages: String[] = [];
let api_server = new WebSock.Server({ port: +monitoring_api_port });
api_server.on("connection", (ws) => {
let curr_idx_to_send = 0;
const interval = setInterval(() => {
while (httptoolkit_server_messages[curr_idx_to_send] != undefined) {
if (ws.readyState != ws.OPEN) {
clearInterval(interval);
ws.close();
break;
}
ws.send(httptoolkit_server_messages[curr_idx_to_send]);
curr_idx_to_send++;
}
}, 100);
});
wss.on("connection", (ws, req) => {
let server_session_url = "ws://127.0.0.1:" + server_port + req.url;
let httptoolkit_server_session: WebSock;
console.log("ws connection:", ws.protocol, typeof ws.protocol);
if (ws.protocol) {
httptoolkit_server_session = new WebSock(
server_session_url,
ws.protocol,
{ joinDuplicateHeaders: true, headers: req.headers }
);
} else {
httptoolkit_server_session = new WebSock(server_session_url, {
joinDuplicateHeaders: true,
headers: req.headers,
});
}
httptoolkit_server_session.onerror = (_) => {
ws.close();
httptoolkit_server_session.close();
return;
};
httptoolkit_server_session.onopen = () => {
console.log("ws connected to the server");
};
httptoolkit_server_session.onmessage = (msg) => {
httptoolkit_server_messages.push(msg.data.toString());
ws.send(msg.data);
};
// Listening for messages from the client
ws.on("message", async (message) => {
await wait_for_open(httptoolkit_server_session);
httptoolkit_server_session.send(message.toString());
});
// Handling client disconnection
ws.on("close", () => {
httptoolkit_server_session.close();
});
});
console.log("WS Proxy started");
console.log(mockServer.proxyEnv);
})();

View File

@ -1,38 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"lib": [
"es2015"
],
"rootDir": "src",
"outDir": "dist",
"strict": true,
"alwaysStrict": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"downlevelIteration": true,
"declaration": true,
"pretty": true
},
"include": [
"typings/**/*",
"src/**/*"
]
}

View File

@ -1,3 +0,0 @@
{
"extends": "tslint:latest"
}

View File

@ -1,9 +1,8 @@
FROM node:22.14.0
# docker run --entrypoint sh --rm -it -v ./certificates:/certs alpine/openssl /certs/gen.sh
RUN git clone https://github.com/httptoolkit/httptoolkit-server
FROM alpine/openssl
WORKDIR /httptoolkit-server
RUN git checkout 490b1b6f5180ad634b60997778c5f96b2f62bf0b \
&& npm i && npm run build:src
COPY gen_certs.sh /gen_certs.sh
CMD /httptoolkit-server/bin/run start -c /certificates
ENTRYPOINT sh /gen_certs.sh
# CMD sh

20
certgen/gen_certs.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/sh
set -xe
cd /certificates
rm -rf *
cat > myconfig.cnf << EOF
[mysection]
keyUsage = critical, digitalSignature, nonRepudiation, keyCertSign, cRLSign
basicConstraints = critical, CA:TRUE
EOF
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -sha256 -days 365 -not_before $(date --date="@$(( $(date +%s) - 86400 * 7 ))" +%y%m%d%H%M%SZ) -nodes -subj "/C=XX/O=Sealcode/OU=CompanySectionName/CN=Do not trust, rentgendroid certificate" -config myconfig.cnf -extensions mysection
cat cert.pem key.pem > mitmproxy-ca.pem
mv cert.pem mitmproxy-ca-cert.pem
rm myconfig.cnf

View File

@ -18,6 +18,7 @@ services:
- /dev/kvm
networks:
- rent_gen_android
ports:
- 45456:45459 # This cannot change
- 45457:45459 # This cannot change
@ -25,6 +26,7 @@ services:
- 3000:3000 # android server port
- 3001:3001 # Notifications server
- 5556:5556 # emulator grpc port
volumes:
- $PWD/shared_buffer:/shared_buffer
- $PWD/android/conf:/conf
@ -38,18 +40,6 @@ services:
volumes:
- $PWD/certificates:/root/.mitmproxy
container_name: mitmproxy
httptoolkit_ui:
build:
context: ./httptoolkit_ui/
args:
# The ip / hostname using which,
# the browser can reach this docker session
DOCKER_HOST: "127.0.0.1"
container_name: httptoolkit_ui
networks:
- rent_gen_android
ports:
- 9080:9080
http_server:
build: ./http_server/
container_name: http_server

Binary file not shown.

View File

@ -49,12 +49,6 @@
<div class="tab-section">
<div class="tab">
<button
class="tablinks active"
onclick="main.open_tab(event, 'httptoolkit-tab')"
>
HttpToolkit UI
</button>
<button class="tablinks" onclick="main.open_tab(event, 'logs-tab')">
Logs
</button>
@ -89,14 +83,6 @@
</div>
</div>
<div class="tabcontent active" id="httptoolkit-tab">
<!-- <iframe -->
<!-- id="httptoolkit-frame" -->
<!-- style="flex-grow: 1" -->
<!-- src="http://localhost:9080/" -->
<!-- title="httptoolkit" -->
<!-- ></iframe> -->
</div>
<div class="tabcontent" id="controls-tab">
<form id="set_coords" onsubmit="main.coords_handler(event)">
<label>

View File

@ -63,9 +63,11 @@ app.get("/", async function (req, res) {
//POST
app.use(express.text({limit: "100mb"}));
app.post("/inspect_har", upload.none(), function (req, res) {
app.post("/inspect_har", upload.none(), async function (req, res) {
let body = req.body;
let har = body.har;
let har_req = await fetch("http://mitmproxy:9111/get_har")
let har = await har_req.text();
let private_data;
if (body.private_data)
private_data = JSON.parse(body.private_data);
@ -74,6 +76,20 @@ app.post("/inspect_har", upload.none(), function (req, res) {
res.send(build_html(har, private_data));
});
app.get("/download_har", upload.none(), async function (req, res) {
let har_req = await fetch("http://mitmproxy:9111/get_har")
let har = await har_req.text();
res.status(200).send(har);
});
app.get("/get_intercept_stats", upload.none(), async function (_req, res) {
let req = await fetch("http://mitmproxy:9111/get_stats")
let stats = await req.text();
res.status(200).send(stats);
});
app.use(fileUpload());
app.post("/upload_apk", async function (req, res) {
if (!req.files || Object.keys(req.files).length === 0) {

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@ import {
open_ports,
app_id_select,
launch_app_form,
sleep,
} from "./shared";
import { start_notifications } from "./notifications";
@ -265,10 +266,6 @@ window.addEventListener("keydown", (event) => {
}
});
export async function sleep(time: number) {
return new Promise((resolve) => setTimeout(resolve, time));
}
async function screenshot_loop() {
var before;
@ -294,4 +291,4 @@ installed_apps_loop();
open_ports_loop();
screenshot_loop();
start_notifications();
// start_traffic_log();
start_traffic_log();

View File

@ -18,3 +18,8 @@ export const app_id_select = document.getElementById("app_id_select")!;
export const launch_app_form = document.getElementById("launch_app_form")!;
export const socket = io();
export async function sleep(time: number) {
return new Promise((resolve) => setTimeout(resolve, time));
}

View File

@ -1,41 +1,38 @@
import { Entry, Har, PostData, Request, Response } from "har-format";
import {
adid_priv_info_table,
lat_priv_info_table,
lon_priv_info_table,
sleep,
} from "./shared";
// Request + responce pairs: 0
const req_res_pairs = document.getElementById("traffic-log-req-res-pairs")!;
// Waiting for the responce: 0
const waiting = document.getElementById("traffic-log-waiting")!;
export async function start_traffic_log() {
const traffic_log_lines = document.getElementById("traffic-log-lines")!;
var before;
const inspect_har_form: HTMLFormElement = document.getElementById(
"inspect-har-form"
)! as HTMLFormElement;
while (true) {
before = performance.now();
let connections = "UNKNOWN";
try {
let req = await fetch("/get_intercept_stats");
const connection = new WebSocket("ws://localhost:10001");
let req_text = await req.text();
let json = JSON.parse(req_text);
connections = json.connections.toString();
} catch(_) {}
req_res_pairs.innerText = `Request + responce pairs: ${connections}`;
const unfinished_entries: Map<string, Entry> = new Map();
const finished_entries: Entry[] = [];
export function start_traffic_log() {
update_stats();
connection.onmessage = (msg) => {
process_msg(msg.data);
update_stats();
};
connection.onclose = connection.onerror = () => {
window.location.reload();
};
while (performance.now() - before < 1000) await sleep(500);
}
}
export function download_har() {
export async function download_har() {
var tempLink = document.createElement("a");
var taBlob = new Blob([JSON.stringify(export_har())], {
let res = await fetch("/download_har");
let res_text = await res.text()
var taBlob = new Blob([res_text], {
type: "text/plain",
});
@ -69,8 +66,6 @@ function launch_window_with_post(url: string, data: any) {
export async function inspect_har() {
let data = {
har: JSON.stringify(export_har()),
private_data: JSON.stringify([
{desc: "adid", data: adid_priv_info_table.textContent},
{desc: "latitude", data: lat_priv_info_table.textContent },
@ -81,144 +76,5 @@ export async function inspect_har() {
}
function update_stats() {
req_res_pairs.innerText = `Request + responce pairs: ${finished_entries.length}`;
waiting.innerText = `Waiting for the responce: ${unfinished_entries.size}`;
}
function process_msg(s: string) {
let obj = JSON.parse(s);
console.log(obj);
if (obj.type !== "data") return;
if (obj.payload && obj.payload.data && obj.payload.data.requestReceived)
process_req(obj.payload.data.requestReceived);
if (obj.payload && obj.payload.data && obj.payload.data.responseCompleted)
process_res(obj.payload.data.responseCompleted);
}
function process_res(res: any) {
let entry = unfinished_entries.get(res.id)!;
let content_type = "application/text";
let headers = JSON.parse(res.rawHeaders).map((header: [string, string]) => {
if (header[0].toLowerCase() === "content-type")
content_type = header[1];
return { name: header[0], value: header[1], comment: "" };
});
//'{"startTime":1751745139334,
// "startTimestamp":347666.762487,
// "bodyReceivedTimestamp":347667.529477,
// "headersSentTimestamp":347906.038202,
// "responseSentTimestamp":347906.616067}'
let timing_events = JSON.parse(res.timingEvents);
let start_ts = timing_events.startTimestamp;
let got_headers_ts = timing_events.headersSentTimestamp;
let end_ts = timing_events.responseSentTimestamp;
let wait_time = got_headers_ts - start_ts;
let recieve_time = end_ts - got_headers_ts;
let response: Response = {
status: res.statusCode,
statusText: res.statusMessage,
httpVersion: entry.request.httpVersion,
cookies: [],
headers,
content: {
size: 0,
mimeType: content_type,
text: res.body,
encoding: "base64",
},
redirectURL: "",
headersSize: -1,
bodySize: -1,
};
entry.response = response;
entry.timings.wait = wait_time;
entry.timings.receive = recieve_time;
unfinished_entries.delete(res.id);
finished_entries.push(entry);
let el = document.createElement("span");
el.innerHTML = `
${entry.request.url}
<br />`;
traffic_log_lines.appendChild(el);
}
function process_req(req: any) {
let content_type = "application/text";
let headers = JSON.parse(req.rawHeaders).map((header: [string, string]) => {
if (header[0].toLowerCase() === "Content-Type")
content_type = header[1];
return { name: header[0], value: header[1], comment: "" };
});
let timing_events = JSON.parse(req.timingEvents);
let start_time: number = timing_events.startTime!;
let start_datetime = new Date(start_time).toISOString();
let request: Request = {
method: req.method,
url: req.url,
httpVersion: req.httpVersion,
cookies: [],
headers,
queryString: [],
postData: req.body
? ({ text: req.body, mimeType: content_type } as PostData)
: undefined,
headersSize: -1,
bodySize: -1,
comment: "",
};
//'{"startTime":1751745139334,"startTimestamp":347666.762487,"bodyReceivedTimestamp":347667.529477,"headersSentTimestamp":347906.038202,"responseSentTimestamp":347906.616067}'
let entry: Entry = {
startedDateTime: start_datetime,
time: 0,
request: request,
response: {
status: 0,
statusText: "",
httpVersion: "",
cookies: [],
headers: [],
content: {
size: 0,
mimeType: "",
},
redirectURL: "",
headersSize: 0,
bodySize: 0,
},
cache: {},
timings: {
wait: 0,
receive: 0,
},
};
unfinished_entries.set(req.id, entry);
}
function export_har(): Har {
let ret: Har = {
log: {
version: "1.2",
creator: {
name: "Rentgendroid",
version: "0.0.1",
},
entries: [...finished_entries, ...unfinished_entries.values()],
},
};
return ret;
// waiting.innerText = `Waiting for the responce: ${unfinished_entries.size}`;
}

View File

@ -1,6 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
int main () {
printf("%p", realloc(0, 10));
}

View File

@ -1,15 +0,0 @@
<script type="text/javascript">
function randomlinks(){
var myrandom=Math.round(Math.random()*3)
var links=new Array()
links[0]="https://www.DemiCardGame.com"
links[1]="https://www.games.xyphienllc.com"
links[2]="https://www.htcg.news"
links[3]="https://www.eTableCon.com"
window.open(links[myrandom], '_blank');
}
</script>
<form>
<input type="button" value="random link!" onClick="randomlinks()">
</form>

View File

@ -1,23 +0,0 @@
FROM node:20.18.1
# Set up node
RUN npm install -g n && n install 22.14.0 && n use 22.14.0 && hash -r
# If iproute2 is not installed,
# node can't figure out what address to bind 0.0.0.0 to
RUN apt-get update && apt-get install iproute2 python3 --yes \
&& git clone https://github.com/httptoolkit/httptoolkit-ui
WORKDIR httptoolkit-ui
RUN git checkout ac44f8e6e1f5f41be988a32eb89c98f57723d005 \
&& npm i && npm run server:setup
ARG DOCKER_HOST=localhost
# This gets sent to the browser as a server URL,
# so it is important that it is set to the domain / ip of the server.
RUN sed -i "s/127.0.0.1/${DOCKER_HOST}/" src/model/proxy-store.ts \
&& npm run build:default # Can take a long time
CMD npm exec static-server ./dist -o

View File

@ -3,6 +3,10 @@ FROM mitmproxy/mitmproxy
USER root
WORKDIR /root
# CMD bash -c 'echo hello from mitmproxy && mitmdump -w /root/.mitmproxy/dump --set mode="socks5@0.0.0.0:1080"'
cmd sleep 1000000000
COPY docker-entrypoint.sh /docker-entrypoint.sh
COPY stats.py /stats.py
# CMD bash -c 'mitmdump -w ~/.mitmproxy/dump --set mode="socks5@0.0.0.0:1080"'
ENTRYPOINT /docker-entrypoint.sh
# cmd sleep 1000000000
#--set mode="regular@0.0.0.0:8000"

6
mitmproxy/docker-entrypoint.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
set -xe
cd /root
script -c 'mitmdump -s /stats.py --set mode=regular@0.0.0.0:1080 -v'

81
mitmproxy/stats.py Normal file
View File

@ -0,0 +1,81 @@
from typing import Optional
import mitmproxy.addons.savehar
from mitmproxy import http
from mitmproxy.types import Path
from mitmproxy.addonmanager import Loader
import threading
import time
import json
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, HTTPServer
class SaveHar2:
def __init__(self) -> None:
self.save_har = mitmproxy.addons.savehar.SaveHar()
self.counter = 0
server_address = ('', 9111)
parr = self
class HTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/get_har':
print(parr.save_har.flows);
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "application/json")
self.end_headers()
har = parr.get_har_json()
self.wfile.write(har)
if self.path == '/get_stats':
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "application/json")
self.end_headers()
har = parr.get_har_json()
self.wfile.write(("{\"connections\": " + str(len(parr.save_har.flows)) +" }\n").encode("utf8"))
self.httpd = HTTPServer(server_address, HTTPRequestHandler)
print("Server started ...")
self.httpd_thread = threading.Thread(target=self.httpd.serve_forever)
self.httpd_thread.start()
def save_har_to_file(self):
self.save_har.export_har(self.save_har.flows, Path("output.har"))
def get_har_json(self) -> bytes:
return json.dumps(self.save_har.make_har(self.save_har.flows), indent=4).encode()
def configure(self, updated):
self.save_har.configure(updated);
def _save_flow(self, flow: http.HTTPFlow) -> None:
self.save_har.flows.append(flow)
def response(self, flow: http.HTTPFlow) -> None:
# websocket flows will receive a websocket_end,
# we don't want to persist them here already
if flow.websocket is None:
self._save_flow(flow)
if flow.websocket is None:
self._save_flow(flow)
self.counter += 1
if (self.counter == 5):
print("Outputing har")
print(self.save_har.flows)
self.save_har_to_file()
self.counter = 0
def error(self, flow: http.HTTPFlow) -> None:
self.response(flow)
def websocket_end(self, flow: http.HTTPFlow) -> None:
self._save_flow(flow)
addons = [SaveHar2()]

View File

@ -5,10 +5,14 @@ async function sleep(time) {
}
async function checkCertExistance() {
try {
return await Promise.all([
fs.access("./certificates/ca.key"),
fs.access("./certificates/ca.pem", fs.constants.R_OK),
fs.access("./certificates/mitmproxy-ca-cert.pem"),
fs.access("./certificates/mitmproxy-ca.pem", fs.constants.R_OK),
]);
} catch (_) {
return false;
}
}
async function generateCert() {
@ -22,23 +26,12 @@ async function generateCert() {
);
}
await $`docker build --tag certgen $PWD/certgen`
// iniciate docker which will create certs
$`docker run --rm -v $PWD/certificates:/certificates --name certGenerator certgen &`;
await $`docker build --tag certgen $PWD/certgen`;
// generate certs
await $`docker run --rm -v $PWD/certificates:/certificates --name certGenerator certgen`;
//wait for certs to generate
let generated = false;
while (!generated) {
try {
await checkCertExistance();
await $`sleep 1`;
generated = true;
} catch {}
}
//kill docker container
await $`docker stop certGenerator`;
await $`docker rmi certgen`
// clean up
await $`docker rmi certgen`;
}
async function generatePreAndroid() {