T09 · Insecure Skill Coding Practices
- Location
scripts/oauth-remote.sh:71- Finding
Authorization Code Injection Enables Arbitrary Local Code Execution
- Content
View full analysis
}" python3 -c " import json, urllib.request, urllib.parse, subprocess, os, sys with open('${STATE_FILE}') as f: oauth = json.load(f) data = urllib.parse.urlencode({ 'grant_type': 'authorization_code', 'code': '${CODE}', 'redirect_uri': oauth['redirect_uri'], 'client_id': oauth['client_id'], 'code_verifier': oauth['verifier'], }).encode() req = urllib.request.Request( '${MCP_URL}/oauth/token', data=data, headers={'Content-Type': 'application/x-www-form-urlencoded'}, ) resp = json.loads(urllib.request.urlopen(req).read()) token = resp.get('access_token', '') if not token: print('ERROR: No access token received', file=sys.stderr) sys.exit(1) subprocess.run(['mcporter', 'config', 'remove', 'xpoz'], capture_output=True) result = subprocess.run( ['mcporter', 'config', 'add', 'xpoz', '${MCP_URL}/mcp', '--header', f'Authorization=Bearer {token}'], capture_output=True, text=True ) " ``` The corresponding instruction directs the Agent to pass user-controlled content to this command: ```bash bash "$(dirname "$0")/../xpoz-setup/scripts/oauth-remote.sh" exchange AUTH_CODE_HERE ``` ### Technical Analysis The OAuth authorization code originates from the user's chat response and is assigned to the shell variable `CODE`. Although shell argument handling itself is quoted, the value is subsequently inserted directly into the source text passed to `python3 -c`. The interpolation occurs inside a Python string literal: ```python 'code': '${CODE}', ``` An authorization-code value containing Python quote delimiters and additional expressions can terminate the intended string and modify the generated Python program. For ex ...[truncated 1840 chars]- Remediation
View remediation
}" python3 - "$STATE_FILE" "$MCP_URL" "$CODE" <<'PY' import json import sys import urllib.parse import urllib.request state_file = sys.argv[1] mcp_url = sys.argv[2] code = sys.argv[3] with open(state_file, encoding="utf-8") as f: oauth = json.load(f) data = urllib.parse.urlencode({ "grant_type": "authorization_code", "code": code, "redirect_uri": oauth["redirect_uri"], "client_id": oauth["client_id"], "code_verifier": oauth["verifier"], }).encode() PY ``` Additional hardening should include: 1. Move the Python logic into a standalone, reviewed script instead of constructing it with `python3 -c`. 2. Treat all chat-provided OAuth values as untrusted input. 3. Parse callback URLs with a URL parser instead of performing textual extraction. 4. Reject malformed codes according to the authorization server's documented syntax, while retaining argument separation as the primary defense. 5. Add regression tests containing quotes, newlines, Python expressions, shell metacharacters, and Unicode input. ]]>
