一、前言
本文的主要内容是笔者之前阅读《揭秘家用路由器0day漏洞挖掘技术》这本书时的读书笔记,现在补充了自己新的理解与体会后发布出来
纵览此书目录,知识体系的框架如下,笔者在此提炼出了明确的大纲
- 前置知识介绍,包括使用工具、分析环境、协议报文格式等
- 漏洞原理,对常见的漏洞进行介绍和攻击思路
- 再往后的知识点会更倾向实战,是对软件层的漏洞分析和利用
- 硬件层面的知识,讲述如何从设备中提取出固件
- 漏挖思路,代审,fuzz…
文章将依照此顺序对核心知识进行梳理,中间将插入漏洞复现、总结概括进行补充,以辅助读者对技术点进行理解
二、前置知识
0x00 杂谈
家用路由器在嵌入式设备里,是一类能够带来极其严重的安全问题的设备
- 首先路由是用户连接互联网的桥梁,掌控所有上网流量,是网络的公共出口,揭示了它的重要性和存在与恶意流量交互的可能性
- 其次随着智能路由器等 IoT 设备(笔者将这个范围理解为网联设备)的兴起,功能点、交互点逐渐增多,这意味着有机会撕开的口子变多了
- 对路由的漏洞利用能够造成严重的危害,当能够劫持用户流量时可以做的事情非常多,比如解密数据进行监听,窃取信息,造成数据泄露;篡改上网数据包,劫持用户访问钓鱼网站;DNS 劫持…
简单来说,对路由的漏洞利用主要分为以下几个大类
- 密码破解:默认密码、弱口令、使用字典暴力破解、侧信道攻击(这种手法通常是利用服务器响应时间、返回的数据包的特征等信息,去降低从 0-N 的爆破难度,以及这里记录一个攻击对象可以是 PIN 码)
- Web 漏洞:家用路由器一般带有 Web 管理服务,可以通过访问特定端口与 Web 界面进行交互,若是能够登录这个页面,通常可以对路由器进行管理和配置。接下来就是渗透的那套思路了,SQL 注入, RCE, CSRF, XSS, 这里可以重点了解 ”利用CSRF攻击路由器“ 的案例
- 后门:曾经很多路由都带有后面,现在不好说了,这种后门利用也没什么好说的
- 缓冲区溢出:此类漏洞要被挖出来了轻则触发 Crash,重则造成 RCE,且易于发现,多关注危险函数 strcpy, sprintf, gets, fgetc
- 逻辑漏洞;通过更加复杂的漏洞利用手法:例如对多个功能点组合利用、攻击链挖掘等,这就需要具体问题具体分析了,这种漏洞一般挖掘和利用的难度都较大,需要一定的经验
0x01 MIPS Linux
路由器是一种嵌入式系统,多采用 MIPS 和 ARM 这两种指令架构
MIPS 指令架构属于 RISC (精简指令集) 体系,是一种普遍应用于小型设备的处理器架构,使用 MIPS 指令架构的 Linux 系统便被称为 MIPS Linux,MIPS 架构广泛应用于嵌入式系统领域
RISC 架构的一些典型指令集包括 MIPS, ARM, RISC-V
在路由器中,常用的一种 MIPS 架构就是 MIPS32
在 MIPS 体系结构中有 32 个通用寄存器 REGISTER,其中主要的寄存器如下
REGISTER | NAME | USAGE |
---|---|---|
$0 | zero | 存储常量 0 |
$2-$3 | v0−v1 | 存放函数调用的返回值或表达式 |
$4-$7 | a0−a3 | 作为函数调用的前四个参数 (arguments) |
$8-$15 | t0−t7 | 供汇编程序使用的临时寄存器 临时变量 |
$16-$23 | s0−s7 | 调用子函数时 用于保存原寄存器的值 |
$24-$25 | t8−t9 | 供汇编程序使用的临时寄存器 补充t0−t7 |
$26-$27 | k0−k1 | 中断/异常处理程序使用 保存系统参数 |
$28 | $gp | 全局指针 (Global Pointer) |
$29 | $sp | 堆栈指针 指向栈顶 (Stack Pointer) |
$31 | $ra | 返回地址 (return address) |
然后还有 3 个特殊的寄存器,分别是 PC (程序计数器)、HI (乘除结果高位寄存器)、LO (乘除结果低位寄存器)
MIPS32 架构的寻址模式有寄存器寻址、立即数寻址、寄存器相对寻址和 PC 相对寻址
在 MIPS 指令集中,需要重点关注的是:
- l 开头的 LOAD 加载指令和 s 开头的 STORE 存储指令,用于从存储器中读取数据到寄存器,或者将寄存器中的数据保存在存储器中
- move 指令用于寄存器之间的值传递
-
算术运算指令的操作数只能是寄存器,例如
add, sub, mult, div
… 笔者认为它们与 AMD 架构下的算术运算指令比较大的区别是操作数的使用和数量,MIPS 架构的算数运算结果都会存在另外一个寄存器中,而不是去覆盖某一个原本存储某个操作数的寄存器 - slt 指令类似于 cmp
- syscall 的系统调用号存放在 $v0 中
-
分支跳转指令由 b 开头,如
b target
;beq $t0, $t1, target
等 -
常见的跳转指令有
j target
;jr $t3
;jal target
0x02 BusyBox
BusyBox 是一个精简的终端,路由器的 shell 是基于 BusyBox 的
路由器系统的存储空间有限,所以使用的 shell 通常是一个经过裁剪的 BusyBox 程序。路由系统中的 shell 所支持的命令,本质是指向 BusyBox 的符号链接
记录一些命令功能(Linux 的基础命令就不再赘述)
$ busybox --help # 查看当前路由器的BusyBox支持的命令
$ uname -r # 打印系统信息 + 显示当前运行的Linux内核版本(-r)
$ ls -al # 查看当前目录下包含隐藏文件的所有文件(-a) + 文件的详细内容(-l)
$ rm -rf /tmp # 强制删除/tmp目录 + 目录下的所有文件(-r) + 无需确认(-f)
$ cp -R /tmp ./now # 将/tmp目录复制到./now下
$ du -sk fireware.bin # 以KB为单位(-sk)查看bin文件大小
$ ps -ef # 查看当前系统正在运行的所有进程 + PID PPID(-f)
$ kill -9 pid # 强制(-9)终止进程号为pid的进程
$ killall -9 xxx # 强制(-9)终止进程名为xxx的进程
$ ifconfig -a # 查看所有网卡的信息(-a)
0x03 HTTP协议
1. 引言
家用路由器中的 Web 服务器经常会存在漏洞,所以需要使用 HTTP 协议与 httpd 通信。路由器的很多漏洞都存在于 Web 服务器没能正确解析攻击者发送的 HTTP 请求协议。因为我们需要了解 HTTP 协议的相关基础知识
HTTP 请求由 3 部分组成:请求行、消息报头、请求正文
2. HTTP 协议请求行
请求行遵循以下格式!
Method Request-URI HTTP-Version CRLF
接下来对每个字段进行讲解
-
Method: 表示请求方法,一般所有方法名都是大写。请求方法有很多种,在路由器安全研究中 GET / POST 使用得最为频繁,它们用于向 Web 服务器提交表单数据,然后其他的请求方法在路由器中不一定会都去实现
以下是通用的对 webserver 进行 request 的请求方法
-
GET: 请求获取 Request-URI 所标识的资源(请求数据)
-
POST: 在 Request-URI 所标识的资源后附加新的数据(发送数据,如提交 Web 表单)
所谓 GET / POST 传参的意思是,对目标服务器去附加数据后使用相应的请求方法去请求 http 服务器,以获取对应的资源。相比之下,GET 方法提交的表单数据只经过简单编码后作为 URI 的一部分传给 httpd;POST 方法提交的数据表单是作为标准数据传送,能够提交大量数据、以及实现信息保密
Request-URI 一般是是从原始 URL 中提取的路径部分,是 HTTP 请求的一部分,它在 HTTP 服务器上用于定位和请求特定的资源。而 URL 是用户用来直接访问资源的完整地址,可以独立存在,及直接访问
-
-
Request-URI: 统一资源标识符
-
HTTP-Version: 表示请求的 HTTP 协议版本
HTTP/1.1 是最广泛使用的HTTP协议版本,它引入了许多改进,包括持久连接(允许多个请求和响应通过同一个 TCP 连接进行传输)和管道化(允许客户端在等待响应时发送多个请求)
随着时间的推移,HTTP/2 进一步改进了协议,引入了头部压缩、服务器推送等特性,以减少延迟并提高性能
HTTP/3 则是最新的版本,它基于 QUIC 协议,提供了更好的性能和安全性
HTTP 安全性也是非常重要的一个方面,因此通常使用 HTTPS (HTTP Secure),即在 HTTP 下加入 SSL/TLS 协议,来保证数据传输的安全性和隐私性
-
CRLF: 表示回车和换行,在 C 语言中表达为 “”,对应的十六进制数是 “0A0D”
3. HTTP协议消息报头
HTTP 消息由 客户端到服务端的请求 和 服务端到客户端的响应 组成,即请求消息和响应消息,在漏挖测试中我们关心的是请求消息
消息请求报头允许客户端向服务器端传递”请求的附加信息“及”客户端自身的信息“。请求报头域的定义如下
字段: 值<回车>
这是访问百度的请求包,也就是说,框里的就是 消息报头域,它的字段是大小写无关的
接下来介绍常用的请求报头
-
Accept、Accept-Encoding、Accept-Language
Accept 用于指定客户端接受哪些类型的信息,例:“Accept: text/html” 表明客户端希望接受 HTML 文本
Accept-Encoding 用于告知支持的内容压缩编码方式,例:“Accept-Encoding: gzip, deflate” 告知压缩算法。若请求消息中没有设置这个域,则服务器假定客户端可以接受各种语言的编码Accept-Language 用于指定一种自然语言 -
Cookie
Cookie 请求头用于客户端向服务器提交 Cookie 信息验证 -
Authorization
Authorization 请求报头域主要用于证明客户端有权查看某个资源。当浏览器访问一个页面时,如果收到服务器的响应代码为401(未授权),可以发送一个包含 Authorization 请求报头域的请求,要求服务器对其进行验证 -
Host
Host 请求报头域主要用于指定被请求资源的 Intenet 主机和端口号,它通常从 HTTP URL 中提取出来。发送请求时,该报头域是必需的
在。可以指定端口,默认端口号 80 -
User-Agent
User-Agent 请求报头域允许客户端将它的操作系统、浏览器和其他属性告诉服务器,例如操作系统的名称和版本
4. HTTP协议请求正文
请求头和请求正文之间存在一个空行。这个空行非常重要,它表示请求头已经结束,接下来的内容是请求正文
请求正文是客户端发送给服务器的数据,比如表单数据、文件上传的内容、或者 XML/JSON 等格式的数据。不同类型的请求可能包含不同类型的请求正文。GET 请求通常不包含请求正文,因为 GET 请求主要用于请求数据,而不是提交数据。下面是一个包含请求正文的 HTTP POST 请求的例子
POST /submit-form HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
name=John&age=30
0x04 文件系统
路由的根文件系统与 Linux 系统基本上是一致的,分析时理清楚开发架构是很重要的,记录一些常规的核心路径
/bin, /sbin, /usr/bin, /usr/sbin
这些目录一般用于存放路由器中的应用程序
/lib, /usr/lib
目录用于存放程序运行时需要的动态库文件
/etc
目录用于存放路由器配置文件。在路由器系统中主要用来存放程序自启动配置文件、脚本文件及各种服务程序的配置文件(如 Web 服务器的配置文件等)
0x05 软件安装&环境配置
本节介绍在路由器漏洞分析过程中必不可少的软件,以及路由器分析环境的安装、配置、使用
1. 分析软件
需要安装好 VMware、Python、IDA Pro、IDA 插件
书本 P50 记录了在 Linux 下安装 IDA 的操作,之所以会存在这个需求,是因为
从路由器的固件提取出的根文件系统中有符号链接,为了避免在虚拟机和实体机(windows 主机)之间进行烦琐的复制操作,以及复制到 Windows 之后会造成符号链接丢失等问题,所以可以将 IDA 移植到 Ubuntu 上运行
2. 分析环境
需要安装好 Binwalk、QEMU、MIPS 交叉编译环境
下面重点介绍 Binwalk 和 QEMU
-
Binwalk
详见下一篇文章的 “四、「实战篇1」软件层分析 —— 0x01 文件系统 —— 自动提取文件系统” -
QEMU 环境搭建
QEMU
是一个模拟器,可以模拟多种处理器架构的计算机系统,这是我们实现跨平台运行二进制文件(如在 x86 的机子上跑其它架构程序)的工具QEMU
主要有两种运行模式:qemu-user
,qemu-user-static
,都装上吧
目前还不太清楚这两种模式的区别,只知道后者是 qemu 的静态编译版本。使用 qemu-user 运行动态链接的程序,需要在本地系统中安装与程序相应的库,而 qemu-user-static 则包含了所有必需的库和二进制文件,可以直接运行静态链接的程序$ sudo apt-get update $ sudo apt-get install qemu qemu-user qemu-user-static
安装
qemu-system
,用于模拟整个计算机系统的跨平台运行$ sudo apt-get install qemu-system uml-utilities bridge-utils
使用 qemu-user-static
虚拟机,运行 PowerPC 32-bit 的静态链接程序
$ qemu-ppc-static ./pwn
当然还能运行其他架构的程序,同理
arm/aarch64: qemu-arm-static/qemu-aarch64-static
mips/mipsel: qemu-mips-static/qemu-mipsel-static
ppc/ppc64: qemu-ppc-static/qemu-ppc64-static
使用 qemu-user
运行动态链接程序,需要先查找并安装对应架构的动态链接库(还是以 PowerPC 架构为例:
$ apt search "libc6-" | grep "powerpc"
$ sudo apt-get install libc6-powerpc-cross
# 安装好的库在 '/usr/powerpc-linux-gnu/lib/' 目录下
对应的,运行别的架构也同理
$ qemu-ppc -L /usr/powerpc-linux-gnu ./pwn
# 通过 `-L` 指定 `/usr/powerpc-linux-gnu` 目录
同理。(命令行中输入 'qemu-tab
' 可以查看 qemu 系列的指令
PowerPC 64-bit 架构:qemu-ppc64
PowerPC 32-bit 架构:qemu-ppc
ARM 64-bit 架构:qemu-aarch64
ARM 32-bit 架构:qemu-arm
MIPS 架构:qemu-mips
小端序 MIPS 架构:qemu-mipsel
0x06 路由器漏洞分析技能
本节介绍在路由器安全研究过程中涉及到的一些技能,书本中的内容包括但不限于:修复路由器的运行环境;配合 IDA 脚本功能,提高漏挖效率;使用 Python 编写脚本与程序自动化交互
1. 修复路由器的运行环境
笔者认为 IoT 这块最主要的痛点在于仿真和调试,由于硬件的缺失,固件在运行过程中容易缺失工作模块,从而导致仿真、动态调试失败
当拿到路由的固件,首先需要将路由的环境模拟起来,便于测试和调试。但当使用 QEMU 运行路由器中的应用程序(如 httpd)时,可能会遇到 路由器相关硬件模块缺失 而导致应用程序启动失败
记录修复路由器程序的大致执行流程:
- 运行程序,定位导致程序异常的函数
- 分析导致异常的函数,编写一个具有相同功能的函数,在函数中伪造执行流程和数据,并将编写的函数封装成一个新的动态库
- 使用 LD_PRELOAD 环境变量加载新的动态库来劫持目标程序中的异常函数,使目标程序执行动态库中的函数
2. IDA Pro
IDA 的基础使用不再赘述,以及能够使用 IDA 远程调试 QEMU 运行的程序
所谓远程调试是指通过网络调试在另一个网络上的计算机中运行的代码的过程。当然,也可以在本地计算机上使用远程调试功能。运行被调试应用程序的计算机(或模拟器)称为调试器服务器,运行 IDA Pro 界面的计算机称为调试器客户端
所以调试 QEMU 模拟器上运行的程序也可以称作远程调试
三、路由器漏洞原理与利用
0x00 路由器Web漏洞
家用路由器一般带有 Web 管理服务,可以通过 Web 管理界面进行路由器的管理和配置,所以也可以对路由管理网站进行渗透测试
1. XSS 漏洞
XSS(跨站攻击)是指入侵者在远程 Web 页面的 HTML 代码中插入具有恶意目的的数据,当用户认为该页面可信赖的,操作浏览器加载该页面时,嵌入其中的脚本将被解释和执行
HTML 作为一种用于创建网页的标准标记语言,它本身主要用于结构化和展示内容,但可以通过嵌入脚本语言(如 JavaScript)来实现用户与网页之间的交互功能
常见的利用手段如下:Cookie 中保存了完整的用户名和密码,“alert(document.cookie)” 这句简单的 JavaScript 脚本能轻易获取用户信息,入侵者可以通过脚本将用户信息发送到他们自己的记录页面中,若是泄露了 Cookie,用户就会遭受安全损失
2. CSRF
CSRF 攻击相较于 XSS 能够直接修改路由器参数,使目标长期被监控,危害更大,也更隐蔽。XSS 利用站点内的信任用户,而 CSRF 则通过伪装受信任用户的请求来利用受信任的网站。CSRF 攻击难以防范,且更具危险性
下面是一个有关 CSRF 攻击原理的例子。攻击者构造了一个针对路由器的 CSRF 链接,欺骗受害者点击该链接,其功能是修改路由器 DNS 为一个伪造的 DNS 服务器。之后,受害者访问正常网页时,因为DNS劫持而将所有网络访问数据发送给攻击者。攻击者监控受害者的所有网络访问行为,受害者却毫不知情。
另外,CSRF 也可以是一个被存储型漏洞, 若攻击者将攻击代码存储在被接受的 HTML 代码中就会导致存储型 CSRF,该漏洞可以伪装成普通链接或隐藏在图像标签中
3. 路由器基础认证漏洞
首先介绍基础认证 HTTP基本认证,并提供一道赛题辅助理解 http协议-基础认证
通过基础认证这种认证方式,攻击者可以构造出可跨站的方法,例如让用户点击超链接(钓鱼)、让浏览器自动请求资源…
早先的 IE 6.0 浏览器,以及现在的 Chrome、Firefox 浏览器,都可以使用一种比较特殊的 URL 访问方法实现路由器认证(http://admin:admin@192.168.0.1)。通过这种方法,在知道用户名和密码的前提下,可以不需要手工输入用户名和密码,就能完成路由器的直接认证和跳转
“可跨站” 通常与潜在的安全风险相关,因为它可能允许攻击者利用一个网站的漏洞来影响另一个网站
单论这种登录方式也许不算一种漏洞,因为这只是基础功能,可以搭配 XSS 或者 CSRF 等利用手法打组合拳
利用 XSS 窃取用户信息等操作和 CSRF 攻击的前提都是受害者处于登录状态。而通过基础认证可以瞬间使未登录状态 -> 已登录状态,从而后续能够结合 XSS 和 CSRF 对路由器进行进一步攻击
部分家用路由器是采用基础认证的登录方式进行验证的。利用这个特性,攻击者可以构造一个网页欺骗受害者点击,受害者点击后,网页脚本利用路由器基础认证功能进行登录操作,就可以使受害者在不知情的情况下登录路由器的管理页面。攻击网页可以附带 XSS 或 CSRF 攻击的脚本代码,若能够获取路由器敏感信息或设置路由器参数,能够对路由器进行持续的监控
0x01 路由器后门漏洞
存在于路由器中的 “后门”,有的是开发人员为了管理和控制路由器而留;有的是发布时遗留的安全漏洞;有的或许是因为厂商对路由器调试的高级接口登录设置认证过于简单,使得攻击者趁虚而入造成安全漏洞,这里将可以绕过安全控制而获取路由器访问权的漏洞统一称为路由器后门漏洞
后门漏洞复现:
0x02 路由器溢出漏洞
缓冲区溢出是一种非常普遍且危险的漏洞,原理不再赘述。MIPS32 架构的函数调用方式与 x86 系统有很大的差别,这个问题主要分成两点去阐述,首先是 MIPS32 架构下的函数调用原理,然后是函数调用的栈布局
1. MIPS32架构函数调用方式
对栈的操作:MIPS32 架构堆栈与x86 架构的一样,都是从高地址向低地址增长的。但是在 MIPS 架构中没有 ebp/rbp 指针,在进入一个函数时,需要将当前栈顶指针上抬 N 字节,这个操作就是在给当前函数开辟它的栈帧,此后栈指针就不再移动,直至函数返回时重新加上 N 这个偏移,这个操作就是在恢复现场。由于不能随意移动栈指针,所以寄存器压栈和出栈时都必须指定偏移量
函数调用机制:
函数调用前,caller 函数会在自己的栈顶预留一部分空间来保存 callee 函数的参数,我们称之为调用参数空间;MIPS 的调用函数指令会将函数的返回地址直接存入 $RA 寄存器而不是堆中
函数调用时,前4个传入的参数通过 a0 a3 传递。有些函数的参数可能会超过4个,此时多余的参数会被放入调用参数空间
2. MIPS32架构函数调用的栈布局
MIPS32 架构下的函数有两种类型——叶子函数和非叶子函数。如果一个函数中不会再调用其他任何函数,那么这个函数就是一个叶子函数,其余的函数都是非叶子函数
函数调用过程(函数 A 调用函数 B):
- 函数调用指令执行时会复制当前
$PC
寄存器的值到$RA
寄存器,即当前$RA
的值就是当前函数执行结束的返回地址,然后跳转到函数 B 执行代码 - 程序跳转到函数 B 后
如果函数 B 是非叶子函数,则函数 B 里首先会把函数 A 的返回地址入栈,函数返回时,函数 B 先从栈中取出被保存在栈上的返回地址,然后将返回地址存入寄存器$RA
,再使用 “jr $RA” 指令返回函数 A
如果函数 B 是叶子函数,则在函数返回时直接使用 “jr $RA” 指令返回函数 A(个人感觉这一块能看出精简指令集的特性,对冗余指令进行优化,叶子函数没必要再入栈上层函数的返回地址)
函数调用参数传递:
MIPS 体系的函数调用,通过 a0 a3 来传递前4个参数,其余参数通过栈传递,栈帧结构一般如下
这里可以看到虽然栈空间中不需要保留前四个参数,但 caller 函数依旧预留了前四个参数的栈内存空间,在 callee 函数内部分配栈空间后先将寄存器的值复制到 caller 函数预留的调用参数空间中
3. 缓冲区溢出漏洞利用
在讨论完叶子函数和非叶子函数的栈帧结构差异后,就可以开始讨论它们各自的栈溢出漏洞利用的可行性了。非叶子函数会存储返回地址在栈中,所以与常规的栈溢出利用区别不大;然而若是在叶子函数中存在缓冲区溢出漏洞一般无法直接利用,除非存在可以溢出大量数据的情况,就可以通过一路覆盖到父函数中的返回地址去劫持程序流
栈溢出漏洞的原理和利用不再赘述,本文主要分析漏挖思路
-
-
-