Cisco ios-shellcoder's handbook

百家 作者:Chamd5安全团队 2021-01-08 09:16:46

概况

开头

Cisco 系统其实应用面很广(路由器、交换机),但是因为设备昂贵,研究起来难度比较大,因此很多人没有研究思科 IOS 系统,但是随着 Windows 和 Linux 安全机制做的越来越好,Cisco IOS 这种安全性可能相对较差的系统就有了比较高的研究价值。Cisco Internetworking Operating System

Overview

主要是交换机和路由器还在运行 Cisco IOS 系统,而且基本上现在运行的这些设备不对 IOS 操作系统进行更新。

Cisco IOS 系统主要是设计用来管理硬件并且能尽可能快的转发数据包,所以IOS操作系统的并行性比较有限

Hardware Platform(硬件平台)

路由器和交换机都有着不同大小的系列产品,产品的变化范围很大,从小型的桌面机到大型系列,而且各自的硬件架构差别也很大。

IOS操作系统主要是在主 CPU 上执行,主要有以下几种 CPU 架构:

  • 2500 系列,主要CPU是 Motorola 68000
  • 7200 系列是 64 位 mips 架构
  • 目前最常用的 CPU 是 32 位 PowerPC 架构,比如部署在 1700 和 2600 系列路由器上
对 Cisco 路由器进行攻击时,要提前了解好,攻击的软件是不是运行在主 CPU 上,由一些特定的流量模式引发的漏洞可能只能崩溃专门的加速硬件,而不会在主操作系统上实现崩溃。

软件包

IOS 是作为单个镜像去部署的,最近 Cisco 公司已经开始发布了一些模块化的 IOS 版本并进行部署。但是目前很多的设备上还是运行着各自的 IOS,比如 Cisco FTP服务器,提供了 18000 多种不同的 IOS 构建。因此不同的 IOS 构建就有不同的内存地址、函数和代码。不像我们传统的 XP 或者 win2000,返回地址是单一的。

虽然 IOS 版本很多,但是有一些主流的版本,用的非常广泛。思科 IOS 主要是根据不同特征和用户群体的不同进行划分。通过一些字母可以表示不同的思科 IOS 系列:

  • 默认系列。思科 IOS 最主要的系列是没有特定字母的,是默认的。这是思科 IOS 最稳定并且用的最广泛的版本 。
  • Technology 系列(T),和主要的系列相比还是有一些不够成熟
  • Service Provider 系列(S),做ISPs
  • Enterprise 系列(E),通常用于企业核心路由器

除了版本和系列,研究者还想研究清楚,在什么平台上,什么样的镜像会被运行,并且会有什么样的特征和功能的提供。但是这个其实没有固定的套路,组合是多种多样的。比如下面是镜像名称里首字母的不同含义:

因为 IOS 镜像版本系列非常多,因此在 IOS 不同版本之中找到共同的属性难度非常大,比 windows 和 linux 系统难度大很多

在未来,随着 IOS XR 版本应用更广泛,这种状况将逐渐改变,但是目前,这种状况要持续很久。

IOS系统架构

Cisco IOS 操作系统架构比较简单,操作系统由内核和设备驱动程序代码构成,用于快速交换数据包的专业软件是设备驱动的一部分。

当分析 IOS 时,可以将整个系统想象为单一的 MS-DOSEXE 程序,并把其当作一个操作系统。IOS 使用的是一个从开始到结束的调度方法,不会在中途抢占进程,而是等进程完成一组工作,自愿返回内核。这和平常的操作系统调度不太相同。

内存布局

IOS 不使用任何的内部保护机制。一个进程的内存部分不会以任何方式屏蔽另一个进程的访问,并且 IOS 大量使用共享内存、全局变量、供任何进程访问的标志。

IOS内存分成以下部分:

所有进程都共享对这些内存区域的访问,因此无法进行跨进程的写保护,虽然安全性不高,但是开销远小于传统的操作系统(传统操作系统为了安全要进行隔离,开销很大)

IOS Heap(IOS 堆结构)

