A netgear router stack-based buffer overflow from discovering to exploit

Introduction

最近在研究一款型号比较老的netgear的路由器,记录一下整个漏洞从挖掘到利用的过程。

Firmware Analysis

首先对路由器固件进行解包,查看其中包含的可执行程序和文件,一般关注的点在 web server 和一些配置文件还有 web 目录。

经过一番搜索,发现 /usr/sbin 目录下有一个 httpd 以及 telnetd,可以确定 httpd 作为路由的 web server, 并且路由是有 telnet 服务的。

现在就要想办法拿到路由的调试接口,一般是通过连接 telnet 后上传 gdbserver,用 gdb 来调试。

先看一下路由开了哪些服务,查看 nmap 扫描结果:

发现其23端口是开启的,尝试连上去,但是没有任何反应。这里就遇到了一个 netgear 路由器的特性,经过搜集资料发现, netgear 默认先启动 telnetenable 服务,该服务启动后会一直监听23端口,等待接受一个特定的key,该key由路由的MAC地址, 加上用户名和密码经过算法加密,如果验证通过后则会开启真正的 telnet 服务。网上已经有现成的工具用来解锁 netgear 的 telnet 服务,放在 github 上。需要提一下的是, 解锁时使用的用户名和密码并不是路由的账户密码,而是在程序中指定的 “Gearguy Geardog”,可以对 telnetenable 进行逆向来确认。

现在我们可以对路由上的服务进行调试了,就以 httpd 为目标,不过在这之前我们得先逆向分析一下程序的逻辑,结合黑盒测试, 找出可能存在漏洞的范围。

Null Point Reference

这里我们不对后台功能进行测试,只对能够访问的范围进行测试,后台的漏洞需要结合其它绕过登陆的漏洞才能达成攻击效果, 影响力以及危害较小。现在要做的就是搜集默认可以访问的范围,可以把web目录下的html放到列表,用 burpsuite 的 intruder 模块 对列表里的url进行测试。对于有有效回显的页面,就是我们有权限访问的范围。但是仅仅是web目录下的一些 html 并没有多大作用, 还是得对二进制程序进行逆向分析,梳理出服务url处理逻辑,有哪些可控的函数。

经过一段逆向分析,找到了函数的 cgi 列表,同样将这份列表添加到我们 intruder 的 target 中,进行自动测试。好消息是跑了没几分钟, 就触发了crash,burp 接收不到返回包了,这时查看一下路由上的进程,httpd 确实已经退出了。

我们来仔细看看触发 crash 的 url,以及 crash 的场景。 触发 crash 的 http 请求:

我们先用 gdb attach 上去,等待 crash 触发。

崩溃现场:

可以看到是一个空指针引用触发的 crash,这样我们就发现了一个 DOS 漏洞 ^_^.

Overflow

其实前面的过程都比较简单,只是普通的测试一遍,还没有对代码逻辑进行深入挖掘。接下来我们对程序崩溃的地方进行深入分析, 看看能不能发现一些有趣的东西。在分析过程中我注意到了 websGetVar 函数,貌似是变量可控的,经过分析之后发现,我们可以通过这个函数, 往栈上写入 0xff 字节的内容。这里我产生了一个思路,找出所有调用这个函数的函数,通过判断目标缓冲区大小是否小于 0xff, 则可以找出时候存在溢出。用 ida 可以直接列出函数调用关系:

可以用一个一个慢慢找,不过这样效率太低,我们使用 idapython 脚本对溢出进行判断,有关 idapython 推荐 The Beginner’s Guide to IDAPytho

wow,总共有三十多处溢出被检测到,我们在里面找一个不需要登陆验证就可以直接访问到的进行利用。

Exploit

利用一般有两种思路,一种是执行 shellcode 获取 shell,另外一种是调用 system 执行命令。一般我会习惯先尝试跳 shellcode, 不过这次不好找地方放 shellcode,web server 的全部操作从接收 http 包到后续的 url 处理都在栈上,并没有使用到堆, 所以 shellcode 只能放 栈上,而 ASLR的保护等级为 1,即栈地址是随机化的,这样就得像办法找 rop gadgets 跳栈地址。

这里还有一个问题,一般 post 数据都会被空字符截断,而代码段地址都是包含空字符的,意味着我们只能使用一次 rop。 在花了很长时间寻找合适的 gadgets 无果的情况下,经大佬提醒,验证了一下路由的 ASLR 是否真的开启,结果发现由于实现问题, 使得动态库的地址并未被随机化,意味着我们可以使用更多的 gadgets,形成 rop 链。接下来通过 ropper 找到了合适的 gadgets,调用 system,成功利用。