cleanup
This commit is contained in:
11
2026/kalmar/web/handout/README.md
Normal file
11
2026/kalmar/web/handout/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Exposed
|
||||
Only the bot container and the aperisolve web container will be exposed
|
||||
# Run
|
||||
```sh
|
||||
./build.sh
|
||||
```
|
||||
# Submit
|
||||
You can submit urls to the bot with:
|
||||
```sh
|
||||
curl http://127.0.0.1:8091/report --json '{"url":"http://web:5000/?..."}'
|
||||
````
|
||||
17
2026/kalmar/web/handout/bot/Dockerfile
Normal file
17
2026/kalmar/web/handout/bot/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM fedora:latest
|
||||
|
||||
RUN dnf update -y && dnf install -y chromium nodejs
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY bot.js .
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||
|
||||
RUN useradd -m user
|
||||
USER user
|
||||
CMD ["node", "bot.js"]
|
||||
73
2026/kalmar/web/handout/bot/bot.js
Normal file
73
2026/kalmar/web/handout/bot/bot.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const express = require('express');
|
||||
const puppeteer = require('puppeteer');
|
||||
const app = express();
|
||||
|
||||
const FLAG = process.env.FLAG || 'kalmar{test_flag}';
|
||||
const CHALLENGE_DOMAIN = process.env.CHALLENGE_DOMAIN || 'http://web:5000/';
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(res => setTimeout(res, ms));
|
||||
}
|
||||
|
||||
async function visitUrl(url) {
|
||||
const browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
args: [
|
||||
// this is not a pwn challenge, please don't rce us
|
||||
'--disable-extensions',
|
||||
'--disable-gpu',
|
||||
'--disable-software-rasterizer',
|
||||
'--js-flags=--noexpose_wasm,--jitless',
|
||||
'--no-sandbox',
|
||||
]
|
||||
});
|
||||
|
||||
try {
|
||||
const page = await browser.newPage();
|
||||
|
||||
await page.goto(CHALLENGE_DOMAIN, {
|
||||
waitUntil: 'networkidle0',
|
||||
});
|
||||
|
||||
// Flag is stored in localStorage
|
||||
await page.evaluate((flag) => {
|
||||
localStorage.setItem("flag", flag);
|
||||
}, FLAG);
|
||||
|
||||
await sleep(1000);
|
||||
await page.close()
|
||||
|
||||
// now visit reported page
|
||||
const page2 = await browser.newPage();
|
||||
await page2.goto(url, {waitUntil: []});
|
||||
|
||||
await sleep(5000);
|
||||
} catch (err) {
|
||||
console.error('Error visiting page:', err);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
app.post('/report', async (req, res) => {
|
||||
const { url } = req.body;
|
||||
|
||||
if (!url || typeof url !== 'string' || !url.startsWith(CHALLENGE_DOMAIN + '?')) {
|
||||
return res.status(400).json({ error: `Invalid URL. Url should be a string and start with ${CHALLENGE_DOMAIN + '?'}` });
|
||||
}
|
||||
|
||||
try {
|
||||
await visitUrl(url);
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
console.error('Error on /report', err);
|
||||
res.status(500).json({ error: 'Failed to visit URL' });
|
||||
}
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 8080;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Bot listening on port ${PORT}`);
|
||||
});
|
||||
6
2026/kalmar/web/handout/bot/package.json
Normal file
6
2026/kalmar/web/handout/bot/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"express": "^5.2.1",
|
||||
"puppeteer": "^24.40.0"
|
||||
}
|
||||
}
|
||||
21
2026/kalmar/web/handout/build.sh
Executable file
21
2026/kalmar/web/handout/build.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env sh
|
||||
git clone https://github.com/Zeecka/AperiSolve.git
|
||||
cd AperiSolve
|
||||
|
||||
# Pinning repo version to 3.0.7
|
||||
git checkout d86416c97d508c14fdda12297fb510d6e7080b13
|
||||
|
||||
# Pinning container version
|
||||
# Version 3.0.7: https://github.com/zeecka/AperiSolve/pkgs/container/aperisolve/versions
|
||||
sed --in-place -e "s/image: ghcr.io\/zeecka\/aperisolve:latest/image: ghcr.io\/zeecka\/aperisolve@sha256:c7f1f827e04d6d98b99f86845fcca31c2516cf13869270542c10b2ff4a6d9cd1/g" compose.yml
|
||||
|
||||
|
||||
# Just for your convencience:
|
||||
sed --in-place -e "s/restart: always/restart: unless-stopped/g" compose.yml
|
||||
|
||||
cp .env.example .env
|
||||
cd ..
|
||||
|
||||
docker compose up --build; docker compose down
|
||||
# If your user is not in the docker group, then run:
|
||||
# sudo docker compose up --build; sudo docker compose down
|
||||
19
2026/kalmar/web/handout/compose.yml
Normal file
19
2026/kalmar/web/handout/compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
nginx:
|
||||
build: ./nginx
|
||||
ports:
|
||||
- "8091:80"
|
||||
depends_on:
|
||||
- web
|
||||
- bot
|
||||
|
||||
bot:
|
||||
build: ./bot
|
||||
# ports:
|
||||
# - 3123:3123
|
||||
environment:
|
||||
- FLAG=kalmar{test_flag}
|
||||
- PORT=3123
|
||||
|
||||
include:
|
||||
- AperiSolve/compose.yml
|
||||
4
2026/kalmar/web/handout/nginx/Dockerfile
Normal file
4
2026/kalmar/web/handout/nginx/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
# Just adding nginx so we can expose everything over one port. It's not part of the challenge
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
17
2026/kalmar/web/handout/nginx/nginx.conf
Normal file
17
2026/kalmar/web/handout/nginx/nginx.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location /report {
|
||||
proxy_pass http://bot:3123;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://web:5000;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user