Talos Vulnerability Report

TALOS-2026-2390

Totolink X2000R boa formMapDelDevice macstr stack-based buffer overflow vulnerability

October 20, 2026

CVE Number

CVE-2026-41223

Summary

A stack-based buffer overflow vulnerability exists in the boa formMapDelDevice macstr 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.

Confirmed Vulnerable Versions

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)

Product URLs

X2000R - https://www.totolink.net/home/menu/newstpl/menu_newstpl/products/id/242.html

CVSSv3 Score

9.1 - CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

CWE

CWE-121 - Stack-based Buffer Overflow

Details

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/formMapDelDevice page, which leads us into the formMapDelDevice() function:

0045de48    int32_t formMapDelDevice(struct client* arg1)

0045de78        char* macstr = get_from_postbody(cli: arg1, "macstr", &cgi_var_pref)       // [1]
0045de90        char* $v0 = get_from_postbody(cli: arg1, "bandstr", &cgi_var_pref)
0045dea8        char* clientoff = get_from_postbody(cli: arg1, "clientoff", &cgi_var_pref) // [2]
0045dec0        char* hostnameAdd = get_from_postbody(cli: arg1, "hostnameAdd", &cgi_var_pref)
0045ded0        int32_t hnameadd_val
0045ded0        
0045ded0        if (sx.d(*clientoff) == 0)
0045ded4            hnameadd_val = sx.d(*hostnameAdd)
0045ded0        else                        // [3]
0045dedc            char* var_d0_1 = $v0
0045deec            p2_vfork_execve("/bin/map_del_device", 2, 0, macstr)  // [4]
0045def4            hnameadd_val = sx.d(*hostnameAdd)
0045def4        

Without going too far in, we examine the potential case in which we’ve provided HTTP POST parameters macstr[1] and clientoff[2], such that we hit the branch at [3] and enter into the /bin/map_del_device with our macstr argument passed in via execve // [4]. Looking at the /bin/map_del_device binary’s main() function we see the following:

004005e0    int32_t main(int32_t argc, char** argv, char** envp)

00400608        int32_t var_44 = 0
00400604        void str_3
00400604        memset(str: &str_3, c: 0, n: 0x2f)
0040061c        int32_t var_78 = 0
00400618        void str_2
00400618        memset(str: &str_2, c: 0, n: 0x2f)
00400630        int32_t string_1 = 0
0040062c        void str_1
0040062c        memset(str: &str_1, c: 0, n: 0x60)
00400644        int32_t string = 0
00400640        void str
00400640        memset(&str, c: 0, n: 0x60)
00400650        uint32_t $v0 = zx.d(var_78.b)
00400650        
0040064c        if (argc s>= 2)                          // [5]
00400658            strcpy(dest: &var_44, src: argv[1])  // [6]
00400668            $v0 = zx.d(var_78.b)
00400668            
00400664            if (argc != 2)
00400670                strcpy(dest: &var_78, src: argv[2]) 
00400678                $v0 = zx.d(var_78.b)

Since we know from the execve call above that our argc parameter is 0x2, we hit the branch at [5] and our macstr parameter is copied via strcpy to the stack, resulting -in a stack-based overflow within the map_del_device binary and subsequent arbitrary code execution.

Crash Information

Breakpoint 1, 0x0045deec in ?? ()

[o.o]> set follow-fork-mode child

[>_>]> set follow-exec-mode new

[<_<]> disable 1

[x.x]> b *0x400658
Breakpoint 2 at 0x400658

[^~^]> c
Continuing.
[Attaching after Thread 11585.11585 vfork to child Thread 17607.17607]
[New inferior 2 (process 17607)]
[Detaching vfork parent process 11585 after child exec]
**************************************************
       [^.^] T-t-thanks for Stopping by! [^.^]
**************************************************
[Inferior 1 (process 11585) detached]
process 17607 is executing new program: /bin/map_del_device
[New inferior 3]
**************************************************
       [^.^] T-t-thanks for Stopping by! [^.^]