IOS 中,每个进程都有自己的堆栈,这个堆栈是系统分配的堆栈。初始化和未初始化变量的存储空间在编译的时候是已知的,并相应的保存在各个区段中。堆是被所有进程共享的,当 IOS 给进程分配堆内存的时候,就是将一个全局的堆中分出(就像是从一个大堆中分出小堆给进程)。因此,来自各种处理的存储块(堆块)通常是地址连续的,在检查路由器上的内存分配时,存储块的分配情况是可见的,例如下图:

可见不同任务的不同内存块是连续的。IOS 的整个堆结构是双链表结构,定义如下:

IOS 的每个堆块都有一个警戒区域,该区域放在每个堆块的实际有效载荷之后(我认为应该是末地址处),警戒区域是一个数值为 0xFD0110DF 的静态数字,通过这个区域来检测堆块是否发生溢出。

没有分配的堆块还应该包含管理信息,空闲块要构成另一个空闲块链表。所以说,一个空闲块其实是两个链表的一部分(全局堆链表和空闲堆块链表)如果 BlockSize 字段的最高位是0,则说明此块是空闲堆块,并且 FreeHeapBlock 结构在块头之后。

显然,这种多重链接的堆结构很容易被破坏。到目前为止,堆损坏是 Cisco 路由器崩溃的最常见的原因。为了防止 IOS 因为堆损坏而造成破坏,要有堆检查这个过程,堆检查会定期遍历堆链表来验证其完整性,堆检查大致执行以下操作:

  1. 验证块头是否有 Magic 数字
  2. 如果该块正在被使用,验证警戒区域是否是 0xFD0110DF
  3. 验证 PrevBlock 指针不为空
  4. 验证 PrevBlock 指针的 NextBlock 指针指向本块
  5. 如果 NextBlock 指针不为空,则验证其是否恰好指向此块警戒区域之后
  6. 验证 NextBlock 指针的 PrevBlock 指针指向本块
  7. 如果 NextBlock 指针为 NULL,则验证其是否确实是在内存边界
除了常规检查之外,当进程分配和释放堆块的时候也会执行这些检查,原先是为了提高路由器的稳定性,现在其实也是为了安全。如果出现故障,Cisco 会重启计算机,路由器能够在几分钟甚至几秒钟内恢复联机并正常运行,让人甚至感觉不到他已经重启了, 当然这些检查能够使漏洞利用更加困难。

IO Memory(外设存储)

IOS 在堆使用方面还有一个特点:称为 IO 内存的内存区域。该区域在一些共享内存路由器上最为重要。因为主CPU和媒体控制器以及系统的其他部分共享这块区域。IO 内存是在分配主堆之前从内存中提取出来的,包含缓冲区。缓冲区既可以供路由器的一些进程使用,也可以供接口使用,这些缓冲区是环形的。其实这块漏洞利用程度比较小,因为在堆块分配之前,这块区域就已经单独划分出来了,所以这块区域的漏洞利用效果不大。

Cisco IOS的漏洞

路由器操作系统提供的攻击面和通用操作系统不同。在两种常见情况下,路由器会处理攻击者制造的攻击数据:一种是路由器做流量转发的时候(从一个端口转发到另一个端口),另一种是路由器是流量的最终目的地,要为其提供相应服务的时候。

通常,路由器其实都不想在流量转发的时候做太多操作,因为这样会降低转发的性能。因此流量转发的时候能利用的漏洞比较少,除非路由器必须要检查或者处理相应流量,这时候可以做漏洞利用(比如要做流量的安全筛选等等)

因此,当路由器提供服务的时候,是可以提供一些攻击的机会的。

IOS 中最常见的中断是数据包解析代码。根本原因是,IOS 中仅向个别开发人员提供了很有限的数据包解析功能,目前还不清楚思科为什么要这么做,可能是因为重复的函数调用对于解析数据包来说代价太大。因此 Cisco IOS 上的大都数服务器实现都使用指向数据包的指针并手动解析。显然,这种策略在提高性能的同时,会更有可能导致解析代码中的漏洞发生。最明显的证明就是处理 IPV4 的时候,解析代码漏洞反复出现。

协议解析代码(解析时触发漏洞)

因为 IOS 能够支持很多协议,所以对这些协议进行解析的时候是最明显的攻击点。经验表明这种方法也是最有前途的,思科路由器支持很广泛的各种协议,包括一些比较神秘的协议,也包括一些很难解析的协议。其实能够预想,这里面肯定有很多漏洞存在。

