failed miserably at hack.lu
This commit is contained in:
BIN
2025/hack.lu/web/MARQUEE.zip
Normal file
BIN
2025/hack.lu/web/MARQUEE.zip
Normal file
Binary file not shown.
15
2025/hack.lu/web/marquee/Dockerfile
Executable file
15
2025/hack.lu/web/marquee/Dockerfile
Executable 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"]
|
||||
22
2025/hack.lu/web/marquee/README.md
Normal file
22
2025/hack.lu/web/marquee/README.md
Normal 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 :)
|
||||
37
2025/hack.lu/web/marquee/TagDeprecater/content.js
Normal file
37
2025/hack.lu/web/marquee/TagDeprecater/content.js
Normal 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;
|
||||
});
|
||||
});
|
||||
});
|
||||
45
2025/hack.lu/web/marquee/TagDeprecater/manifest.json
Normal file
45
2025/hack.lu/web/marquee/TagDeprecater/manifest.json
Normal 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>"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
2025/hack.lu/web/marquee/TagDeprecater/no_entry.png
Normal file
BIN
2025/hack.lu/web/marquee/TagDeprecater/no_entry.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
17
2025/hack.lu/web/marquee/TagDeprecater/popup.html
Normal file
17
2025/hack.lu/web/marquee/TagDeprecater/popup.html
Normal 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>
|
||||
38
2025/hack.lu/web/marquee/TagDeprecater/popup.js
Normal file
38
2025/hack.lu/web/marquee/TagDeprecater/popup.js
Normal 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
|
||||
}
|
||||
})
|
||||
})
|
||||
24
2025/hack.lu/web/marquee/TagDeprecater/styles.css
Normal file
24
2025/hack.lu/web/marquee/TagDeprecater/styles.css
Normal 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;
|
||||
}
|
||||
6
2025/hack.lu/web/marquee/TagDeprecater/sw.js
Normal file
6
2025/hack.lu/web/marquee/TagDeprecater/sw.js
Normal 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">`
|
||||
})
|
||||
})
|
||||
58
2025/hack.lu/web/marquee/TagDeprecater/test.html
Normal file
58
2025/hack.lu/web/marquee/TagDeprecater/test.html
Normal 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
65
2025/hack.lu/web/marquee/bot.js
Executable 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);
|
||||
});
|
||||
10
2025/hack.lu/web/marquee/docker-compose.yml
Normal file
10
2025/hack.lu/web/marquee/docker-compose.yml
Normal 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
|
||||
14
2025/hack.lu/web/marquee/package.json
Executable file
14
2025/hack.lu/web/marquee/package.json
Executable 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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user