dsad
This commit is contained in:
135
src/components/modules/AdminFinder.vue
Normal file
135
src/components/modules/AdminFinder.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import Terminal from "../Terminal.vue";
|
||||
import Prompt from "../Prompt.vue";
|
||||
import { useTerminal } from "../../composables/useTerminal";
|
||||
|
||||
interface AdminHit {
|
||||
url: string;
|
||||
status: number;
|
||||
size: number;
|
||||
title?: string;
|
||||
platform?: string;
|
||||
login_form: boolean;
|
||||
auth_header?: string;
|
||||
redirect?: string;
|
||||
}
|
||||
|
||||
const baseUrl = ref("https://example.com");
|
||||
const useBuiltin = ref(true);
|
||||
const extraPaths = ref("");
|
||||
const acceptStatusStr = ref("200,301,302,401,403");
|
||||
const concurrency = ref(40);
|
||||
const timeoutMs = ref(5000);
|
||||
const followRedirects = ref(false);
|
||||
const running = ref(false);
|
||||
const progress = ref({ done: 0, total: 0 });
|
||||
const hits = ref<AdminHit[]>([]);
|
||||
const log = useTerminal();
|
||||
const unlistens: UnlistenFn[] = [];
|
||||
const builtinCount = ref(0);
|
||||
|
||||
async function loadCount() {
|
||||
try {
|
||||
const list: string[] = await invoke("admin_finder_wordlist");
|
||||
builtinCount.value = list.length;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
if (running.value) return;
|
||||
running.value = true;
|
||||
hits.value = [];
|
||||
progress.value = { done: 0, total: 0 };
|
||||
log.clear();
|
||||
|
||||
const extras = extraPaths.value.split(/[\s\n,]+/).map(s => s.trim()).filter(Boolean);
|
||||
const statuses = acceptStatusStr.value.split(/[,\s]+/).map(s => parseInt(s, 10)).filter(n => !isNaN(n));
|
||||
|
||||
log.info(`target: ${baseUrl.value}`);
|
||||
log.info(`paths: builtin=${useBuiltin.value ? builtinCount.value : 0} + ${extras.length} extra`);
|
||||
|
||||
unlistens.push(await listen<string>("adminfind:status", (e) => log.dim(String(e.payload))));
|
||||
unlistens.push(await listen<{ done: number; total: number }>("adminfind:progress", (e) => { progress.value = e.payload; }));
|
||||
unlistens.push(await listen<AdminHit>("adminfind:hit", (e) => {
|
||||
const h = e.payload;
|
||||
hits.value.push(h);
|
||||
const flags: string[] = [];
|
||||
if (h.login_form) flags.push("LOGIN");
|
||||
if (h.auth_header) flags.push("AUTH");
|
||||
if (h.platform) flags.push(h.platform);
|
||||
const flagStr = flags.length ? `[${flags.join("|")}]` : "";
|
||||
const line = `${String(h.status).padEnd(4)} ${h.url} ${flagStr}${h.title ? ` "${h.title.slice(0, 50)}"` : ""}`;
|
||||
if (h.login_form || h.auth_header) log.hit(line);
|
||||
else log.valid(line);
|
||||
}));
|
||||
|
||||
try {
|
||||
await invoke("admin_finder_run", {
|
||||
req: {
|
||||
base_url: baseUrl.value.trim(),
|
||||
extra_paths: extras,
|
||||
use_builtin: useBuiltin.value,
|
||||
accept_status: statuses,
|
||||
concurrency: concurrency.value,
|
||||
timeout_ms: timeoutMs.value,
|
||||
follow_redirects: followRedirects.value,
|
||||
},
|
||||
});
|
||||
const logins = hits.value.filter(h => h.login_form || h.auth_header).length;
|
||||
log.ok(`done: ${hits.value.length} responses, ${logins} login panel(s) found`);
|
||||
} catch (e: any) {
|
||||
log.err(String(e));
|
||||
} finally {
|
||||
running.value = false;
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() { while (unlistens.length) { const u = unlistens.pop(); if (u) u(); } }
|
||||
|
||||
onMounted(loadCount);
|
||||
onUnmounted(cleanup);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="module">
|
||||
<div class="form">
|
||||
<Prompt label="target" v-model="baseUrl" placeholder="https://target.com" />
|
||||
|
||||
<div class="row">
|
||||
<label class="mini wide"><span>status</span><input v-model="acceptStatusStr" placeholder="200,301,302,401,403" /></label>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label class="toggle"><input type="checkbox" v-model="useBuiltin" /><span>builtin ({{ builtinCount }})</span></label>
|
||||
<label class="toggle"><input type="checkbox" v-model="followRedirects" /><span>follow</span></label>
|
||||
<label class="mini"><span>conc</span><input v-model.number="concurrency" type="number" min="1" max="200" /></label>
|
||||
<label class="mini"><span>t/out</span><input v-model.number="timeoutMs" type="number" min="500" max="30000" /></label>
|
||||
</div>
|
||||
|
||||
<details class="adv">
|
||||
<summary>extra paths (optional)</summary>
|
||||
<textarea class="term-input" v-model="extraPaths" rows="3" spellcheck="false" placeholder="one path per line, e.g. panel/admin, custom-admin-path" />
|
||||
</details>
|
||||
|
||||
<button class="exec" :disabled="running" @click="run">{{ running ? "[ probing... ]" : "> find admin" }}</button>
|
||||
|
||||
<div v-if="progress.total" class="bar">
|
||||
<div class="bar-fill" :style="{ width: (progress.done / progress.total) * 100 + '%' }" />
|
||||
<span class="bar-text">{{ progress.done }} / {{ progress.total }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Terminal :lines="log.lines.value" title="admin-finder // panel discovery" @clear="log.clear()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.adv { border: 1px solid var(--border); background: var(--bg-panel); padding: 4px 8px; }
|
||||
.adv summary { cursor: pointer; font-size: 10px; color: var(--fg-dim); text-transform: uppercase; letter-spacing: 0.1em; padding: 2px 0; }
|
||||
.adv summary:hover { color: var(--fg); }
|
||||
.adv > *:not(summary) { margin-top: 6px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user