mirror of
https://github.com/agresdominik/file-leak.git
synced 2026-04-21 18:05:48 +00:00
Proof of concept
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "File-Leak",
|
||||||
|
"version": "1.0",
|
||||||
|
|
||||||
|
"description": "Find hidden files on websites you visit which are not supposed to be there",
|
||||||
|
"homepage_url": "https://agres.online",
|
||||||
|
|
||||||
|
"icons": {
|
||||||
|
"32": "static/icons/icon32.png",
|
||||||
|
"48": "static/icons/icon48.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"webNavigation",
|
||||||
|
"storage",
|
||||||
|
"<all_urls>",
|
||||||
|
"http://*/*",
|
||||||
|
"https://*/*"
|
||||||
|
],
|
||||||
|
|
||||||
|
"browser_action": {
|
||||||
|
"default_icon": "static/icons/icon48.png",
|
||||||
|
"default_title": "File-leak",
|
||||||
|
"default_popup": "popup/html/main.html"
|
||||||
|
},
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"scripts": ["popup/js/background.js"],
|
||||||
|
"persistent": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"js": ["popup/js/visual.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"web-ext": "^9.1.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start:firefox": "web-ext run --source-dir ./"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: #1e2430;
|
||||||
|
color: #e4e7ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-content {
|
||||||
|
padding: 12px;
|
||||||
|
width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #f0f3f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
label, button {
|
||||||
|
display: block;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: #2d3441;
|
||||||
|
border: 1px solid #3a4253;
|
||||||
|
color: #e4e7ec;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #3a4253;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry-box {
|
||||||
|
margin-top: 10px;
|
||||||
|
background: #252b36;
|
||||||
|
border: 1px solid #333b49;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.2fr 1fr auto auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px;
|
||||||
|
background: #2b3240;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry span {
|
||||||
|
font-size: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry button {
|
||||||
|
padding: 4px 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html lang="en-US">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" href="../css/styles.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="popup-content">
|
||||||
|
|
||||||
|
<h1> Hidden Files </h1>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="listenerToggle"> Enable scanning
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button id="runOnceBtn">Scan this site</button>
|
||||||
|
|
||||||
|
<div id="entry-box">
|
||||||
|
<div id="entry-list"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../js/visual.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
// function to scan the url the tab is on for hidden files
|
||||||
|
|
||||||
|
async function onTabUpdate(tabId, changeInfo, tab) {
|
||||||
|
if (changeInfo.status !== "complete") return;
|
||||||
|
|
||||||
|
const url = new URL(tab.url);
|
||||||
|
const hostname = url.hostname;
|
||||||
|
|
||||||
|
const stored = await browser.storage.local.get("entries");
|
||||||
|
const existingEntries = stored.entries || [];
|
||||||
|
|
||||||
|
const alreadyDone = existingEntries.some(e => e.domainField === hostname);
|
||||||
|
if (alreadyDone) return;
|
||||||
|
|
||||||
|
async function tryFetch(pathname) {
|
||||||
|
const target = url.origin + pathname;
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await fetch(target, { redirect: "manual" });
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return response.status === 200 ? target : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
env: await tryFetch("/.env"),
|
||||||
|
git: await tryFetch("/.git"),
|
||||||
|
dsstore: await tryFetch("/.DS_Store"),
|
||||||
|
config: await tryFetch("/.config"),
|
||||||
|
svn: await tryFetch("/.svn"),
|
||||||
|
npm: await tryFetch("/.npm"),
|
||||||
|
hg: await tryFetch("/.hg"),
|
||||||
|
docker: await tryFetch("/.docker"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const newEntries = [...existingEntries];
|
||||||
|
|
||||||
|
for (const [key, foundPath] of Object.entries(results)) {
|
||||||
|
if (!foundPath) continue;
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
domainField: hostname,
|
||||||
|
pathField: foundPath,
|
||||||
|
type: key
|
||||||
|
};
|
||||||
|
|
||||||
|
newEntries.push(entry);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.storage.local.set({ entries: newEntries });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable, Idsable automatic listener and the message listener for it
|
||||||
|
|
||||||
|
function enableListener() {
|
||||||
|
if (!browser.tabs.onUpdated.hasListener(onTabUpdate)) {
|
||||||
|
browser.tabs.onUpdated.addListener(onTabUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableListener() {
|
||||||
|
if (browser.tabs.onUpdated.hasListener(onTabUpdate)) {
|
||||||
|
browser.tabs.onUpdated.removeListener(onTabUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener((msg) => {
|
||||||
|
if (msg.type === "toggleListener") {
|
||||||
|
if (msg.enabled) enableListener();
|
||||||
|
else disableListener();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Singe run, can be merged with onTabUpdate function
|
||||||
|
|
||||||
|
async function runSingleScan() {
|
||||||
|
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||||
|
if (!tabs.length) return;
|
||||||
|
const tab = tabs[0];
|
||||||
|
const url = new URL(tab.url);
|
||||||
|
const hostname = url.hostname;
|
||||||
|
|
||||||
|
async function tryFetch(path) {
|
||||||
|
const target = url.origin + path;
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await fetch(target, { redirect: "manual" });
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return response.status === 200 ? target : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
env: await tryFetch("/.env"),
|
||||||
|
git: await tryFetch("/.git"),
|
||||||
|
dsstore: await tryFetch("/.DS_Store"),
|
||||||
|
config: await tryFetch("/.config"),
|
||||||
|
svn: await tryFetch("/.svn"),
|
||||||
|
npm: await tryFetch("/.npm"),
|
||||||
|
hg: await tryFetch("/.hg"),
|
||||||
|
docker: await tryFetch("/.docker"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const stored = await browser.storage.local.get("entries");
|
||||||
|
const entries = stored.entries || [];
|
||||||
|
|
||||||
|
for (const [key, foundPath] of Object.entries(results)) {
|
||||||
|
if (!foundPath) continue;
|
||||||
|
|
||||||
|
entries.push({
|
||||||
|
domainField: hostname,
|
||||||
|
pathField: foundPath,
|
||||||
|
type: key
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.storage.local.set({ entries });
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener((msg) => {
|
||||||
|
if (msg.type === "runOnce") {
|
||||||
|
runSingleScan();
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// Handling of adding saving and loading list entries
|
||||||
|
|
||||||
|
function addEntry(domain, path) {
|
||||||
|
|
||||||
|
const list = document.getElementById("entry-list");
|
||||||
|
|
||||||
|
const entry = document.createElement("div");
|
||||||
|
entry.className = "entry";
|
||||||
|
|
||||||
|
const domainField = document.createElement("span");
|
||||||
|
domainField.textContent = domain;
|
||||||
|
|
||||||
|
const pathField = document.createElement("span");
|
||||||
|
pathField.textContent = path;
|
||||||
|
|
||||||
|
const deleteButton = document.createElement("button");
|
||||||
|
deleteButton.textContent = "Delete";
|
||||||
|
deleteButton.addEventListener("click", function () {
|
||||||
|
entry.remove();
|
||||||
|
saveAllEntries();
|
||||||
|
})
|
||||||
|
|
||||||
|
const openButton = document.createElement("button");
|
||||||
|
openButton.textContent = "Open";
|
||||||
|
openButton.addEventListener("click", function () {
|
||||||
|
const url = pathField.textContent;
|
||||||
|
if (url) {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
entry.appendChild(domainField);
|
||||||
|
entry.appendChild(pathField);
|
||||||
|
entry.appendChild(deleteButton);
|
||||||
|
entry.appendChild(openButton);
|
||||||
|
|
||||||
|
list.appendChild(entry);
|
||||||
|
|
||||||
|
saveAllEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAllEntries() {
|
||||||
|
const entries = [];
|
||||||
|
|
||||||
|
document.querySelectorAll(".entry").forEach(entry => {
|
||||||
|
const spans = entry.querySelectorAll("span");
|
||||||
|
entries.push({
|
||||||
|
domainField: spans[0].textContent,
|
||||||
|
pathField: spans[1].textContent
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.storage.local.set({ entries });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadEntries() {
|
||||||
|
const stored = await browser.storage.local.get("entries");
|
||||||
|
if (!stored.entries) return;
|
||||||
|
|
||||||
|
for (const data of stored.entries) {
|
||||||
|
addEntry(data.domainField, data.pathField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load entries when popup starts
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
loadEntries();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle persistance for toggle button
|
||||||
|
|
||||||
|
const toggle = document.getElementById("listenerToggle");
|
||||||
|
|
||||||
|
browser.storage.local.get("listenerEnabled").then(({ listenerEnabled }) => {
|
||||||
|
toggle.checked = !!listenerEnabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
toggle.addEventListener("change", (e) => {
|
||||||
|
const enabled = e.target.checked;
|
||||||
|
browser.storage.local.set({ listenerEnabled: enabled });
|
||||||
|
browser.runtime.sendMessage({ type: "toggleListener", enabled });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run one scan on button press
|
||||||
|
|
||||||
|
document.getElementById("runOnceBtn").addEventListener("click", () => {
|
||||||
|
browser.runtime.sendMessage({ type: "runOnce" });
|
||||||
|
});
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 662 B |
Binary file not shown.
|
After Width: | Height: | Size: 869 B |
Reference in New Issue
Block a user