failed miserably at hack.lu

This commit is contained in:
2025-10-19 20:35:43 +02:00
parent 9361bb3f29
commit c04f977a69
56 changed files with 120530 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,15 @@
FROM node:24-alpine
RUN apk update && apk add chromium
WORKDIR /app/
COPY . /app/
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
ENV PUPPETEER_SKIP_DOWNLOAD=true
RUN ["npm", "install"]
EXPOSE 3000
ENTRYPOINT ["node", "bot.js"]

View File

@@ -0,0 +1,22 @@
# Bot
change the url to the one you want to visit in docker-compose.yml
```
docker compose up --build
```
or build and run the docker container with the following command
```
docker build -t bot .
docker run -i --rm bot 'http://your-url-here'
```
see the Dockerfile and docker-compose.yml for more information
# TagDeprecater Extension
Go to chrome://extensions/ and enable developer mode, then click on load unpacked and select the extension folder.
Please dont install the extension in your main browser, it is not safe :)

View File

@@ -0,0 +1,37 @@
chrome.storage.sync.get('replacementString', ({ replacementString }) => {
// can be extended, from https://www.w3.org/TR/2014/REC-html5-20141028/obsolete.html#obsolete
const deprecatedTags = [
'applet',
'acronym',
'bgsound',
'dir',
'frame',
'frameset',
'noframes',
'hgroup',
'isindex',
'listing',
'nextid',
'noembed',
'plaintext',
'strike',
'xmp',
'basefont',
'big',
'blink',
'center',
'font',
'marquee',
'multicol',
'nobr',
'spacer',
'tt'
];
deprecatedTags.forEach((tag) => {
const elements = document.querySelectorAll(tag);
elements.forEach((el) => {
el.outerHTML = replacementString;
});
});
});

View File