路由器相关服务(服务异常)

其实研究路由器提供的相关服务也是一个方向。IOS 不提供多线程或者是流程派生之类的功能,因此是通过多个并行服务来处理的。所以,如果我们故意提供大量的或者有冲突的信息,IOS 可能就会服务异常。虽然从协议角度来说并不成立。因为路由信息分发和处理是 Cisco 的核心业务,状态机等代码在 IOS 上通常是相对稳定的,但是这个方向也值得考虑。

安全功能

主要分为两种,检测功能本身存在的漏洞、逻辑漏洞

和其他网络安全技术相似,附加的过滤和防护机制也为 IOS 开启了新的攻击途径。IOS 必须检查的恶意数据越多,发生漏洞的可能性也越大。Cisco 每次推新的过滤或者一些服务,都需要添加复杂的协议解析代码,就会增加额外的攻击面。未来的主要目标可能是入侵检测功能、内容过滤器、重定向器、加密隧道等等。

IOS 处理流量过程中的逻辑错误,通常对应于流量过滤代码。比如说没有应用IP过滤规则或者没有正确应用IP过滤规则。因此可以通过构造一些特殊的数据包去加以利用。

命令行界面

CIsco 路由器的命令行操作是比较难提权的。一般有 15 种不同的特权等级,用户等级的命令权限很低,因为一般路由器的管理员都不希望其他人能够随便的登录或者操作自己的路由器。但是当有监控工具执行的时候,攻击者有时可以获取到用户级别的访问权限。

IOS逆向

找漏洞有时候不一定非要逆向 IOS 系统,有时候直接模糊测试(fuzz)也可以,但是如果找到漏洞,并且想后续利用的话,就必须理解相关的代码、内存布局等等,因此还是要对 IOS 进行逆向。

直接从 Cisco 拿镜像的话需要 CCO 账户,不太好获得。但是如果我们当前有设备,可以把当前使用的设备的镜像 dump 出来,用 TFTP 服务器 dump 就可以。

拿到镜像后,要有一个大的虚拟环境来运行,要有 IDA 这些反编译器等等,然后可以进行后续的工作。

提取IOS镜像

IOS 镜像拓展名都是.bin文件,而且一般镜像都是被压缩的,当启动时,镜像会解压运行。一些小路由器的镜像,直接都是代码开头。现在的 IOS 镜像一般都是 ELF 二进制文件。镜像开头一般都包含自解压的代码。

在 IOS 镜像加载到 IDA 之前,一般要把前面的自解压代码部分删除,并重新保存...但是现在应该不用这么做,直接拿 bandizip 解压bin文件就行。

(有时候 ida 加载之后,不能正确生成字符串的交叉引用,这样的话可能需要 ida python 这些脚本,正确得到交叉引用的结果)

区别IOS镜像的不同

其实这个就是二进制比对,新老版本的 IOS 镜像去做比对,可能能定位到 1-day 漏洞。

用 bindiff 去做比对,需要 ida 先解析生成idb文件,但是有时候 ida 不能正常解析识别所有函数,但是 Cisco 使用 GNU 工具链来构建代码,就是说,下一个函数都是从上一个函数的结束位置开始。因此,我们可以自己去编写 idapython,让 ida 把所有的函数都成功解析:

解析完函数后,可以进行二进制比对,一定要定位一些重要的函数,特别是涉及路由器崩溃、日志记录和调试等功能的函数。

运行时分析

单纯的静态分析不够,最好能够在执行的时候进行调试分析。

Cisco调试工具

思科路由器提供了一些基本的记载工具,可以调试崩溃。一般有两个级别:IOS本身生成的一个崩溃时候的 sump,还有一个是 ROMMON 功能

ROM监控

对于思科来说,ROMMON 就像 EFI BIOS 对于现代台式机一样,加载启动项之类之类的,很重要。老一点的路由器有一个基本接口,只允许设置几个启动参数;现代路由器有更丰富的 ROMMON 代码和功能集。

开机的时候,按CTRL+Break即可进入 ROMMON。当路由器通过 ROMMON 启动主映像的时候,路由器会标记。正常情况下,当路由器出现崩溃,会显示崩溃信息并且重启,当通过 ROMMON 启动的时候,路由器崩溃会返回 ROMMON,供我们研究内存、寄存器等情况。

