My solutions for CTFs
0

Configure Feed

Select the types of activity you want to include in your feed.

CognitiveHack 2026

Takumi Akimoto (May 17, 2026, 4:46 PM +0900) 8e1acb06 622f54f1

+1113
+193
cognitivehack/2026/fool-the-lockout/app.py
··· 1 + from flask import Flask, render_template, request, redirect, url_for, session, make_response 2 + import time 3 + import secrets 4 + import json 5 + 6 + 7 + app = Flask(__name__) 8 + app.secret_key = secrets.token_hex(16) 9 + 10 + user_db = {} 11 + """ format -> 12 + username: "password" 13 + } 14 + """ 15 + 16 + request_rates = {} 17 + """ format -> 18 + "ip_addr":{ 19 + "num_requests": int 20 + "epoch_start": timestamp 21 + "lockout_until" : int # -1 if not locked out, timestamp of lockout end 22 + } 23 + """ 24 + 25 + MAX_REQUESTS = 10 # max failed attempts before a user is locked out 26 + EPOCH_DURATION = 30 # timeframe for failed attempts (in seconds) 27 + LOCKOUT_DURATION = 120 # duration a user will be locked out for (in seconds) 28 + 29 + RATE_LIMITED_HTML = "<h1>Rate Limited Exceeded</h1><p>You have sent too many requests, requests from your IP will be temporarily blocked.</p>" 30 + 31 + 32 + 33 + ## ------------------------ HELPER FUNCTIONS ------------------------ ## 34 + 35 + """Quick function to no-cache web page responses""" 36 + def no_cache(response): 37 + response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" 38 + response.headers["Pragma"] = "no-cache" 39 + response.headers["Expires"] = "-1" 40 + return response 41 + 42 + 43 + """Returns true if a user is logged in, false otherwise""" 44 + def logged_in(): 45 + if "user" in session: 46 + return True 47 + return False 48 + 49 + 50 + """Returns the current user (or None if there is none)""" 51 + def current_user(): 52 + if "user" in session: 53 + return session["user"] 54 + return None 55 + 56 + 57 + """Add a new user to db""" 58 + def add_new_user(username, password): 59 + user_db[username] = password 60 + print("Added (username=%s, password=%s) to user_db" % (username, password)) 61 + 62 + 63 + """ Updates the request rates db for a given client ip, since information will likely be stale.""" 64 + def refresh_request_rates_db(client_ip): 65 + curr_time = time.time() 66 + if client_ip not in request_rates: 67 + return 68 + 69 + # check if attempt interval has elapsed, if so sets it to 0 70 + epoch_start_time = request_rates[client_ip]["epoch_start"] 71 + if curr_time - epoch_start_time > EPOCH_DURATION: 72 + request_rates[client_ip]["num_requests"] = 0 73 + request_rates[client_ip]["epoch_start"] = -1 74 + 75 + # if was locked out but period ended update store 76 + lockout_end = request_rates[client_ip]["lockout_until"] 77 + if (lockout_end != -1) and time.time() >= lockout_end: 78 + request_rates[client_ip]["lockout_until"] = -1 79 + 80 + 81 + """For a given user IP, checks how many requests the user has made (by updating the storage) and if 82 + the user it has exceeded the assigned rate limit. Returns true if the user has exceeded rate limit, 83 + false otherwise. """ 84 + def exceeded_rate_limit() -> bool: # Could do a daemon, but since checks of status are always done before updating its not really necessary 85 + curr_time = time.time() 86 + 87 + # Grab the IP of the client 88 + client_ip = request.remote_addr 89 + print(f"Request ip address: {client_ip}", flush=True) 90 + 91 + # refresh & add new entry to db if it doesnt exist 92 + refresh_request_rates_db(client_ip) 93 + if client_ip not in request_rates: 94 + request_rates[client_ip] = { 95 + "num_requests": 0, 96 + "epoch_start": -1, 97 + "lockout_until": -1 98 + } 99 + print(f"New entry added to db", flush=True) 100 + 101 + # log request if it was a POST 102 + if request.method == "POST": 103 + request_rates[client_ip]['num_requests'] += 1 104 + # if epoch hasnt started, set epoch 105 + if request_rates[client_ip]['epoch_start'] == -1: 106 + request_rates[client_ip]['epoch_start'] = curr_time 107 + print(f"DB updated - {client_ip}:{request_rates[client_ip]}", flush=True) 108 + 109 + # check if we exceeded rate threshold, return True if so 110 + if request_rates[client_ip]['num_requests'] > MAX_REQUESTS: 111 + if request_rates[client_ip]["lockout_until"] == -1: 112 + request_rates[client_ip]['lockout_until'] = curr_time + LOCKOUT_DURATION 113 + print("Account locked out") 114 + print(f"DB - {client_ip}:{request_rates[client_ip]}", flush=True) 115 + return True 116 + 117 + return False 118 + 119 + 120 + ## ------------------------ APP ROUTES ------------------------ ## 121 + 122 + """ Login portal """ 123 + @app.route("/login", methods=['GET', 'POST']) 124 + def login(): 125 + ## TODO - check rate limit 126 + if exceeded_rate_limit(): 127 + return RATE_LIMITED_HTML 128 + 129 + # if POST, accept form data and try to add user 130 + if request.method == "POST": 131 + user_input = request.form['username'] 132 + pswd_input = request.form['password'] 133 + print("User input: %s, password input: %s" % (user_input, pswd_input)) 134 + 135 + # non-existent user or bad password 136 + if (user_input not in user_db) or (user_db[user_input] != pswd_input): 137 + msg = f"Invalid username or password." 138 + return render_template("login.html", error=msg) 139 + 140 + # authenticate user 141 + session["user"] = user_input 142 + print("Successfully logged in, session=%s" % (session)) 143 + return redirect(url_for("index")) # note 'index' refers to the FUNCTION NAME 144 + 145 + # return normal page if 'GET' 146 + return no_cache(make_response(render_template('login.html'))) 147 + 148 + 149 + """ Homepage """ 150 + @app.route("/", methods=['GET']) 151 + def index(): 152 + if exceeded_rate_limit(): 153 + return RATE_LIMITED_HTML 154 + 155 + # authenticate 156 + if not logged_in(): 157 + return redirect(url_for("login")) 158 + 159 + # display homepage according to login 160 + user = current_user() 161 + flag = open("/challenge/flag.txt").read().strip() 162 + return no_cache(make_response(render_template("index.html", user=user, flag=flag))) 163 + 164 + 165 + """ Logout """ 166 + @app.route("/logout", methods=['GET']) 167 + def logout(): 168 + if exceeded_rate_limit(): 169 + return RATE_LIMITED_HTML 170 + 171 + if "user" in session: 172 + session.pop('user', None) 173 + print("Logged out, popped session") 174 + return redirect(url_for("login")) 175 + 176 + 177 + if __name__ == '__main__': 178 + username, password = None, None 179 + # get profile data 180 + try: 181 + with open("/challenge/profile.json", "r") as file: 182 + profile = json.load(file) 183 + username = profile["username"] 184 + password = profile["password"] 185 + except Exception as e: 186 + print(f"Error setting up profile in app:\n{e}") 187 + exit(1) 188 + 189 + # add new user 190 + add_new_user(username, password) 191 + 192 + # start app 193 + app.run(host='0.0.0.0', port=8000, debug=True)
+100
cognitivehack/2026/fool-the-lockout/creds-dump.txt
··· 1 + rora;winner1 2 + birendra;rumble 3 + khalid;sting 4 + stanislaw;ming 5 + maged;nimrod 6 + sigrid;telephon 7 + alysse;sutton 8 + emely;tyrant 9 + cornel;rodman 10 + shamira;marion 11 + cymbre;california 12 + romola;steven 13 + leisa;basketba 14 + goldie;ferrari 15 + celia;beatles 16 + kathrine;tango 17 + adrianne;iiiiii 18 + rebbecca;core 19 + meridel;bolton 20 + riva;trent 21 + dorris;sponge 22 + ngai;ellie 23 + gwynn;grizzly 24 + olenka;london1 25 + vahe;devilman 26 + germ;bigguns 27 + bradwin;doogie 28 + marinette;pic\'s 29 + kori;swimming 30 + leita;4you 31 + arzu;calimero 32 + roanna;trooper1 33 + meena;tracy 34 + beryle;zippy 35 + field;sunflowe 36 + keaton;hall 37 + amandine;whatup 38 + cherise;dean 39 + aidan;gallaries 40 + medria;locutus 41 + marga;infinite 42 + triston;kristina 43 + ljilyana;carsten 44 + paulo;chicks 45 + woodrow;14141414 46 + dacie;diamond1 47 + evy;sex4me 48 + amabelle;fatty 49 + technical;market 50 + celesta;drive 51 + sherill;icecube 52 + nadir;vides 53 + ayesha;necklace 54 + dolorita;concorde 55 + linnet;yaya 56 + clareta;yankee1 57 + colm;goblue 58 + felton;divine 59 + tera;mccabe 60 + bethan;barber 61 + rohit;berry 62 + cali;assword 63 + faina;choke 64 + saleem;ella 65 + luelle;bolitas 66 + emmey;spanner 67 + carlyn;kokoko 68 + sallyanne;mordor 69 + my;hotsex 70 + constantia;budlight 71 + tenille;lambert 72 + suria;james007 73 + princeton;olympic 74 + goska;allan 75 + joana;citroen 76 + deane;shoe 77 + brynna;church 78 + oliver;higgins 79 + erinn;nineinch 80 + val;sampson 81 + duquette;5252 82 + sieber;ripple 83 + danice;smooth 84 + romonda;texaco 85 + sinead;infiniti 86 + linette;reddog 87 + almendra;christin 88 + meriann;kajak 89 + shedman;ewtosi 90 + elody;athome 91 + doloritas;birgit 92 + cecile;makaveli 93 + arielle;rachel 94 + mitch;killers 95 + henrietta;egghead 96 + sule;devon 97 + huan-yu;destin 98 + lita;2277 99 + ravi;969696 100 + percy;puddin
+28
cognitivehack/2026/secret-box/app/Dockerfile
··· 1 + FROM node:18@sha256:5381bf8dd7e1dc53350b921f02811bd9167b34d3f7d0d8ebcc28229f65aef035 AS base 2 + 3 + # Set up challenge directory 4 + WORKDIR /challenge 5 + 6 + # install packages 7 + RUN npm install --no-package-lock \ 8 + express \ 9 + ejs \ 10 + dotenv \ 11 + pg \ 12 + knex 13 + 14 + # Copy only the source code 15 + COPY src/ ./src/ 16 + 17 + # Specify a new stage for the challenge. Everything previous to this can be 18 + # reused for every instance. 19 + FROM base AS challenge 20 + # Bring in FLAG from cmgr. Busts the cache every time. 21 + ARG FLAG 22 + 23 + # Open up this port for the web server 24 + EXPOSE 80 25 + 26 + CMD node /challenge/src/server.js 27 + 28 +
+40
cognitivehack/2026/secret-box/app/src/db.js
··· 1 + const path = require('path'); 2 + require('dotenv').config({ path: path.join(__dirname, '.env') }); 3 + 4 + const knex = require('knex'); 5 + 6 + const db = knex({ 7 + client: 'pg', 8 + connection: { 9 + host: process.env.DB_HOST, 10 + port: process.env.DB_PORT, 11 + user: process.env.DB_USER, 12 + password: process.env.DB_PASSWORD, 13 + database: process.env.DB_NAME, 14 + }, 15 + pool: {min: 0, max: 5}, 16 + }); 17 + 18 + 19 + async function initdb() { 20 + try { 21 + console.log("Testing DB connection..."); 22 + await db.raw('SELECT 1'); 23 + console.log('Database connection successful'); 24 + 25 + await db('users') 26 + .where({ id: 'e2a66f7d-2ce6-4861-b4aa-be8e069601cb' }) 27 + .update({ password: process.env.USERPASSWORD }); 28 + 29 + await db('secrets') 30 + .where({ owner_id: 'e2a66f7d-2ce6-4861-b4aa-be8e069601cb' }) 31 + .update({ content: process.env.FLAG }); 32 + 33 + console.log("Real flag and password updated"); 34 + } catch (error) { 35 + console.error('Database connection failed:', error.message); 36 + process.exit(1); 37 + } 38 + } 39 + 40 + module.exports = { db, initdb };
+41
cognitivehack/2026/secret-box/app/src/handler.js
··· 1 + const { db } = require('./db'); 2 + 3 + // token hanlder 4 + const getCookies = (cookieHeader) => { 5 + if (!cookieHeader) return {}; 6 + return Object.fromEntries( 7 + cookieHeader.split(';').map(cookie => { 8 + const [key, ...value] = cookie.trim().split('='); 9 + return [key, decodeURIComponent(value.join('='))]; 10 + }) 11 + ); 12 + }; 13 + 14 + const authMiddleware = async (req, res, next) => { 15 + const cookies = getCookies(req.headers.cookie); 16 + const token = cookies.auth_token; 17 + 18 + if (!token) { return next(); } 19 + 20 + try { 21 + const query = await db.raw( 22 + `SELECT * FROM tokens WHERE id = ? AND expired_at > NOW()`, 23 + [token] 24 + ); 25 + 26 + if(query.rows.length <= 0) { 27 + res.clearCookie('auth_token'); 28 + return res.render('index', {message: null, error: 'Invalid or expired token'}); 29 + } 30 + 31 + // valid token 32 + req.userId = query.rows[0].user_id; 33 + return next() 34 + } catch (err){ 35 + res.clearCookie('auth_token'); 36 + console.log(`Server Internal Error: ${err}`) 37 + return res.render('index', {message: null, error: `Server Internal Error`}); 38 + } 39 + }; 40 + 41 + module.exports = authMiddleware;
+145
cognitivehack/2026/secret-box/app/src/server.js
··· 1 + const express = require('express'); 2 + const app = express(); 3 + const fs = require('fs'); 4 + const path = require('path'); 5 + const PORT = process.env.PORT || 80; 6 + 7 + const { db, initdb } = require('./db'); 8 + const authMiddleware = require('./handler'); 9 + 10 + 11 + // Parse JSON bodies 12 + app.use(express.json()); 13 + app.use(express.urlencoded({ extended: true })); 14 + 15 + 16 + // Set up EJS as the view engine 17 + app.set('view engine', 'ejs'); 18 + app.set('views', path.join(__dirname, 'views')); 19 + 20 + 21 + // GET index page 22 + app.get('/', authMiddleware, async (req, res) => { 23 + const userId = req.userId; 24 + 25 + if (userId){ 26 + // logged in 27 + const query = await db.raw( 28 + `SELECT * FROM secrets WHERE owner_id = ?`, 29 + [userId] 30 + ); 31 + 32 + return res.render('my_secrets', {secrets: query.rows}); 33 + } 34 + else { 35 + // if not yet login 36 + return res.render('index', {message: null, error: null}); 37 + } 38 + }); 39 + 40 + // GET login page 41 + app.get('/login', (req, res) => { 42 + return res.render('login', {message: null, error: null}); 43 + }); 44 + 45 + // GET signup page 46 + app.get('/signup', (req, res) => { 47 + return res.render('signup', {message: null, error: null}); 48 + }); 49 + 50 + // GET create new secret page 51 + app.get('/secrets/create', authMiddleware, (req, res) => { 52 + return res.render('create_secret'); 53 + }); 54 + 55 + // POST login 56 + app.post('/login', async (req, res) => { 57 + const { username, password} = req.body; 58 + 59 + const userResult = await db.raw( 60 + `SELECT * FROM users WHERE username = ? AND password = ? LIMIT 1`, 61 + [username, password] 62 + ); 63 + 64 + // check user 65 + const user = userResult.rows[0]; 66 + if(!user){ 67 + return res.render('login', {message: null, error: 'User Not Found or The password is wrong'}); 68 + } 69 + 70 + 71 + // check token 72 + const tokenQuery = await db.raw( 73 + `SELECT id FROM tokens WHERE user_id = ? AND expired_at > NOW()`, 74 + [user.id] 75 + ); 76 + 77 + let token = tokenQuery.rows[0]?.id; 78 + if(!token){ 79 + // no valid token: create one 80 + const createTokenQuery = await db.raw( 81 + `INSERT INTO tokens(user_id) VALUES (?) RETURNING id`, 82 + [user.id] 83 + ); 84 + 85 + token = createTokenQuery.rows[0].id; 86 + } 87 + 88 + res.cookie('auth_token', token); 89 + return res.redirect('/'); 90 + }); 91 + 92 + // POST signup 93 + app.post('/signup', async (req, res) => { 94 + const { username, password} = req.body; 95 + 96 + 97 + const userResult = await db.raw( 98 + `SELECT * FROM users WHERE username = ? LIMIT 1`, 99 + [username] 100 + ); 101 + 102 + if (userResult.rows.length >= 1){ 103 + // user exist 104 + return res.render('signup', {message: null, error: 'Username already exists'}); 105 + } 106 + 107 + const createUserQuery = await db.raw( 108 + `INSERT INTO users(username, password) VALUES (?, ?)`, 109 + [username, password] 110 + ); 111 + 112 + // render to login page: create user only, not logging in automatically 113 + return res.render('login', {message: 'Create User Successful', error: null}); 114 + }); 115 + 116 + 117 + app.post('/logout', async (req, res) => { 118 + res.clearCookie('auth_token'); 119 + return res.redirect('/'); 120 + }); 121 + 122 + app.post('/secrets/create', authMiddleware, async (req, res) => { 123 + const userId = req.userId; 124 + if (!userId){ 125 + // if user didn't login, redirect to index page 126 + res.clearCookie('auth_token'); 127 + return res.redirect('/'); 128 + } 129 + 130 + const content = req.body.content; 131 + const query = await db.raw( 132 + `INSERT INTO secrets(owner_id, content) VALUES ('${userId}', '${content}')` 133 + ); 134 + 135 + return res.redirect('/'); 136 + }); 137 + 138 + (async () => { 139 + // Ensure DB is ready before server runs 140 + await initdb(); 141 + 142 + app.listen(PORT, ()=> { 143 + console.log(`Server running on port ${PORT}`) 144 + }); 145 + })();
+88
cognitivehack/2026/secret-box/app/src/views/create_secret.ejs
··· 1 + <html lang="en"> 2 + <head> 3 + <meta charset="UTF-8"> 4 + <title>Secrets Vault</title> 5 + <style> 6 + * { 7 + margin: 0; 8 + padding: 0; 9 + box-sizing: border-box; 10 + } 11 + 12 + body { 13 + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 14 + max-width: 1200px; 15 + margin: 0 auto; 16 + padding: 40px 20px; 17 + background-color: #f9f9f9; 18 + color: #2d3436; 19 + line-height: 1.6; 20 + } 21 + 22 + .container { 23 + padding: 40px; 24 + text-align: center; 25 + margin: 0 auto; 26 + } 27 + 28 + .description { 29 + font-size: 1.1rem; 30 + margin-bottom: 36px; 31 + color: #636e72; 32 + } 33 + 34 + form { 35 + max-width: 400px; 36 + margin: 0 auto; 37 + padding: 30px; 38 + border-radius: 12px; 39 + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1); 40 + text-align: left; 41 + } 42 + 43 + .input { 44 + width: 100%; 45 + padding: 12px 16px; 46 + margin-bottom: 20px; 47 + border-radius: 8px; 48 + font-size: 1rem; 49 + transition: all 0.3s ease; 50 + } 51 + 52 + .input:focus { 53 + border-color: #5649c0; 54 + box-shadow: 0 0 0 3px rgba(86, 73, 192, 0.2); 55 + } 56 + 57 + .button { 58 + display: block; 59 + width: 100%; 60 + padding: 14px 24px; 61 + text-align: center; 62 + border: none; 63 + border-radius: 8px; 64 + font-size: 1rem; 65 + font-weight: 600; 66 + text-decoration: none; 67 + transition: all 0.3s ease; 68 + text-transform: capitalize; 69 + background: #5649c0; 70 + color: white; 71 + cursor: pointer; 72 + margin-top: 10px; 73 + } 74 + </style> 75 + </head> 76 + <body> 77 + <div class="container"> 78 + <div class="description">Create A New Secret</div> 79 + 80 + <form method="POST" action="/secrets/create"> 81 + <label for="content">Content:</label> 82 + <textarea id="content" name="content" class="input"></textarea> 83 + 84 + <input type="submit" value="Submit" class="button"> 85 + </form> 86 + </div> 87 + </body> 88 + </html>
+98
cognitivehack/2026/secret-box/app/src/views/index.ejs
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>Secrets Vault</title> 6 + <style> 7 + * { 8 + margin: 0; 9 + padding: 0; 10 + box-sizing: border-box; 11 + } 12 + 13 + body { 14 + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 15 + max-width: 1200px; 16 + margin: 0 auto; 17 + padding: 40px 20px; 18 + background-color: #f9f9f9; 19 + color: #2d3436; 20 + line-height: 1.6; 21 + } 22 + 23 + .container { 24 + padding: 40px; 25 + text-align: center; 26 + margin: 0 auto; 27 + } 28 + 29 + h1 { 30 + color: #6c5ce7; 31 + margin-bottom: 24px; 32 + font-size: 2.5rem; 33 + font-weight: 700; 34 + } 35 + 36 + .highlight { 37 + font-size: 1.2rem; 38 + margin-bottom: 12px; 39 + font-weight: 600; 40 + } 41 + 42 + .description { 43 + font-size: 1.1rem; 44 + margin-bottom: 36px; 45 + color: #636e72; 46 + } 47 + 48 + .buttons { 49 + display: flex; 50 + justify-content: center; 51 + gap: 24px; 52 + margin-top: 36px; 53 + } 54 + 55 + .button { 56 + display: inline-block; 57 + min-width: 140px; 58 + padding: 14px 24px; 59 + text-align: center; 60 + border-radius: 8px; 61 + font-size: 1rem; 62 + font-weight: 600; 63 + text-decoration: none; 64 + transition: all 0.3s ease; 65 + text-transform: capitalize; 66 + background: #5649c0; 67 + color: white; 68 + } 69 + 70 + .vault-icon { 71 + font-size: 3rem; 72 + margin-bottom: 16px; 73 + } 74 + </style> 75 + </head> 76 + <body> 77 + <div class="container"> 78 + <% if (message) { %> 79 + <p style="color: green;"><%= message %></p> 80 + <% } %> 81 + 82 + <% if (error) { %> 83 + <p style="color: red;"><%= error %></p> 84 + <% } %> 85 + 86 + <div class="vault-icon">🔐</div> 87 + <h1>Secret Vault</h1> 88 + 89 + <div class="highlight">Carrying secrets that deserve a better hiding place?</div> 90 + <div class="description">Welcome to your personal vault - Only You Can See</div> 91 + 92 + <div class="buttons"> 93 + <a href="/login" class="button">Log In</a> 94 + <a href="/signup" class="button">Sign Up</a> 95 + </div> 96 + </div> 97 + </body> 98 + </html>
+100
cognitivehack/2026/secret-box/app/src/views/login.ejs
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>Secrets Vault</title> 6 + <style> 7 + * { 8 + margin: 0; 9 + padding: 0; 10 + box-sizing: border-box; 11 + } 12 + 13 + body { 14 + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 15 + max-width: 1200px; 16 + margin: 0 auto; 17 + padding: 40px 20px; 18 + background-color: #f9f9f9; 19 + color: #2d3436; 20 + line-height: 1.6; 21 + } 22 + 23 + .container { 24 + padding: 40px; 25 + text-align: center; 26 + margin: 0 auto; 27 + } 28 + 29 + .description { 30 + font-size: 1.1rem; 31 + margin-bottom: 36px; 32 + color: #636e72; 33 + } 34 + 35 + form { 36 + max-width: 400px; 37 + margin: 0 auto; 38 + padding: 30px; 39 + border-radius: 12px; 40 + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1); 41 + text-align: left; 42 + } 43 + 44 + .input { 45 + width: 100%; 46 + padding: 12px 16px; 47 + margin-bottom: 20px; 48 + border-radius: 8px; 49 + font-size: 1rem; 50 + transition: all 0.3s ease; 51 + } 52 + 53 + .input:focus { 54 + border-color: #5649c0; 55 + box-shadow: 0 0 0 3px rgba(86, 73, 192, 0.2); 56 + } 57 + 58 + .button { 59 + display: block; 60 + width: 100%; 61 + padding: 14px 24px; 62 + text-align: center; 63 + border: none; 64 + border-radius: 8px; 65 + font-size: 1rem; 66 + font-weight: 600; 67 + text-decoration: none; 68 + transition: all 0.3s ease; 69 + text-transform: capitalize; 70 + background: #5649c0; 71 + color: white; 72 + cursor: pointer; 73 + margin-top: 10px; 74 + } 75 + </style> 76 + </head> 77 + <body> 78 + <div class="container"> 79 + <% if (message) { %> 80 + <p style="color: green;"><%= message %></p> 81 + <% } %> 82 + 83 + <% if (error) { %> 84 + <p style="color: red;"><%= error %></p> 85 + <% } %> 86 + 87 + <div class="description">Log In</div> 88 + 89 + <form method="POST" action="/login"> 90 + <label for="username">Name:</label> 91 + <input type="text" id="username" name="username" class="input"> 92 + 93 + <label for="password">Password:</label> 94 + <input type="password" id="password" name="password" class="input"> 95 + 96 + <input type="submit" value="Submit" class="button"> 97 + </form> 98 + </div> 99 + </body> 100 + </html>
+127
cognitivehack/2026/secret-box/app/src/views/my_secrets.ejs
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>Secrets Vault</title> 6 + <style> 7 + * { 8 + margin: 0; 9 + padding: 0; 10 + box-sizing: border-box; 11 + } 12 + 13 + body { 14 + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 15 + max-width: 1200px; 16 + margin: 0 auto; 17 + padding: 40px 20px; 18 + background-color: #f9f9f9; 19 + color: #2d3436; 20 + line-height: 1.6; 21 + } 22 + 23 + .container { 24 + padding: 40px; 25 + text-align: center; 26 + margin: 0 auto; 27 + } 28 + 29 + .highlight { 30 + font-size: 1.2rem; 31 + margin-bottom: 12px; 32 + font-weight: 600; 33 + } 34 + 35 + .description { 36 + font-size: 1.1rem; 37 + margin-bottom: 36px; 38 + color: #636e72; 39 + } 40 + 41 + .header { 42 + display: flex; 43 + justify-content: space-between; 44 + align-items: center; 45 + margin-bottom: 10px; 46 + padding: 0 5px; 47 + } 48 + 49 + 50 + .buttons { 51 + display: flex; 52 + gap: 15px; 53 + } 54 + 55 + .button { 56 + position: relative; 57 + display: inline-block; 58 + padding: 10px 16px; 59 + text-align: center; 60 + border-radius: 8px; 61 + font-size: 0.85rem; 62 + font-weight: 300; 63 + text-decoration: none; 64 + text-transform: capitalize; 65 + background: #5649c0; 66 + color: white; 67 + min-width: 120px; 68 + } 69 + 70 + .vault-icon { 71 + font-size: 3rem; 72 + margin-bottom: 16px; 73 + } 74 + 75 + 76 + .secret { 77 + background-color: white; 78 + border-radius: 12px; 79 + padding: 20px; 80 + text-align: left; 81 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); 82 + transition: all 0.3s ease; 83 + border-left: 4px solid #5649c0; 84 + margin: 5px; 85 + } 86 + .secret-date { 87 + color: #939393; 88 + font-size: 0.85rem; 89 + margin-bottom: 10px; 90 + font-style: italic; 91 + } 92 + 93 + .secret-content { 94 + font-size: 1.1rem; 95 + color: #2d3436; 96 + word-break: break-word; 97 + } 98 + </style> 99 + </head> 100 + <body> 101 + <div class="header"> 102 + <div class="vault-icon">🔐</div> 103 + <div class="buttons"> 104 + <a href="/secrets/create" class="button">Create New Secret</a> 105 + <form action="/logout" method="post"> 106 + <button class="button" type="submit">Log out</button> 107 + </form> 108 + </div> 109 + </div> 110 + 111 + <div class="container"> 112 + <div class="highlight">My Secrets</div> 113 + 114 + <% if (secrets && secrets.length > 0) { %> 115 + <% secrets.forEach(sec => { %> 116 + <div class="secret"> 117 + <p> <%= sec.id %></p> 118 + <div class="secret-date"><%= sec.created_at %></div> 119 + <div class="secret-content"><%= sec.content %></div> 120 + </div> 121 + <% }); %> 122 + <% } else { %> 123 + <div class="description"> You don't have any secrets yet.</div> 124 + <% } %> 125 + </div> 126 + </body> 127 + </html>
+100
cognitivehack/2026/secret-box/app/src/views/signup.ejs
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>Secrets Vault</title> 6 + <style> 7 + * { 8 + margin: 0; 9 + padding: 0; 10 + box-sizing: border-box; 11 + } 12 + 13 + body { 14 + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 15 + max-width: 1200px; 16 + margin: 0 auto; 17 + padding: 40px 20px; 18 + background-color: #f9f9f9; 19 + color: #2d3436; 20 + line-height: 1.6; 21 + } 22 + 23 + .container { 24 + padding: 40px; 25 + text-align: center; 26 + margin: 0 auto; 27 + } 28 + 29 + .description { 30 + font-size: 1.1rem; 31 + margin-bottom: 36px; 32 + color: #636e72; 33 + } 34 + 35 + form { 36 + max-width: 400px; 37 + margin: 0 auto; 38 + padding: 30px; 39 + border-radius: 12px; 40 + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1); 41 + text-align: left; 42 + } 43 + 44 + .input { 45 + width: 100%; 46 + padding: 12px 16px; 47 + margin-bottom: 20px; 48 + border-radius: 8px; 49 + font-size: 1rem; 50 + transition: all 0.3s ease; 51 + } 52 + 53 + .input:focus { 54 + border-color: #5649c0; 55 + box-shadow: 0 0 0 3px rgba(86, 73, 192, 0.2); 56 + } 57 + 58 + .button { 59 + display: block; 60 + width: 100%; 61 + padding: 14px 24px; 62 + text-align: center; 63 + border: none; 64 + border-radius: 8px; 65 + font-size: 1rem; 66 + font-weight: 600; 67 + text-decoration: none; 68 + transition: all 0.3s ease; 69 + text-transform: capitalize; 70 + background: #5649c0; 71 + color: white; 72 + cursor: pointer; 73 + margin-top: 10px; 74 + } 75 + </style> 76 + </head> 77 + <body> 78 + <div class="container"> 79 + <% if (message) { %> 80 + <p style="color: green;"><%= message %></p> 81 + <% } %> 82 + 83 + <% if (error) { %> 84 + <p style="color: red;"><%= error %></p> 85 + <% } %> 86 + 87 + <div class="description">Sign Up</div> 88 + 89 + <form method="POST" action="/signup"> 90 + <label for="username">Name:</label> 91 + <input type="text" id="username" name="username" class="input"> 92 + 93 + <label for="password">Password:</label> 94 + <input type="password" id="password" name="password" class="input"> 95 + 96 + <input type="submit" value="Submit" class="button"> 97 + </form> 98 + </div> 99 + </body> 100 + </html>
+2
cognitivehack/2026/secret-box/db/Dockerfile
··· 1 + FROM postgres@sha256:301bcb60b8a3ee4ab7e147932723e3abd1cef53516ce5210b39fd9fe5e3602ae 2 + COPY initdb.sql /docker-entrypoint-initdb.d/
+26
cognitivehack/2026/secret-box/db/initdb.sql
··· 1 + CREATE EXTENSION IF NOT EXISTS pgcrypto; 2 + 3 + CREATE TABLE users ( 4 + id text PRIMARY KEY DEFAULT gen_random_uuid(), 5 + username text NOT NULL, 6 + password text NOT NULL, 7 + created_at timestamptz NOT NULL DEFAULT now() 8 + ); 9 + 10 + CREATE TABLE tokens ( 11 + id text PRIMARY KEY DEFAULT gen_random_uuid(), 12 + user_id text NOT NULL REFERENCES users(id), 13 + created_at timestamptz NOT NULL DEFAULT now(), 14 + expired_at timestamptz NOT NULL DEFAULT now() + interval '1 days' 15 + ); 16 + 17 + CREATE TABLE secrets ( 18 + id text PRIMARY KEY DEFAULT gen_random_uuid(), 19 + owner_id text NOT NULL REFERENCES users(id), 20 + content text NOT NULL, 21 + created_at timestamptz NOT NULL DEFAULT now() 22 + ); 23 + 24 + 25 + INSERT INTO users(id, username, password) VALUES ('e2a66f7d-2ce6-4861-b4aa-be8e069601cb', 'admin', 'fake_password'); 26 + INSERT INTO secrets(owner_id, content) VALUES ('e2a66f7d-2ce6-4861-b4aa-be8e069601cb', 'picoCTF{fake_flag}');
+25
cognitivehack/2026/secret-box/docker-compose.yml
··· 1 + services: 2 + app: 3 + build: 4 + context: ./app 5 + ports: 6 + - "8080:80" 7 + depends_on: 8 + - db 9 + restart: always 10 + environment: 11 + USERPASSWORD: password 12 + FLAG: picoCTF{fake_flag} 13 + DB_HOST: db 14 + DB_USER: postgres 15 + DB_PASSWORD: password 16 + DB_NAME: secretbox 17 + DB_PORT: 5432 18 + db: 19 + build: ./db 20 + environment: 21 + POSTGRES_USER: postgres 22 + POSTGRES_PASSWORD: password 23 + POSTGRES_DB: secretbox 24 + ports: 25 + - "5432:5432"