T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- oasis/server.py:122
- Finding
- Administrator Token Exfiltration Through an Attacker-Controlled Callback<![CDATA[ ## Vulnerability Details **File Location**: `oasis/server.py:122-143`, `oasis/server.py:153-179` **Vulnerability Type**: Server-Side Request Forgery and credential disclosure **Risk Level**: Critical ### Vulnerable Code ```python cb_url = getattr(engine, "callback_url", None) if cb_url: conclusion = forum.conclusion if forum else "(无结论)" status = forum.status if forum else "error" cb_session = getattr(engine, "callback_session_id", "default") or "default" user_id = forum.user_id if forum else "anonymous" internal_token = os.getenv("INTERNAL_TOKEN", "") text = ( f"[OASIS 子任务完成通知]\n" f"Topic ID: {topic_id}\n" f"状态: {status}\n" f"主题: {forum.question if forum else '?'}\n\n" f"📋 结论:\n{conclusion}" ) try: async with httpx.AsyncClient(timeout=10.0) as client: await client.post( cb_url, json={"user_id": user_id, "text": text, "session_id": cb_session}, headers={"X-Internal-Token": internal_token}, ) ``` ```python @app.post("/topics", response_model=dict) async def create_topic(req: CreateTopicRequest): """Create a new discussion topic. Returns topic_id for tracking.""" topic_id = str(uuid.uuid4())[:8] forum = DiscussionForum( topic_id=topic_id, question=req.question, user_id=req.user_id, max_rounds=req.max_rounds, ) discussions[topic_id] = forum forum.save() engine = DiscussionEngine( forum=forum, schedule_yaml=req.schedule_yaml, schedule_file=req.schedule_file, bot_enabled_tools=req.bot_enabled_tools, bot_timeout=req.bot_timeout, user_id=req.user_id, early_stop=req.early_stop, discussion=req.discussion, ) engine.callback_url = req.callback_url engine.callback_session_id = req.callback_session_id ``` ### Technical Analysis The unauthenticated `/topics` endpoint accepts ...[truncated 1874 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Require authentication on `/topics` and every other OASIS route. - Derive `user_id` from the authenticated principal instead of accepting it as an authority-bearing request field. - Do not send `INTERNAL_TOKEN` to a URL supplied by a client. - Replace arbitrary callbacks with preconfigured callback identifiers mapped to exact trusted destinations. - If callbacks must be configurable, enforce an exact scheme, hostname, port, and path allowlist. - Resolve hostnames and reject loopback, link-local, private, multicast, metadata, and reserved addresses unless explicitly required. - Disable HTTP redirects or revalidate every redirect destination. - Use narrowly scoped, short-lived, audience-bound callback credentials instead of the global internal token. - Rotate `INTERNAL_TOKEN` after remediation because existing deployments may already have exposed it. ]]>