@@ -0,0 +1,45 @@
{
"manifest_version": 3,
"name": "TagDeprecater",
"version": "1.0",
"description": "Fix the web by deprecating HTML tags.",
"permissions": [
"activeTab",
"storage"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "sw.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": "no_entry.png"
},
"icons": {
"16": "no_entry.png",
"48": "no_entry.png",
"128": "no_entry.png"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js"
]
}
],
"web_accessible_resources": [
{
"resources": [
"*"
],
"matches": [
"<all_urls>"
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>TagDeprecater</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>TagDeprecater</h1>
<label for="replacement">Replacement String:</label>
<textarea id="replacement"></textarea>
<button id="save">Save</button><button id="reset">Reset</button>
<script src="popup.js"></script>
</body>
</html>

View File

@@ -0,0 +1,38 @@
document.addEventListener("DOMContentLoaded", () => {
const replacementInput = document.getElementById("replacement")
const saveButton = document.getElementById("save")
const resetButton = document.getElementById("reset")
const DEFAULT = `<img src="${chrome.runtime.getURL('no_entry.png')}" alt="STOP">`
// Load current replacement string
chrome.storage.sync.get("replacementString", ({ replacementString }) => {
replacementInput.value = replacementString
})
// Save new replacement string
saveButton.addEventListener("click", () => {
const newReplacement = replacementInput.value || DEFAULT
chrome.storage.sync.set({ replacementString: newReplacement }, () => {
alert("Replacement string saved!")
})
})
// Reset to default
resetButton.addEventListener("click", () => {
chrome.storage.sync.set({ replacementString: DEFAULT }, () => {
replacementInput.value = DEFAULT
})
})
// Listen for messages from content script
window.addEventListener("message", (event) => {
if (event.data.type === "GET") {
chrome.storage.sync.get("replacementString", ({ replacementString }) => {
event.source.postMessage({ type: "REPLACEMENT_STRING", replacementString }, event.origin)
})
} else if (event.data.type === "SET") {
chrome.storage.sync.set({ replacementString: event.data.replacementString })
replacementInput.value = event.data.replacementString
}
})
})

View File

@@ -0,0 +1,24 @@
body {
font-family: Arial, sans-serif;
margin: 10px;
}
textarea {
width: 100%;
height: 60px;
margin-bottom: 10px;
}
button {
background-color: #0078D7;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 5px;
margin-right: 5px;
}
button:hover {
background-color: #0056A4;
}

View File

@@ -0,0 +1,6 @@
chrome.runtime.onInstalled.addListener(() => {
// Set default replacement string
chrome.storage.sync.set({
replacementString: `<img src="${chrome.runtime.getURL('no_entry.png')}" alt="STOP">`
})
})

View File

@@ -0,0 +1,58 @@
<script>
function sendMessage(type, payload = {}) {
// In this simulation, the target window is the current window.
// In a real exploit, you'd target the window object of the extension's frame/tab.
const targetWindow = window;
const targetOrigin = '*'; // Since the listener is vulnerable, we can use '*'
const message = { type, ...payload };
targetWindow.postMessage(message, targetOrigin);
}
// The attack sequence
function runAttack() {
console.log("--- ATTACK SEQUENCE STARTED ---", 'action');
// ----------------------------------------------------
// STEP 1: Attacker wants to READ the current stored value (GET)
// ----------------------------------------------------
console.log("ATTACKER: Initiating GET request to read current value.", 'action');
sendMessage("GET");
// ----------------------------------------------------
// STEP 2: Attacker wants to WRITE a malicious value (SET)
// ----------------------------------------------------
const maliciousPayload = "XSS_PAYLOAD_HERE_123456789";
console.log(`ATTACKER: Initiating SET request to override value with: ${maliciousPayload}...`, 'action');
sendMessage("SET", { replacementString: maliciousPayload });
// ----------------------------------------------------
// STEP 3: Attacker listens for the response (for the GET request)
// ----------------------------------------------------
window.addEventListener("message", function listener(event) {
// IMPORTANT: The attacker must also check event.origin for security!
// However, since we are only worried about receiving the response here:
if (event.data.type === "REPLACEMENT_STRING") {
console.log(`ATTACKER: Successfully received data from extension! Stored Value: ${event.data.replacementString}`, 'success');
// You can remove the listener after receiving the response if needed
// window.removeEventListener("message", listener);
}
});
// ----------------------------------------------------
// STEP 4: Confirming the write worked by reading it back (GET)
// ----------------------------------------------------
setTimeout(() => {
console.log("ATTACKER: Re-reading value to confirm malicious write worked.", 'action');
sendMessage("GET");
}, 500); // Wait briefly for the async SET operation to complete
}
// Start the simulation when the DOM is ready
document.addEventListener('DOMContentLoaded', () => {
runAttack();
});
</script>

65
2025/hack.lu/web/marquee/bot.js Executable file
View File

@@ -0,0 +1,65 @@
const puppeteer = require('puppeteer');
const http = require('http');
const TIMEOUT_SECS = parseInt(process.env.TIMEOUT_SECS || '30', 10);
const FLAG = process.env.FLAG || 'flag{***************************}';
const EXTENSIONPATH = '/app/TagDeprecater';
if (process.argv.length < 3) {
console.log(`Usage: node ${process.argv[1]} <url>`);
process.exit(1);
}
const URL = process.argv[2];
if (!URL || URL === '' || typeof (URL) !== 'string' || !/^https?:\/\/./.test(URL)) {
console.log('[bot] No URL provided!');
process.exit(1);
}
// start the flag webserver
const flagServer = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`<marquee>${FLAG}</marquee>`);
});
flagServer.listen(1337);
(async () => {
// launch a browser with our config
const browser = await puppeteer.launch({
headless: true,
pipe: true,
args: [
`--disable-extensions-except=${EXTENSIONPATH}`,
`--load-extension=${EXTENSIONPATH}`,
// disable stuff we do not need
'--disable-gpu', '--disable-software-rasterizer', '--disable-dev-shm-usage',
// disable sandbox since it does not work inside docker
'--no-sandbox',
// no exploits please
"--js-flags=--noexpose_wasm,--jitless",
],
});
// close the browser after TIMEOUT_SECS seconds
setTimeout(() => {
console.log('[bot] Timeout reached, closing browser');
browser.close();
process.exit(0);
}, TIMEOUT_SECS * 1000);
// open a new page
let page = await browser.newPage();
// log console messages
page.on('console', msg => console.log('[console]', msg.text()));
// open the link
console.log(`[bot] Visiting URL: ${URL}`);
await page.goto(URL);
})().catch(error => {
console.log('[bot] Error:', error);
process.exit(1);
});

View File

@@ -0,0 +1,10 @@
# docker run -i --rm bot 'http://your-url-here'
services:
bot:
build: .
restart: no
environment:
- TIMEOUT_SECS=10
- FLAG=flag{***************************}
command: http://your-url-here

View File

@@ -0,0 +1,14 @@
{
"name": "bot-instance",
"version": "1.0.0",
"description": "Admin bot instance",
"main": "bot.js",
"scripts": {
"start": "node bot.js"
},
"author": "kunte_",
"license": "MIT",
"dependencies": {
"puppeteer": "latest"
}
}