CVE-2024-41592 是 forescout 一篇为 《Breaking Into DrayTekRouters Before Threat Actors Do It Again》[1]的漏洞报告其中的一个漏洞。
漏洞产生于 GetCGI() 函数中, 在该函数中处理字符串参数会造成越界导致栈溢出。
漏洞分析
固件解压和调试准备
这里以Draytek 3910的 4.3.1 的版本作为调试 测试版本,进行展开分析。固件的解密和解压不展开赘述,可以参考之前 《HEXACON2022 - Emulate it until you make it! Pwning a DrayTek Router by Philippe Laulheret》 [2]slide 或者其他研究员的文章。
解压后能在 rootfs/firmware/vqemu/sohod64.bin 目录下找到主程序, Draytek 3910 采用了奇葩的 Linux + Qemu + RTOS 的奇葩架构,即在 arm linux操作系统上使用qemu 运行 drayos 的RTOS 操作系统。这里的调试方式采用的是使用编译 Draytek 开源的qemu代码进行编译,然后就可以正常调试。
Although this seems straightforward, challenges exist. Consider the “FreeCtrlName()” function called when a CGI handler returns (Figure 13). This function “frees” all the POST/GET request data structures, including the query string buffer. It simply iterates over the 32-bit pointers located in the lower 4 bytes of the stack 21 DRAY:BREAK - BREAKING INTO DRAYTEK ROUTERS BEFORE THREAT ACTORS DO IT AGAIN addresses and frees them, zeroing out the pointer values as well. Oddly, the higher 4-byte addresses (e.g., pointers to query string parameters values) are never freed
FreeCtrlName 函数伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
__int64 __fastcall FreeCtrlName(__int64 result) { int v1; // [xsp+1Ch] [xbp+1Ch] int i; // [xsp+2Ch] [xbp+2Ch]
这个函数的 free 逻辑是, 遍历栈上的指针, 一直free 直到为 0 为止, 因此我们需要找到一个函数可以在栈上写一个 0 , 这样就能避免这个问题。在原文[1] 甚至后来 12月在 Blackhat EU 《When (Remote) Shells Fall Into The Same Hole: Rooting DrayTekRouters Before Attackers Can Do It Again》[3]的slide 上都没有提及这个所谓的 [vulnerable-cgi-page].cgi 是什么。
1.《Breaking Into DrayTekRouters Before Threat Actors Do It Again》https://www.forescout.com/resources/draybreak-draytek-research/↩
2.《HEXACON2022 - Emulate it until you make it! Pwning a DrayTek Router by Philippe Laulheret》https://www.youtube.com/watch?v=CD8HfjdDeuM↩
3.《When (Remote) Shells Fall Into The Same Hole: Rooting DrayTekRouters Before Attackers Can Do It Again》 https://i.blackhat.com/EU-24/Presentations/EU24-Dashevskyi-When-Remote-Shells-Fall-Into-The-Same-Hole.pdf↩
──(root㉿kali)-[/home/kali/Desktop] └─# lvdisplay --- Logical volume --- LV Path /dev/groupA/home LV Name home VG Name groupA LV UUID vPWDHH-AlTq-GvBS-UAnf-orT1-yT2d-TdbWyK LV Write Access read/write LV Creation host, time (none), 2025-01-09 17:28:21 -0500 LV Status NOT available LV Size <4.87 GiB Current LE 1246 Segments 1 Allocation inherit Read ahead sectors auto
--- Logical volume --- LV Path /dev/groupA/runtime LV Name runtime VG Name groupA LV UUID dFDVOl-kYQR-J3N5-3HNC-toXc-9947-sj0yzc LV Write Access read/write LV Creation host, time (none), 2025-01-09 17:28:39 -0500 LV Status NOT available LV Size <19.46 GiB Current LE 4981 Segments 2 Allocation inherit Read ahead sectors auto
--- Logical volume --- LV Path /dev/groupZ/home LV Name home VG Name groupZ LV UUID cOTBS1-oaYw-PlAt-puTS-Uvq5-6C91-pK6QHK LV Write Access read/write LV Creation host, time (none), 2024-10-07 06:47:49 -0400 LV Status NOT available LV Size 6.72 GiB Current LE 1721 Segments 1 Allocation inherit Read ahead sectors auto
# swing @ sw in ~/Dropbox/Attachments/SafetyEquipment/VPN/ivc/2.3 [17:53:53] $ file out2.bak out2.bak: gzip compressed data, last modified: Sat Oct 5 17:32:45 2024, max compression, from Unix, original size modulo 2^32 118361088
$ ./openconnect 172.16.64.222 --protocol=pulse --dump-http-traffic -vvv Attempting to connect to server 172.16.64.222:443 Connected to 172.16.64.222:443 SSL negotiation with 172.16.64.222 Server certificate verify failed: signer not found
CVE-2024-41592 是 forescout 一篇为 《Breaking Into DrayTekRouters Before Threat Actors Do It Again》[1]的漏洞报告其中的一个漏洞。
漏洞产生于 GetCGI() 函数中, 在该函数中处理字符串参数会造成越界导致栈溢出。
漏洞分析
固件解压和调试准备
这里以Draytek 3910的 4.3.1 的版本作为调试 测试版本,进行展开分析。固件的解密和解压不展开赘述,可以参考之前 《HEXACON2022 - Emulate it until you make it! Pwning a DrayTek Router by Philippe Laulheret》 [2]slide 或者其他研究员的文章。
解压后能在 rootfs/firmware/vqemu/sohod64.bin 目录下找到主程序, Draytek 3910 采用了奇葩的 Linux + Qemu + RTOS 的奇葩架构,即在 arm linux操作系统上使用qemu 运行 drayos 的RTOS 操作系统。这里的调试方式采用的是使用编译 Draytek 开源的qemu代码进行编译,然后就可以正常调试。
Although this seems straightforward, challenges exist. Consider the “FreeCtrlName()” function called when a CGI handler returns (Figure 13). This function “frees” all the POST/GET request data structures, including the query string buffer. It simply iterates over the 32-bit pointers located in the lower 4 bytes of the stack 21 DRAY:BREAK - BREAKING INTO DRAYTEK ROUTERS BEFORE THREAT ACTORS DO IT AGAIN addresses and frees them, zeroing out the pointer values as well. Oddly, the higher 4-byte addresses (e.g., pointers to query string parameters values) are never freed
FreeCtrlName 函数伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
__int64 __fastcall FreeCtrlName(__int64 result) { int v1; // [xsp+1Ch] [xbp+1Ch] int i; // [xsp+2Ch] [xbp+2Ch]
这个函数的 free 逻辑是, 遍历栈上的指针, 一直free 直到为 0 为止, 因此我们需要找到一个函数可以在栈上写一个 0 , 这样就能避免这个问题。在原文[1] 甚至后来 12月在 Blackhat EU 《When (Remote) Shells Fall Into The Same Hole: Rooting DrayTekRouters Before Attackers Can Do It Again》[3]的slide 上都没有提及这个所谓的 [vulnerable-cgi-page].cgi 是什么。
1.《Breaking Into DrayTekRouters Before Threat Actors Do It Again》https://www.forescout.com/resources/draybreak-draytek-research/↩
2.《HEXACON2022 - Emulate it until you make it! Pwning a DrayTek Router by Philippe Laulheret》https://www.youtube.com/watch?v=CD8HfjdDeuM↩
3.《When (Remote) Shells Fall Into The Same Hole: Rooting DrayTekRouters Before Attackers Can Do It Again》 https://i.blackhat.com/EU-24/Presentations/EU24-Dashevskyi-When-Remote-Shells-Fall-Into-The-Same-Hole.pdf↩
──(root㉿kali)-[/home/kali/Desktop] └─# lvdisplay --- Logical volume --- LV Path /dev/groupA/home LV Name home VG Name groupA LV UUID vPWDHH-AlTq-GvBS-UAnf-orT1-yT2d-TdbWyK LV Write Access read/write LV Creation host, time (none), 2025-01-09 17:28:21 -0500 LV Status NOT available LV Size <4.87 GiB Current LE 1246 Segments 1 Allocation inherit Read ahead sectors auto
--- Logical volume --- LV Path /dev/groupA/runtime LV Name runtime VG Name groupA LV UUID dFDVOl-kYQR-J3N5-3HNC-toXc-9947-sj0yzc LV Write Access read/write LV Creation host, time (none), 2025-01-09 17:28:39 -0500 LV Status NOT available LV Size <19.46 GiB Current LE 4981 Segments 2 Allocation inherit Read ahead sectors auto
--- Logical volume --- LV Path /dev/groupZ/home LV Name home VG Name groupZ LV UUID cOTBS1-oaYw-PlAt-puTS-Uvq5-6C91-pK6QHK LV Write Access read/write LV Creation host, time (none), 2024-10-07 06:47:49 -0400 LV Status NOT available LV Size 6.72 GiB Current LE 1721 Segments 1 Allocation inherit Read ahead sectors auto
# swing @ sw in ~/Dropbox/Attachments/SafetyEquipment/VPN/ivc/2.3 [17:53:53] $ file out2.bak out2.bak: gzip compressed data, last modified: Sat Oct 5 17:32:45 2024, max compression, from Unix, original size modulo 2^32 118361088
$ ./openconnect 172.16.64.222 --protocol=pulse --dump-http-traffic -vvv Attempting to connect to server 172.16.64.222:443 Connected to 172.16.64.222:443 SSL negotiation with 172.16.64.222 Server certificate verify failed: signer not found
CVE-2024-41592 是 forescout 一篇为 《Breaking Into DrayTekRouters Before Threat Actors Do It Again》[1]的漏洞报告其中的一个漏洞。
漏洞产生于 GetCGI() 函数中, 在该函数中处理字符串参数会造成越界导致栈溢出。
漏洞分析
固件解压和调试准备
这里以Draytek 3910的 4.3.1 的版本作为调试 测试版本,进行展开分析。固件的解密和解压不展开赘述,可以参考之前 《HEXACON2022 - Emulate it until you make it! Pwning a DrayTek Router by Philippe Laulheret》 [2]slide 或者其他研究员的文章。
解压后能在 rootfs/firmware/vqemu/sohod64.bin 目录下找到主程序, Draytek 3910 采用了奇葩的 Linux + Qemu + RTOS 的奇葩架构,即在 arm linux操作系统上使用qemu 运行 drayos 的RTOS 操作系统。这里的调试方式采用的是使用编译 Draytek 开源的qemu代码进行编译,然后就可以正常调试。
Although this seems straightforward, challenges exist. Consider the “FreeCtrlName()” function called when a CGI handler returns (Figure 13). This function “frees” all the POST/GET request data structures, including the query string buffer. It simply iterates over the 32-bit pointers located in the lower 4 bytes of the stack 21 DRAY:BREAK - BREAKING INTO DRAYTEK ROUTERS BEFORE THREAT ACTORS DO IT AGAIN addresses and frees them, zeroing out the pointer values as well. Oddly, the higher 4-byte addresses (e.g., pointers to query string parameters values) are never freed
FreeCtrlName 函数伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
__int64 __fastcall FreeCtrlName(__int64 result) { int v1; // [xsp+1Ch] [xbp+1Ch] int i; // [xsp+2Ch] [xbp+2Ch]
这个函数的 free 逻辑是, 遍历栈上的指针, 一直free 直到为 0 为止, 因此我们需要找到一个函数可以在栈上写一个 0 , 这样就能避免这个问题。在原文[1] 甚至后来 12月在 Blackhat EU 《When (Remote) Shells Fall Into The Same Hole: Rooting DrayTekRouters Before Attackers Can Do It Again》[3]的slide 上都没有提及这个所谓的 [vulnerable-cgi-page].cgi 是什么。
1.《Breaking Into DrayTekRouters Before Threat Actors Do It Again》https://www.forescout.com/resources/draybreak-draytek-research/↩
2.《HEXACON2022 - Emulate it until you make it! Pwning a DrayTek Router by Philippe Laulheret》https://www.youtube.com/watch?v=CD8HfjdDeuM↩
3.《When (Remote) Shells Fall Into The Same Hole: Rooting DrayTekRouters Before Attackers Can Do It Again》 https://i.blackhat.com/EU-24/Presentations/EU24-Dashevskyi-When-Remote-Shells-Fall-Into-The-Same-Hole.pdf↩
──(root㉿kali)-[/home/kali/Desktop] └─# lvdisplay --- Logical volume --- LV Path /dev/groupA/home LV Name home VG Name groupA LV UUID vPWDHH-AlTq-GvBS-UAnf-orT1-yT2d-TdbWyK LV Write Access read/write LV Creation host, time (none), 2025-01-09 17:28:21 -0500 LV Status NOT available LV Size <4.87 GiB Current LE 1246 Segments 1 Allocation inherit Read ahead sectors auto
--- Logical volume --- LV Path /dev/groupA/runtime LV Name runtime VG Name groupA LV UUID dFDVOl-kYQR-J3N5-3HNC-toXc-9947-sj0yzc LV Write Access read/write LV Creation host, time (none), 2025-01-09 17:28:39 -0500 LV Status NOT available LV Size <19.46 GiB Current LE 4981 Segments 2 Allocation inherit Read ahead sectors auto
--- Logical volume --- LV Path /dev/groupZ/home LV Name home VG Name groupZ LV UUID cOTBS1-oaYw-PlAt-puTS-Uvq5-6C91-pK6QHK LV Write Access read/write LV Creation host, time (none), 2024-10-07 06:47:49 -0400 LV Status NOT available LV Size 6.72 GiB Current LE 1721 Segments 1 Allocation inherit Read ahead sectors auto
# swing @ sw in ~/Dropbox/Attachments/SafetyEquipment/VPN/ivc/2.3 [17:53:53] $ file out2.bak out2.bak: gzip compressed data, last modified: Sat Oct 5 17:32:45 2024, max compression, from Unix, original size modulo 2^32 118361088
$ ./openconnect 172.16.64.222 --protocol=pulse --dump-http-traffic -vvv Attempting to connect to server 172.16.64.222:443 Connected to 172.16.64.222:443 SSL negotiation with 172.16.64.222 Server certificate verify failed: signer not found