Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F21589927
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
15 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/android/code/index.ts b/android/code/index.ts
index 4226475..9304684 100644
--- a/android/code/index.ts
+++ b/android/code/index.ts
@@ -1,477 +1,481 @@
import child_process from "child_process";
import { Server } from "socket.io";
import { exit } from "process";
import fs from "node:fs/promises";
import { summerize_har } from "har-analyzer/analyzer";
import { AnalysisResponse, AnalysisResult } from "./types";
async function spawnPromise(
program: string,
args: string[]
): Promise<{ output: string; code: number }> {
return new Promise((resolve, _reject) => {
let output = "";
const process = child_process.spawn(program, args);
process.stdout.on("data", (data) => {
output += data;
});
process.stderr.on("data", (data) => {
output += data;
});
process.on("close", (code) => {
resolve({ output, code });
});
process.on("error", (err) => {
console.log("Process has errored", err);
resolve({ output, code: 1 });
});
});
}
let io = new Server();
async function send_private_data() {
let adid = (await spawnPromise("bash", ["/conf/get_adid.sh"])).output;
let gps_coords = (await spawnPromise("bash", ["/conf/get_location.sh"]))
.output;
let gps_coords_split = gps_coords.trim().split(",");
io.emit("private_info", {
adid,
latitude: gps_coords_split[0],
longitude: gps_coords_split[1],
});
}
async function send_ports_scan() {
function state_to_confidence(state) {
if (state === "open") return 3;
if (state === "filtered") return 2;
if (state === "closed") return 1;
console.error("unexpected nmap port state:", state);
return 0;
}
// [{"proto": "tcp", "port": "2221", "state": "open", "name": "ftp"}, ...]
let out = (await spawnPromise("bash", ["/conf/nmap_scan.sh"])).output;
let ports_info = JSON.parse(out);
let ports = new Map();
for (let row of ports_info) {
let prev = ports.get(row.port);
if (
!prev ||
state_to_confidence(row.state) > state_to_confidence(prev.state)
) {
prev = row;
ports.set(row.port, prev);
}
}
let ret = [];
for (let [port, port_row] of ports.entries()) {
ret.push({ port: port, state: port_row.state, name: port_row.name });
}
io.emit("ports_scan", ret);
}
async function send_open_ports() {
let ports = (
await spawnPromise("bash", ["/conf/get_ports.sh"])
).output.trim();
if (ports !== "") {
io.emit("open_ports", ports.split(" "));
} else {
io.emit("open_ports", []);
}
}
async function send_installed_apps() {
let out = await spawnPromise("bash", ["/conf/get_installed_apps.sh"]);
if (out.code != 0) {
send_notification(
io,
false,
"Listing installed apps with frida",
out.output
);
}
try {
io.emit("installed_apps", JSON.parse(out.output));
} catch {}
}
function send_notification(socket, is_ok, context, message) {
socket.emit("notification", {
is_ok,
context,
message,
});
}
let analyzer_running = false;
-let uuids_to_analyze = [];
+type ANALYSE_REQ = {uuid: string, app_id?: string};
+let uuids_to_analyze: ANALYSE_REQ[] = [];
let analysis_results: Record<string, AnalysisResult> = {};
async function analyze() {
// CORRECTNESS: Because the UUIDs are removed from the `uuids_to_analyze` array by index,
// it's important that only one instance of this function runs at any given moment
if (analyzer_running) {
return;
}
analyzer_running = true;
try {
while (uuids_to_analyze.length !== 0) {
- let uuid = uuids_to_analyze[0];
+ let uuid = uuids_to_analyze[0].uuid;
console.info("Running uuid: " + uuid);
const cmd = await spawnPromise("bash", ["/conf/analyze.sh", uuid]);
console.info("Finished running uuid with: ", cmd);
const har_route = `/shared_buffer/app/${uuid}/har`;
if (cmd.code !== 0) {
console.error("Analysis error:", cmd.output);
analysis_results[uuid] = {
status: "analysis_failed",
error: "Failed to analyze the app",
error_code:
cmd.code == 2 ? "signature_check_failed" : "other",
};
} else {
let har = await fs.readFile(har_route, {
encoding: "utf8",
});
const adid = await fs.readFile(
`/shared_buffer/app/${uuid}/adid`,
{
encoding: "utf8",
}
);
const requested_adid = parseInt(
await fs.readFile(
`/shared_buffer/app/${uuid}/requested_adid`,
{ encoding: "utf8" }
)
);
const adid_perm_required = parseInt(
await fs.readFile(
`/shared_buffer/app/${uuid}/adid_perm_required`,
{ encoding: "utf-8" }
)
) == 0 ? false : true;
const screenshot = await fs.readFile(
`/shared_buffer/app/${uuid}/screenshot`,
{ encoding: "utf8" }
);
const fallback = parseInt(await fs.readFile(
`/shared_buffer/app/${uuid}/fallback`,
{ encoding: "utf8" }
)) == 1;
analysis_results[uuid] = {
status: "ok",
screenshot,
requested_adid,
adid_perm_required,
fallback,
summary: await summerize_har(new Blob([har]), [
[adid, "adid"],
]),
};
}
// delete the har file immediatelly
await fs.rm(har_route, { force: true, recursive: true });
// Delete the uuid
uuids_to_analyze = uuids_to_analyze.slice(1);
// Create a timer to delete the results
setTimeout(
async () => {
console.log("cleaning up uuid:", uuid);
analysis_results[uuid] = undefined;
await fs.rm(`/shared_buffer/app/${uuid}`, {
force: true,
recursive: true,
});
},
// 10 min timer
1000 * 60 * 10
);
}
} catch (e) {
console.error("Analyzing apk failed: ", e);
}
analyzer_running = false;
}
let gps_setting_in_progress = false;
//maybe check output of child processes and send errors in some way
io.on("connection", (socket) => {
send_notification(socket, true, "Server started", "");
socket.onAny((ev, ...args) => {
console.log("server got: ", ev, ...args);
});
let screenshots_in_flight = 0;
let last_screen_id = 0;
socket.on("screenshot", async (id) => {
last_screen_id = id;
if (screenshots_in_flight > 1) return;
screenshots_in_flight++;
try {
let screenshot = await spawnPromise("bash", [
"/conf/screenshot.sh",
]);
let image_data_base64 = screenshot.output;
socket.emit("screenshot_data", {
data: image_data_base64,
resp_id: last_screen_id,
});
} catch (err) {
console.error(
"Failed to get the screenshot. The emulator probably died",
err
);
return;
}
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--;
});
let scan_ports_requests_in_flight = 0;
socket.on("ports_scan_req", async () => {
if (scan_ports_requests_in_flight > 0) {
send_notification(
socket,
false,
"A scan is already in progress...",
""
);
return;
}
scan_ports_requests_in_flight++;
await send_ports_scan();
scan_ports_requests_in_flight--;
send_notification(socket, true, "Nmap scan finished...", "");
});
socket.on("start_frida_app", async (app_id) => {
await spawnPromise("bash", ["/conf/start_frida_app.sh", app_id]);
send_notification(
socket,
true,
"Frida intercepting stopped",
"the traffic of " + app_id + " is no longer being intercepted"
);
});
socket.on("shallow_reset", async () => {
send_notification(socket, true, "Shallow reset is in progress", "");
await spawnPromise("bash", ["/conf/restart_mitmproxy.sh"]);
send_notification(socket, true, "Shallow reset is finished", "");
});
socket.on("full_reset", async () => {
console.log("Exiting for a reaset....");
exit(0);
});
socket.on("reset_adid", async () => {
await spawnPromise("bash", ["/conf/reset_adid.sh"]);
await send_private_data();
});
socket.on("back", async () => {
if (gps_setting_in_progress) {
send_notification(
socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
}
await spawnPromise("bash", ["/conf/back.sh"]);
});
socket.on("recent", async () => {
if (gps_setting_in_progress) {
send_notification(
socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
}
await spawnPromise("bash", ["/conf/recent.sh"]);
});
socket.on("home", async () => {
if (gps_setting_in_progress) {
send_notification(
socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
}
await spawnPromise("bash", ["/conf/home.sh"]);
});
socket.on("install", async () => {
const res = await spawnPromise("bash", ["/conf/install.sh"]);
send_notification(
socket,
res.code === 0,
"Installing the application",
res.output
);
});
// https://ihateregex.io/expr/uuid/
// The \b seams to be redundant
const uuid_regex =
/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/;
- socket.on("analyze", async (uuid) => {
- // this is not a user controlled input,
- // but just in case this changes later
- if (uuid_regex.exec(uuid) !== null) {
- uuids_to_analyze.push(uuid);
- analyze();
- }
+
+ socket.on("analyze", async (param: ANALYSE_REQ) => {
+ uuids_to_analyze.push(param);
+ analyze();
});
socket.on(
"analysis_status",
- async (uuid, callback: (resp: AnalysisResponse) => void) => {
+ async (uuid: string, callback: (resp: AnalysisResponse) => void) => {
// We need to validate the uuid, so the users don't start
// doing anything dumb (injecting commands)
if (uuid_regex.exec(uuid) === null) {
callback({ status: "invalid_uuid" });
return;
}
- let idx = uuids_to_analyze.indexOf(uuid);
+ let idx = uuids_to_analyze.findIndex((e) => e.uuid == uuid);
if (idx != -1) {
callback({ status: "waiting", queue_position: idx });
return;
}
if (analysis_results[uuid] === undefined) {
callback({ status: "job_not_found" });
return;
}
callback(analysis_results[uuid]);
}
);
+ socket.on(
+ "queue_status",
+ async (callback: (resp: ANALYSE_REQ[]) => void) => {
+ callback(uuids_to_analyze)
+ }
+ )
// drag handles both drag and click
socket.on("motionevent", async (data) => {
if (gps_setting_in_progress) {
send_notification(
socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
}
await spawnPromise("bash", [
"/conf/motionevent.sh",
data.motionType,
data.x + "",
data.y + "",
]);
});
// drag handles both drag and click
socket.on("drag", async (data) => {
if (gps_setting_in_progress) {
send_notification(
socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
}
await spawnPromise("bash", [
"/conf/drag.sh",
data.startX + "",
data.startY + "",
data.endX + "",
data.endY + "",
data.dragTime + "",
]);
});
socket.on("key", async (data) => {
if (gps_setting_in_progress) {
send_notification(
socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
}
await spawnPromise("bash", ["/conf/press_key.sh", data.key]);
});
socket.on("setcoord", async (data) => {
if (gps_setting_in_progress) {
send_notification(
socket,
false,
"Interactions not allowed when setting gps coordinates",
""
);
return;
}
gps_setting_in_progress = true;
const res = await spawnPromise("bash", [
"/conf/set_geo_full.sh",
data.lon + "",
data.lat + "",
]);
send_notification(
socket,
res.code === 0,
"Setting the moch location",
res.output
);
gps_setting_in_progress = false;
await send_private_data();
});
});
io.listen(3000);
console.log("listening on port 3000");
diff --git a/http_server/code/index.ts b/http_server/code/index.ts
index 219c970..461ca20 100644
--- a/http_server/code/index.ts
+++ b/http_server/code/index.ts
@@ -1,85 +1,91 @@
// This is always on the first line, to avoid the majority of git conflicts
import "./webui"
import express from "express";
import { writeFile } from "node:fs/promises";
import { execSync } from "node:child_process";
import fileUpload from "express-fileupload";
const analysis_app = express();
analysis_app.use(express.urlencoded({ extended: false }));
analysis_app.use(express.text({ limit: "500mb" }));
analysis_app.use(fileUpload());
import { randomUUID } from "node:crypto";
import { back } from "./shared"
analysis_app.post("/analyze_apk", async function (req, res) {
try {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send("No files were uploaded.");
}
let uuid = randomUUID();
let dir = `/shared_buffer/app/${uuid}`;
execSync(`mkdir -p ${dir}`);
if (Array.isArray(req.files.app)) {
for (const [idx, file] of req.files.app.entries()) {
// SAFETY: The only user controlled input is the file names, and the file contents
// We replace the file names with indexes here
await file.mv(`${dir}/${idx}.apk`);
}
} else {
await req.files.app.mv(`${dir}/0.apk`);
}
- back.emit("analyze", uuid);
+ back.emit("analyze", {uuid: uuid});
res.status(200).send(uuid);
} catch (e) {
console.log(e);
res.status(500);
}
});
analysis_app.post("/analyze_app_id", async function (req, res) {
console.log(req.body);
let app_id = Object.keys(req.body)[0];
// allows for versions with org.example.app@100.2.3
let re = /^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+){1,}(?:@(?:[0-9]+\.?)+)?$/;
if (typeof app_id != "string" || !re.test(app_id)) {
return res.status(400).send("Invalid request");
}
let uuid = randomUUID();
let dir = `/shared_buffer/app/${uuid}`;
execSync(`mkdir -p ${dir}`);
await writeFile(`${dir}/app_name`, app_id);
- back.emit("analyze", uuid);
+ back.emit("analyze", {uuid: uuid, app_id: app_id});
res.status(200).send(uuid);
});
analysis_app.post("/analysis_status", async function (req, res) {
console.log(req.body);
let uuid = Object.keys(req.body)[0];
if (typeof uuid != "string") {
return res.status(400).send("Invalid request");
}
// SAFETY: the UUID is verified / parsed in the backend
back.emit("analysis_status", uuid, (response: any) => {
return res.status(200).send(JSON.stringify(response));
});
});
+analysis_app.get("/queue_status", async function (_req, res) {
+ back.emit("queue_status", (response: any) => {
+ return res.status(200).send(JSON.stringify(response));
+ })
+})
+
analysis_app.listen(8081, () =>
console.log("Analysis API listening on port 8081")
);
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 9, 13:35 (16 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1182188
Default Alt Text
(15 KB)
Attached To
Mode
R134 rentgen-android
Attached
Detach File
Event Timeline