CVE-2026-30815
An os command injection vulnerability exists in the Openvpn configuration restore script_security functionality of Tp-Link Archer AX53 v1.0 1.3.1 Build 20241120 rel.54901(5553). A specially crafted configuration value can lead to arbitrary command execution. An attacker can upload a malicious file to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Tp-Link Archer AX53 v1.0 1.3.1 Build 20241120 rel.54901(5553)
Archer AX53 v1.0 - https://www.tp-link.com/my/support/download/archer-ax53/
9.1 - CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE-77 - Improper Neutralization of Special Elements used in a Command (‘Command Injection’)
The TP-Link Archer AX53 AX3000 Dual Band Gigabit Wi-Fi 6 Router is currently among the most popular routers sold online, and boasts impressive gigabit speeds for the price. This router also features remote cloud access via the TP-Link HomeShield application and smart home functionality.
A long existing feature of TP-Link routers and most routers in general is the ability to backup the router configuration into a file which can then be reuploaded to the device to restore the router to a particular operational state. On the TP-Link AX3000, this functionality is accessed via the Web GUI by navigating to the ‘Advanced > System’ menu. Upon hitting the ‘Back Up’ button, the browser will download a file called ArcherAX53v120241120131n.bin. As described in a previously disclosed vulnerability on an older model of TP-Link routers https://github.com/aaronsvk/CVE-2022-30075 , this binary blob must first be decrypted with a static AES key and IV, then unpacked as a GZIP archive. After this, one must unpack the resulting tar file to get two resulting files: ori-backup-user-config.bin and ori-backup-certificate.bin with actual contents. We then unpack these two files in the same exact manner, first decrypting with the same AES key and IV and unpacking decrypted binary as a gzip file. For ori-backup-user-config.bin we end up with a config.xml file that is human readable. This configuration xml can be editied and then repacked with a reverse of the process to manually change the configuration of the device.
Among the different configuration options, there exist some that are parsed by /etc/init.d/ startup scripts, and for the purposes of this writeup we only care to look at /etc/init.d/openvpn:
start_instance() { local s=”$1”
section_enabled "$s" || return 1
[ ! -d "/var/run" ] && mkdir -p "/var/run"
[ ! -d "/var/etc" ] && mkdir -p "/var/etc"
[ -f "/var/etc/openvpn-$s.conf" ] && rm "/var/etc/openvpn-$s.conf"
// [...]
# append flags
append_bools "$s" \
auth_nocache auth_retry auth_user_pass_optional bind ccd_exclusive client client_cert_not_required \
client_to_client comp_noadapt disable \
disable_occ down_pre duplicate_cn fast_io float http_proxy_retry \
ifconfig_noexec ifconfig_nowarn ifconfig_pool_linear management_forget_disconnect management_hold \
management_query_passwords management_signal mktun mlock mtu_test multihome mute_replay_warnings \
nobind no_iv no_name_remapping no_replay opt_verify passtos persist_key persist_local_ip \
persist_remote_ip persist_tun ping_timer_rem pull push_reset \
remote_random rmtun route_noexec route_nopull single_session socks_proxy_retry \
suppress_timestamps tcp_nodelay test_crypto tls_client tls_exit tls_server \
tun_ipv6 up_delay up_restart username_as_common_name
# append params
append_params "$s" \
cd askpass auth auth_user_pass auth_user_pass_verify bcast_buffers ca cert \
chroot cipher client_config_dir client_connect client_disconnect comp_lzo connect_freq \
connect_retry connect_timeout connect_retry_max crl_verify dev dev_node dev_type dh \
echo engine explicit_exit_notify fragment group hand_window hash_size \
http_proxy http_proxy_option http_proxy_timeout ifconfig ifconfig_pool \
ifconfig_pool_persist ifconfig_push inactive ipchange iroute keepalive \
key key_method keysize learn_address link_mtu lladdr local log log_append \
lport management management_log_cache max_clients \
max_routes_per_client mode mssfix mtu_disc mute nice ns_cert_type ping \
ping_exit ping_restart pkcs12 plugin port port_share prng proto rcvbuf \
redirect_gateway remap_usr1 remote remote_cert_eku remote_cert_ku remote_cert_tls \
reneg_bytes reneg_pkts reneg_sec \
replay_persist replay_window resolv_retry route route_delay route_gateway \
route_metric route_up rport script_security secret server server_bridge setenv shaper sndbuf \ // [1]
socks_proxy status status_version syslog tcp_queue_limit tls_auth \
tls_cipher tls_remote tls_timeout tls_verify tmp_dir topology tran_window \
tun_mtu tun_mtu_extra txqueuelen user verb down push up
// [...]
service_start /usr/sbin/openvpn --syslog "openvpn($s)" --writepid "$SERVICE_PID_FILE" --config "/var/etc/openvpn-$s.conf"
This init script parses our uploaded configuration, searching for all of the openvpn configuration options in the above lists. As such, we can cause the openvpn server to run with almost any configuration. For those with knowledge of openvpn server configurations, this is in fact very dangerous as there are a few openvpn options listed above that directly allow commands to be run at certain times in the server’s lifetime. There is a safeguard built into openvpn to prevent arbitrary scripts from being run, the script_security option. Normally it defaults to ‘1’, which prevents arbitrary scripts from being run, limiting openvpn to only being able to run built-in binaries, but as seen above in the line at [1], we can see that we can change this value. Setting it to ‘2’ lets us run arbitrary scripts, which is useful for certain other openvpn config options that don’t let us pass arbtrary arguments, for instance the up option. Thus, if we include the following xml lines within our config.xml, we can run an arbitrary script on the deivce:
<script_security>2</script_security>
<up>"/usr/sbin/telnetd"</up>
While running telnetd is rather useless for our purposes, and there might be built in binaries that allow us to gain shell access, another vector is presented to us, the fore mentioned ori-backup-certificate.bin within our backup binary. If we decrypt this file as mentioned above and then unpack this binary as a gzip file, we’re left with a tar archive that we can unpack to see the following files:
-rw------- 1261 2025-08-05 13:00 ./ca.crt
-rw-r--r-- 144 2025-08-05 13:00 ./client.conf
-rw------- 3707 2025-08-05 13:00 ./client.crt
-rw------- 916 2025-08-05 13:00 ./client.key
-rw------- 245 2025-08-05 13:00 ./dh1024.pem
-rw------- 3725 2025-08-05 13:00 ./server.crt
-rw------- 916 2025-08-05 13:00 ./server.key
Those with VPN knowledge will probably recognize this as common files utilized by openvpn, and we can find this same exact directory layout inside of /etc/openvpn/ on the device. It thus follows that we can unpack this tarball, add a script file of our choosing, repack the ori-backup-certificate.bin with this script file included. This tarball overwrites the contents of /etc/openvpn, and then we can run our custom script via the openvpn <up> parameter, resulting in arbitrary OS command execution.
Vendor advisory: https://www.tp-link.com/us/support/faq/5055/
2026-01-12 - Vendor Disclosure
2026-04-08 - Vendor Patch Release
2026-05-07 - Public Release
Discovered by Lilith >_> of Cisco Talos.