This commit is contained in:
2026-04-15 01:05:54 +02:00
parent db0324c43d
commit bc0c08342d
84 changed files with 28780 additions and 0 deletions

View 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/?..."}'
````

View 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"]

View 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}`);
});

View File

@@ -0,0 +1,6 @@
{
"dependencies": {
"express": "^5.2.1",
"puppeteer": "^24.40.0"
}
}

View 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

View 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

View 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

View 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;
}
}
}