漏洞分析
最直观的方式,是先看POC,得到大概利用思路,再进行静态分析,然后拿真实设备调试(咸鱼)。
http认证绕过
使用的后端是mini_httpd,一个小型的嵌入式后端服务器,常见的还有lighthttpd、httpd等等,或者直接通过一些脚本例如lua来充当后端也是存在的。
通过在URL中附加%00currentsetting.htm
来达到身份认证绕过,本来一开始以为是类似于之前的Netgear的一个身份认证绕过,通过strstr()
此类函数直接判定URL中包含一些全局资源,然后无条件返回请求的资源,但是,并不是这样的,而是currentsetting.htm
字段会触发一个判定标志,这个标志=1会直接使判定通过。这个标志在http解析流程中一共有三个被赋值的地方,分别是:
- 在从
00407A28
开始的函数,也就是http的处理流程,当header的解析时,当SOAPAction
字段包含特定的字符串urn:NETGEAR-ROUTER:service
- 在函数从
00407A28
地址开始,同样在http解析流程中,当请求URL中包含字符串setupwizard.cgi
- 还是在这个http处理的函数中
但是前两个产生的标志位,出现在函数的比较靠前位置,都会导致程序的提前中止,就不能达到绕过的效果,第三个则相对靠后,不会退出。
因此可以通过构造如下的请求,对任意页面进行未授权访问:
1 | GET /file-to-access%00currentsetting.htm HTTP/1.1 |
发生在setup.cgi中的sesstion id认证绕过
在main函数的代码开头,如果是POST方法,紧接下来就是对于/tmp/SessionFile
文件的读取。先从POST请求中获取id字段的值,然后通过一个子函数sub_403F04
从/tmp/SessionFile
中读取存在系统中的id,二者进行比较。如果相同则通过了id的校验。验证逻辑关键代码如下:
1 | id_loc = strstr(post_data, "id="); |
但是在子函数sub_403F04
中存在逻辑上的问题,如果session_file
不存在,id_from_file
会直接返回0。那么,就可以通过构造id=0&sp=ABC
这种肯定找不到session_file
的字段,从而达到id_from_post == id_from_file == 0
验证通过。
1 | int sub_403F04(char* session_file) { |
setup.cgi未检验密码修改
整个cgi的处理流程大概是,当用户通mini_httpd登录,mini_httpd会将请求方式和请求附加参数写入到环境变量中,cgi读取环境变量REQUEST_METHOD
获取请求方式,例如GET或POST;读取QUERY_STRING
获取请求参数;然后通过写入能唯一标识会话的一些参数到文件中,用于会话管理。最后就是具体的对用户发送的数据进行处理。这个流程可以在setup.cgi文件逆向的main
函数中查看,还是比较清晰明了。
在CVE作者的分析文章里面,有提到是通过cgi的哪一个接口直接修改密码的,我也定位到了这个函数sub_40808
。但是,这个函数在cgi中没有被调用过?那么作者是如何得到这个接口的呢,直接通过抓包么。先直接给出payload,通过构造如下的方式可以重新设置密码。
1 | GET /setup.cgi?todo=con_save_passwd&sysNewPasswd=ABC&sysConfirmPasswd=ABC%00currentsetting.htm HTTP/1.1 |
对于sub_40808
的逆向,流程也很简单,检查两次输入的新密码是否相同,如果相同,就写入到NVRAM中的http_password中。但是如果要永久更改admin账号的密码到/etc/passwd和/etc/htpasswd中,可以通过如下两种方式之一:
- 重启设备,可以通过调用接口
/setup.cgi?todo=reboot
,将密码写入到/etc/passwd和/etc/htpasswd中 - 调用接口
/setup.cgi?todo=save_passwd
将密码写入到文件中
猜测这两个接口,是因为有路由器真实设备,在初始化的时候,第一次设置密码,通过分析交互http数据包得到的。
/tmp/etc目录权限管理
可以通过setup.cgi开启路由器的telnet,结合之前的mini_httpd和setup.cgi的认证绕过,请求/setup.cgi?todo=debug
。此时通过telnet登录得到的权限是admin权限,而不是root权限。但是因为/tmp/etc目录权限管理的问题,可以在/tmp/etc/passwd中添加一个root权限的账号。操作如下:
1 | cd /tmp/etc |
出现问题的原因是分析如下,/etc/目录通过软链接到了/tmp/etc/目录,而/tmp/etc/目录的权限是777。
那么admin权限的用户不能更改/etc/passwd文件,因为这是被root拥有的且权限为644(rw-r–r–)。但是admin权限的用户可以创建一个新的passwd文件,然后通过如上的方式,添加root权限账号。
这是执行了添加root权限操作后的文件属性。
小结
通过如上一系列的攻击链,先通过http的认证绕过,可以访问到setup.cgi;但是setup.cgi的操作也是存在sessionID认证,于是再次进行认证绕过;而且通过分析setup.cgi提供的接口,发现可以任意修改admin权限的登录密码,还可以开启调试模式的telnet;虽然这个时候通过telnet登录上去的是一个admin权限(非root),但是恰好由于/etc/里面的文件权限管理的问题,可以添加root权限的账号和密码。
那么这一系列的操作下来,就达到了一个未授权RCE漏洞。太强了太强了。