在 ROMMON 模式中,还可以进入特权模式,就可以读取、写入内存内容,在NVRAM上启用或者禁用写保护,并且可以在内存任何位置设置断点。对于研究人员来说会方便很多

进入 ROMMON 的特权模式需要和机器相关的密码,所以必须先想办法收集机器的 cookie

获得完 cookie 后,要计算得到特权模式的密码,将打印的数字视为16位(2字节一组)的 16 进制值,并且把前五组相加

还有几点要注意的就是,平台不同,端序不同,因此计算的时候字节序不同,还有就是字母统一为小写,还有就是如果计算之后超长了(长度大于了4个16进制数),取右4个数(把左边超出的切掉)。

如果特权密码输入正确的话,会弹出提示,输入错误没有什么反应,进入特权模式后,输入help可以看到所有的新获得的权限。

崩溃转储

思科现在支持将崩溃信息记录并存储。IOS 可以将崩溃信息写内存卡里或者闪存里,也可以通过 TFTP 服务器传到远端。不同版本路由器可能提供的转储信息记录功能不相同,但是基本信息都是可以记录的,崩溃转储功能要配置一下:

总之要利用好崩溃时转储的文件,会有很多有价值的方向:

GDB代理

Cisco 可以用 GDB 远程调试

攻击Cisco IOS

栈溢出、堆溢出都可以利用。栈溢出利用得好的话能够产生立马的效果,堆溢出如果产生,路由器要反应几十秒,去做堆检查。可以用 debug sanity 指令去做检测加速。

栈溢出

栈溢出利用和其他平台的相似,覆盖函数返回地址,劫持控制流;或者是通过栈溢出,来溢出展示一些栈内的有价值的数据,进而方便后续的利用。主要是有两个难点,一个是思科平台太多,可能攻击者没法选择正确的shellcode;另一个是思科的堆管理模式太简单,用简单的堆管理方法去分配堆栈,会导致栈的相关地址是不固定的。

获取给定进程的堆栈地址:IOS 拥有一个包含所有进程上下文信息的结构的指针数组,可以用命令show memory allocating-process找到这个数组,并列出内存块和分配的进程。输出还将包括每个进程的名为Process Stack的条目。因此为了找到这个进程的堆栈指针,用show process cpu指令去获得这个进程对应的ID,然后在条目里找就可以了

show mem +地址可以看内存内容 

show stacks+pid可以看进程的栈信息

堆溢出

如果是堆块的溢出,通常会触发崩溃,此时堆栈检查会告诉 IOS 怎么崩溃的,并且记录相关的信息,作为崩溃转储。但是瑞一处不是那么好做的,因为 IOS 会使用静态的 magic number 去检测是否发生了溢出。

对于静态的魔术数字,并且我们能够获得堆块长度,我们可以通过构造正确的魔术数字,成功绕过这个检查,但是对于有些场合,我们不好把握堆块长度或者什么,这个检查就很难绕过。

还有一个问题就是,堆检查会定期对堆进行循环检查,因此利用难度也会加大。

因此为了使路由器实际在堆块中使用可预测的地址,攻击者必须先让路由器崩溃,然后路由器重新启动。路由器在重启过程中每次可能会使用相同的内存地址分配的堆块。因此就可以获得一个相对稳定的地址,然后去构造漏洞。

堆块的BlockSize字段会进行验证,因此也需要把这个字段构造成正确的值。介于0x7FFFFFD00x7FFFFFFF之间就行

下面是一个路由器的检查表:

总之,IOS 内存结构就是,通过全局堆,然后给每个进程分配堆块,每个进程的进程栈也由这些堆块构成,所以 IOS 的栈和堆的地址不固定,但是因为 IOS 对栈溢出的检查比较少,可以比较方便的构造栈溢出,除了触发崩溃,也能通过溢出获得敏感信息;而因为有堆检查的存在,导致堆溢出构造难度比较大。如果获得了管理员权限,堆结构其实也是可以看的,但是远程调试路由器的时候,管理员权限很难获得,所以通常都是构造栈溢出比较多。

结束


招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系admin@chamd5.org



关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接