Linksys WRT54G路由器溢出漏洞分析——运行环境修复
1-、漏洞介绍:
Linksys WRT54G是一款SOHO无线路由器,在功能、稳定性、双天线信号覆盖能力方面都得到了用户的认可。它还支持第三方固件,从而使其功能更加强大。
但Linksys WRT54G v2版本的路由器曝出过一个漏洞,CVE编号为CVE-2005-2799。该漏洞存在于路由器Web服务器程序HTTPD的apply.cgi处理脚本中,由于对发送的POST请求没有设置足够的边界与内容长度检查,当未经认证的远程攻击者向路由器的apply.cgi页面发送内容长度大于10000字节的POST请求时,就可以触发缓冲区溢出。这个漏洞会允许未经认证的用户在受影响的路由器上以root权限执行任意命令。
2、WRT54G固件漏洞研究:
2.1 WRT54G httpd服务器模糊测试:
在firmadyne许你还能运行路由器防火墙,使用burpesuite测试apply.cgi请求可以导致httpd服务崩溃。说明该请求存在bug。
配置好参数后,点击start attack开始攻击,在攻击串超过10000后httpd服务器崩溃。
2.2 修复运行环境
当我们使用模拟器(QEMU)运行路由器中的Web服务器时,遇到一个问题——模拟器缺乏硬件的模拟,导致程序无法执行。而需要执行的Web服务器就是应用程序试图采用NVRAM中的信息来配置参数,但由于找不到设备导致了错误的发生。在路由器中,常见的NVRAM动态库libnvram.so提供了nvram_get()函数和nvram_set()函数来获取和设置配置参数。入股使用模拟器运行应用程序,会在调用nvram_get()函数时失败,导致应用程序无法运行(因为模拟器中没有NVRAM)。使用如下命令运行HTTPD:
在运行的过程中可以看到,程序报错,提示找不到/dev/nvram文件或目录,且实用netstat命令查看当前系统开放的端口时没有发现80端口,Web服务器启动失败。
2.2.1修复NVRAM
使用zcutlip的一个nvram-faker来修复NVRAM。Nvram-faker虽然是一个简单的动态库,但可以使用LD_PRELOAD劫持libnvram库中的函数调用。只需要向一个ini的配置文件中写出合理的NVRAM配置,就可以使Web服务器程序运行。
2.2.2 修改nvram-faker增加劫持函数
原始fork函数会启动很多子进程导致程序调试复杂,所以需要劫持它,简化调试。
增加劫持get_mac_from_ip,修改nvram-faker.h文件添加fork函数;
修改nvram-faker.c添加fork\get_mac_from_ip函数。
2.2.3 编译nvram-faker
编译好之后,会在/nvram-faker目录下生成一个名为”libnvram-faker.so”的动态库。然后将libnvram-faker.so和同目录下的nvram.ini复制到WRT54G路由器的根文件系统中。由于libnvram-faker.so使用了共享库编译,所以还需要将mipsel-linux-gcc交叉编译环境中lib库下的libgcc_s.so.1复制到WRT54G路由器的根文件系统中。
2.2.4 修复HTTPD执行环境
HTTPD在运行时需要对/var目录下的某些文件进行操作,而这些文件是在Linux启动过程中才会产生的,因此编写了prepare.sh脚本修改HTTPD执行环境。
编写run_cgi.sh脚本提供两种方法执行HTTPD,一种是不需要调试器介入直接运行程序的执行模式,另一种是开放1234调试接口等等调试器链接。在QEMU环境中模拟执行HTTPD时,使用LD_PRELOAD环境变量加载libnvram-faker.so劫持函数调用,修复因硬件缺失导致的运行错误。
2.3 漏洞成因分析
运行prepare.sh脚本,修复HTTPD执行环节;
使用run_cgi.sh脚本调试模式执行HTTPD,等待调试器链接;
使用IDA加载HTTPD,进行远程附加调试。待HTTPD服务器开启后,在Windows下运行测试脚本wrt54g-test.py;
可以看到,Ubuntu中的HTTPD程序已经崩溃了。阅读崩溃部分的代码,发现程序希望将0写入0x41419851(0x41414141+0x5710)处时造成错误。其原因是:系统找不到0x41419851这块内存,而0x41414141是我们发送的伪造数据,0x5710正好是伪造的POST参数的总长度。
从汇编代码中可以看到,崩溃现场在do_apply_post函数的代码段中。从命令上可以知道,该函数的功能是处理apply的POST参数。
下面,我们看一下崩溃现场附近的代码,分析造成漏洞的真正原因,
在do_apply_post函数偏移0x3C处的伪代码如下:
读取长度为content-length的所有POST数据到post_buf,如果读取的POST数据长度不为0,就计算post_buf中数据的长度。
这里的content-length是POST参数的长度,在调用do_apply_post函数时并没有进行校验,而该长度在使用读取数据进入内存时也没有进行校验就直接都去了POST参数,因此导致了缓冲区溢出。
而产生缓冲区溢出的内存post_buf位于HTTPD的.data段中。在应用程序中,.data段用于存放已初始化的全局变量,这里的post_buf大小为0x2710字节(10000字节)。
现在我们已经弄清楚了漏洞的原理。该漏洞在接收超过10000字节的来自攻击者伪造的数据包时,由于在do_apply_post函数调用前后没有验证POST数据的长度,而在do_apply_post函数中使用了自定义的wfread()函数,并调用了fread()系统函数,直接将伪造的超长POST数据全部富之岛大小为10000字节的全局变量post_buf中,所以导致了缓冲区溢出。
2.4 漏洞利用
生成POC;wrt54g_POC.py
利用流程:
01 打开网页,访问网关(路由器)。网关是192.168.1.1,浏览器访问192.168.1.1,登录WRT54G路由器,在首页上可以看到当前路由器的型号和固件版本。
02 使用nc命令在192.168.1.100上打开4444端口监听,命令为”nc -lp 4444”。
03 执行测试脚本,命令为”wrt54g_POC.py 192.168.1.1”。
04 控制路由器,执行任意命令。