Talos Vulnerability Report

TALOS-2025-2326

GeoVision LPC2011/LPC2211 DdnsSetting.cgi OS command injection vulnerability

June 15, 2026
CVE Number

CVE-2026-42364

Summary

A OS command injection vulnerability exists in the DdnsSetting.cgi functionality of LPC2011/LPC2211 (version(s): 1.10). A specially crafted DDNS configuration can lead to arbitrary command execution. An attacker can modify a configuration value to trigger this vulnerability.

Confirmed Vulnerable Versions

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

LPC2011/LPC2211 (version(s): 1.10)

Product URLs

LPC2011/LPC2211 - https://www.geovision.com.tw/product/GV-LPC2011

CVSSv3 Score

9.9 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

CWE

CWE-77 - Improper Neutralization of Special Elements used in a Command (‘Command Injection’)

Details

The DdnsSetting.cgi endpoint is vulnerable to a command injection. The main purpose of this endpoint is to modify the DDNS settings that are used to generate the configuration file of a service called ez-ipupdate (see manpage for more details).

The data in the various fields (for example szHostname) is loaded without sanitization; it is then possible to insert line breaks and thus create new configuration entries that were not meant to be changed. In particular, a malicious execute configuration entry can be added that will trigger the execution of the provided command upon successful update of a ddns server. Another entry can be added as well to redirect the ddns traffic to an attacker controlled server, which will ensure the rogue update is always successful. This can lead to code execution as root on the device.

To confirm the issue, we can follow the flow of data from the web endpoint to the final configuration file to confirm no sanitization takes place. First, we can see the data passed to DdnsSetting.cgi is not sanitized and values are directly stored into the settings object:

int __fastcall main(int argc, const char **argv, const char **envp)
{
   /* */

  settings = (geo_settings *)geo_setting_create(0);
  if ( settings )
  {
    v5 = getcgivars();
    v6 = (const char **)v5;
    settings->ddns_enabled = 0;
    while ( 1 )
    {
      v7 = *v6;
      if ( !*v6 )
        break;
      if ( !strcmp(*v6, "bEnable") )
      {
        settings->ddns_enabled = strtol(v6[1], 0, 10);
      }
      else if ( !strcmp(v7, "byVendor") )
      {
        settings->ddns_isDyndns = strtol(v6[1], 0, 10);
      }
      else if ( !strcmp(v7, "szHostName") )
      {
        snprintf(settings->ddns_hostname, 0x80u, "%s", v6[1]);
      }
      else if ( !strcmp(v7, "username") )
      {
        snprintf(s, 0x80u, v6[1]);
      }
      else if ( !strcmp(v7, "password") )
      {
        snprintf(settings->ddns_password, 0x20u, "%s", v6[1]);
      }
      v6 += 2;
    }

    /* */ 

    if ( SIiUTIL_WebWriteFlshNew(settings->streaming_Cmd_Port, 256, settings) != -268435456 )
    {
      strcpy(v17, "Notify Fail\n");
      v9 = syscall(224);
      snprintf(v18, 0x220u, "(%d) %s[%d]: %s", v9, "main", 83, v17);
      syslog(6, "%s", v18);
    }
    /* */
}

Then, the ipcamd service is responsible for starting the DDNS service and update its configuration based on the global settings that were set previously:

int __fastcall DDns_init_and_start(geo_settings *a1) // from ipcamd
{
  if ( !a1 )
    return -1;
  SIiDdns_Init(
    &g_ddns_obj,
    a1->ddns_username,
    a1->ddns_password,
    a1->ddns_hostname,
    a1->ddns_enabled,
    a1->ddns_isDyndns,
    a1->field_B8DC,
    a1->bIsWireless);
  SIiDdns_Start((int)&g_ddns_obj);
  return 0;
}

We can see that the SIiDdns_Init function does not sanitize inputs:

   int __fastcall SIiDdns_Init(
        ddns_obj *ddns_obj,
        __int8 *username,
        __int8 *password,
        __int8 *hostname,
        int ddns_enabled,
        int is_dyndns,
        int a7,
        int is_wireless)
{


  v26 = -268435456;
  v25 = 0;
  memset(v24, 0, sizeof(v24));
  memset(ddns_obj, 0, sizeof(ddns_obj));
  snprintf(ddns_obj->username, 0x100u, "%s", username);
  snprintf(ddns_obj->password, 0x100u, "%s", password);
  snprintf(ddns_obj->hostname, 0x100u, "%s", hostname);
  
     /* */
 }

And finally, the s_SIiDdns_UpdateCfg generates the configuration file without sanitization either:

   int __fastcall s_SIiDdns_UpdateCfg(ddns_obj *ddns_obj)
{
/**/
strcat(config_buffer, "#service-type=dyndns-static\n");
v33 = strlen(config_buffer);
sprintf(&config_buffer[v33], "user=%s:%s\n", ddns_obj->username, ddns_obj->password);
v34 = strlen(config_buffer);
sprintf(&config_buffer[v34], "host=%s\n", ddns_obj->hostname);
v35 = strlen(config_buffer);

   /**/
   
config_buffer_size = strlen(config_buffer);
fwrite(config_buffer, 1u, config_buffer_size, temp_config_file);
fclose(temp_config_file);
sprintf(v44, "cp %s %s", temp_file_name, "/var/config/ddns.conf");
res = GV_CMD_RUN_NO_MSG(v44);
if ( res )
  syslog(6, "%s:command=%s, dwResult(%d)\n", "s_SIiDdns_UpdateCfg", v44, res);
unlink(temp_file_name);
return 0xF0000000;
  /**/
}

Finally we can confirm from the ez-ipupdate documentation the existence of an execute parameter:

-e, --execute <command> shell command to execute after a successful update

Which can be included as a configuration variable:

` -c, –config-file configuration file, almost all arguments can be given with: [=] to see a list of possible config commands try`.

All this together means that by inserting linebreaks in various fields of the DDNS configuration, it is possible to inject new variables inside the ddns.conf file, including an execute parameter that will trigger code execution as root upon successfully DDNS sync. This issue is somewhat mitigated as the DdnsSetting.cgi is only available to the admin user, and thus would require a malicious user to first find a way to retrieve the admin credentials or bypass the login page. See TALOS-2025-2322 for more details in how this could be achieved.

Timeline

2026-02-17 - Initial Vendor Contact
2026-02-24 - Vendor Disclosure
2026-04-14 - Vendor Patch Release
2026-06-15 - Public Release

Credit

Philippe Laulheret of Cisco Talos