**************************************************
[New Thread 17607.17607]
[Switching to Thread 17607.17607]

Thread 3.1 "map_del_device" hit Breakpoint 2.2, 0x00400658 in ?? ()

[~.~]> !ls
***********************************************************************************
***********************************************************************************
v0         : 0x00000000                         | s3         : 0x004ad410
v1         : 0x0003e810                         | s4         : 0x004afe48
a0[S]      : 0x7ff6ae8c                         | s5         : 0x004afe68
a1[S]      : 0x7ff6bf3e                         | s6         : 0x004c03cc
a2         : 0x00000000                         | s7         : 0x00000010
a3[S]      : 0x7ff6ae8c                         | t8         : 0x00000000
t0         : 0x00000000                         | t9[L]      : 0x77a16810
t1         : 0x00000000                         | k0         : 0x00000001
t2         : 0x00000000                         | k1         : 0x00000000
t3         : 0x00000000                         | gp         : 0x0000000c
t4         : 0x77a8e014                         | sp[S]      : 0x7ff6ae10
t5         : 0x00000002                         | ra         : 0x00400648
t6         : 0x00400509                         | status     : 0x0000f413
t7         : 0x00410000                         | badvaddr[L] : 0x77a0dde0
s0[S]      : 0x7ff6b034                         | pc         : 0x00400658
s1         : 0x00000003                         | fcsr       : 0x00000000
s2         : 0x004afe88                         | restart    : 0x00000000
***********************************************************************************
   0x40064c:    bnez    t8,0x40067c
   0x400650:    lbu     v0,224(sp)
   0x400654:    lw      a1,4(s0)
=> 0x400658:    jal     0x400b00
   0x40065c:    addiu   a0,sp,276
   0x400660:    li      t8,2
   0x400664:    beq     s1,t8,0x40067c
   0x400668:    lbu     v0,224(sp)
***********************************************************************************

***********************************************************************************

[o.O]> info reg a0 a1
a0: 0x7ff6ae8c
a1: 0x7ff6bf3e

[^.^]> info reg sp
sp: 0x7ff6ae10

[-.-]> x/1s $a1
0x7ff6bf3e:     'A' <repeats 128 times>

[^.^]> set follow-fork-mode parent

[o.o]> set follow-exec-mode same

[>_>]> c
Continuing.
[Detaching after fork from child process 18412]
[Detaching after fork from child process 18414]
[Detaching after fork from child process 18430]
[Detaching after fork from child process 18432]

Thread 3.1 "map_del_device" received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

[^_^] SIGSEGV

[x.x] Could not find /proc/%d/maps, changing debug mode to `remote`
***********************************************************************************
***********************************************************************************
v0         : 0x00000000                         | s3         : 0x004ad410
v1         : 0x00000000                         | s4         : 0x004afcb0
a0         : 0x00000001                         | s5         : 0x004afcd0
a1[S]      : 0x7ff1e3a4                         | s6         : 0x004c03cc
a2         : 0x00000000                         | s7         : 0x00000010
a3         : 0x00000000                         | t8         : 0x0000000e
t0         : 0x00000000                         | t9[L]      : 0x77834ac0
t1         : 0x00004800                         | k0         : 0x00000010
t2         : 0x00000000                         | k1         : 0x00000000
t3         : 0x00000003                         | gp         : 0x778b2400
t4         : 0x0000005a                         | sp[S]      : 0x7ff1e538
t5         : 0x00000000                         | ra         : 0x41414141
t6         : 0x0000002e                         | status     : 0x0000f413
t7         : 0x00000013                         | badvaddr   : 0x41414140
s0         : 0x41414141                         | pc         : 0x41414141
s1         : 0x41414141                         | fcsr       : 0x00000000
s2         : 0x004afcf0                         | restart    : 0x00000000
***********************************************************************************
Invalid $pc @ 0x41414141
#0  0x41414141 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
***********************************************************************************

***********************************************************************************

Timeline

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

Credit

Lilith Wyatt of Cisco Talos