TALOS-2026-2431
CVE-2026-9869
A stack-based buffer overflow vulnerability exists in the boa formTR069Config functionality of X2000R (version(s): TOTOLINK-X2000R-Gh-V1.0.0-B20250421.1327). A specially crafted HTTP request can lead to arbitrary code execution. An attacker can make an authenticated HTTP request 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.
X2000R (version(s): TOTOLINK-X2000R-Gh-V1.0.0-B20250421.1327)
X2000R - https://www.totolink.net/home/menu/newstpl/menu_newstpl/products/id/242.html
9.1 - CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE-121 - Stack-based Buffer Overflow
The Totolink X2000R is a popular budget gigabit home router primarly found in eastern markets. Supporting IPV6, WPA3, mesh capabilities among other features, the Totolink X2000R is a solid choice for the price point.
The Totolink X2000R mainly relies on the standalone boa binary for all web administration and subsequent configuration. While normally a security risk in itself due to the age, it appears that the version running on the X2000R has been patched. Regardless, for the current set of vulnerabilities we only pay attention to the Totolink-specific issues, so we start from the point where authentication has already occurred via the /boafrm/formLogin webpage, and then we can successfully interact with another /boafrm/form* page, each of which have a corresponding callback. For the current vulnerability we examine the /boafrm/formTR069Config page, which leads us into the formTR069Config() function:
0045bb44 size_t formTR069Config(struct client* arg1)
0045bbf4 apmib_get(MIB_TR069_URL, out: &var_5a4)
0045bc1c char* fmt
0045bc1c int32_t $a2_7
0045bc1c char username[0x64]
0045bc1c char* $a1_3
0045bc1c
0045bc1c if (apmib_set(MIB_TR069_URL, get_from_postbody(cli: arg1, "url", &cgi_var_pref)) != 0) // [1]
0045bc3c char var_6a8[0x104]
0045bc3c apmib_get(MIB_TR069_URL, out: &var_6a8)
0045bc5c int32_t $s4_1 = 0 u< strcmp(str1: &var_5a4, str2: &var_6a8) ? 1 : 0
0045bc58 apmib_get(MIB_TR069_PASSWORD, out: &var_78)
0045bc6c int32_t $s1_1 = $s4_1
0045bc68 apmib_get(MIB_TR069_USERNAME, out: &var_b8)
0045bc74 apmib_get(MIB_CON_REQ_NAME, out: &var_178)
0045bc80 apmib_get(MIB_CON_REQ_PW, out: &var_1b8)
0045bc94 char* tr069_username = get_from_postbody(cli: arg1, "username", &cgi_var_pref)
The first parameter we need to make sure we have is the url HTTP POST parameter [1], it doesn’t particularly matter what the url is, it just needs to be set. The function then pulls various configuration from flash via the calls to apmib_get which will be used later. Continuing to the parameter we most care about:
0045bd9c char* password = get_from_postbody(cli: arg1, "password", &cgi_var_pref) // [2]
0045bda8 int32_t $v0_8 = check_for_spacews(password) // [3]
0045bdb0 int32_t ascicheck
0045bdb0
0045bdb0 if ($v0_8 == 0)
0045bdc4 ascicheck = make_sure_lt_0x7f_ascii(password) // [4]
0045bdc4
0045bdcc if ($v0_8 != 0 || ascicheck == 0)
0045bdc0 $a1_9 = "Set Password error!"
0045bdbc goto label_45bcbc
0045bdbc
0045bde0 if (apmib_set(MIB_TR069_PASSWORD, password) == 0)
0045bdf0 $a1_3 = "Set Password error!"
0045bdec goto label_45bc2c
At [2], the program reads in our password, and then at [3] and [4], checks to see if the input has any spaces or ASCII chars over hex value 0x7f. Assuming we pass both of these checks, we then set the MIB_TR069_PASSWORD flash value to our input. Soon after these lines of code we can quickly see an issue:
0045c3e8 apmib_get(MIB_TR069_PASSWORD, out: &var_f8) // [5]
0045c3f4 // max len 0x64
0045c3f4 apmib_get(MIB_TR069_USERNAME, out: &var_138)
For some reason, after writing our password HTTP POST value into flash, the program then decides to read it from flash onto the stack at [5]. While it’s worth noting that each specific MIB flash parameter has its own max length, the MIB_TR069_PASSWORD length is greater than 0xF8, which let’s us utilize this apmib_get to get a stack-based buffer overflow, resulting in code execution.
Breakpoint 3, 0x0045c3e8 in ?? ()
[x.x]> !ls
***********************************************************************************
***********************************************************************************
v0 : 0x00000000 | s3[X] : 0x004780c0
v1 : 0x00000000 | s4 : 0x00000001
a0 : 0x00000001 | s5 : 0x00000001
a1[S] : 0x7fb1fa88 | s6[H] : 0x006fefbc
a2[L] : 0x7792a2d0 | s7 : 0x0000000f
a3 : 0x00000001 | t8[X] : 0x0048c2ec
t0[H] : 0x004afce0 | t9[L] : 0x77dfb8bc
t1 : 0x41414141 | k0 : 0x00000000
t2 : 0x41414141 | k1 : 0x00000000
t3 : 0x41414141 | gp[L] : 0x77e17020
t4 : 0x41414141 | sp[S] : 0x7fb1f4c0
t5 : 0x41414141 | ra[X] : 0x0045c364
t6 : 0x41414141 | status : 0x0000f413
t7[H] : 0x00490000 | badvaddr[H] : 0x004980f0
s0[H] : 0x006fe6e0 | pc[X] : 0x0045c3e8
s1 : 0x00000001 | fcsr : 0x00000000
s2 : 0x00480000 | restart : 0x00000000
***********************************************************************************
0x45c3dc: jal 0x47b8d0
0x45c3e0: sw v0,804(sp)
0x45c3e4: addiu a1,sp,1480
=> 0x45c3e8: jal 0x47b7d0
0x45c3ec: li a0,704
0x45c3f0: addiu a1,sp,1416
0x45c3f4: jal 0x47b7d0
0x45c3f8: li a0,703
***********************************************************************************
#0 0x0045c3e8 in (0x400000 0x47c000 0x7c000 0x0 r-xp /bin/boa)(+0x5c3e8)
***********************************************************************************
-----------------------------------------------------------------------------
[>_>]>
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
[^_^] SIGSEGV
***********************************************************************************
***********************************************************************************
v0 : 0x00000046 | s3 : 0x41414141
v1[H] : 0x00490000 | s4 : 0x41414141
a0[H] : 0x004bdc79 | s5 : 0x41414141
a1[S] : 0x7fb1ece6 | s6 : 0x41414141
a2 : 0x00000001 | s7 : 0x0000000f
a3[S] : 0x7fb1ece6 | t8[X] : 0x0048c074
t0 : 0x0000003e | t9[L] : 0x778e26d0
t1 : 0x6c6d7468 | k0 : 0x00085002
t2 : 0x00000008 | k1 : 0x00000000
t3 : 0x00000400 | gp[L] : 0x77932400
t4 : 0x00000800 | sp[S] : 0x7fb1fb80
t5 : 0x00000807 | ra : 0x41414141
t6[S] : 0x7fb1ea28 | status : 0x0000f413
t7[H] : 0x00490000 | badvaddr : 0x41414140
s0 : 0x41414141 | pc : 0x41414141
s1 : 0x41414141 | fcsr : 0x00000000
s2 : 0x41414141 | restart : 0x00000000
***********************************************************************************
Invalid $pc @ 0x41414141
#0 0x41414141 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
***********************************************************************************
2026-06-16 - vulndev@cisco.com - Initial Vendor Notification
2026-06-23 - vulndiscovery@external.cisco.com - Follow-up sent
2026-06-25 - fae_hardware@zioncom.net - Vendor Acknowledged
2026-06-25 - vulndiscovery@external.cisco.com - Resent all notifications
2026-07-16 - vulndiscovery@external.cisco.com - Follow-up sent
2026-09-15 - vulndiscovery@external.cisco.com - Follow-up sent
Lilith Wyatt of Cisco Talos