mitmproxy
This commit is contained in:
parent
e3f97f5488
commit
cab43f63ce
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
2665
android/proxy_cache_thing/package-lock.json
generated
2665
android/proxy_cache_thing/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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": []
|
||||
}
|
@ -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);
|
||||
})();
|
@ -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/**/*"
|
||||
]
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "tslint:latest"
|
||||
}
|
@ -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
20
certgen/gen_certs.sh
Executable 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
|
@ -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.
@ -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>
|
||||
|
@ -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) {
|
||||
|
679
http_server/code/package-lock.json
generated
679
http_server/code/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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}`;
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main () {
|
||||
printf("%p", realloc(0, 10));
|
||||
}
|
@ -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>
|
@ -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
|
@ -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
6
mitmproxy/docker-entrypoint.sh
Executable 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
81
mitmproxy/stats.py
Normal 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()]
|
29
start.mjs
29
start.mjs
@ -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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user