Sec Hotspot 首页  排行榜  收藏本站  技术博客  RSS
统计信息
已收录文章数量:12925 篇
已收录公众号数量:89 个
本站文章为爬虫采集,如有侵权请告知
已收录微信公众号
网信中国 区块链大本营 白说区块链 区块链投资家 区块链官微 区块链铅笔Blockchain HACK学习呀 二道情报贩子 合天智汇 小白帽学习之路 小米安全中心 弥天安全实验室 SAINTSEC SecPulse安全脉搏 TideSec安全团队 360安全卫士 游侠安全网 计算机与网络安全 安全祖师爷 安全学习那些事 腾讯安全联合实验室 黑客技术与网络安全 安全圈 腾讯御见威胁情报中心 Python开发者 Python之禅 编程派 Python那些事 Python程序员 安全威胁情报 吾爱破解论坛 行长叠报 安在 i春秋 嘶吼专业版 E安全 MottoIN 网信防务 网安杂谈 数说安全 互联网安全内参 漏洞战争 安全分析与研究 邑安全 ChaMd5安全团队 天融信阿尔法实验室 安全牛 SecWiki 安全学术圈 信安之路 漏洞感知 浅黑科技 Secquan圈子社区 奇安信集团 奇安信 CERT 国舜股份 雷神众测 盘古实验室 美团安全应急响应中心 瓜子安全应急响应中心 顺丰安全应急响应中心 蚂蚁金服安全响应中心 携程安全应急响应中心 滴滴安全应急响应中心 字节跳动安全中心 百度安全应急响应中心 腾讯安全应急响应中心 网易安全应急响应中心 OPPO安全应急响应中心 京东安全应急响应中心 Bypass CNNVD安全动态 安恒应急响应中心 天融信每日安全简报 奇安信威胁情报中心 看雪学院 黑白之道 水滴安全实验室 安全客 木星安全实验室 云鼎实验室 绿盟科技安全预警 白帽汇 深信服千里目安全实验室 腾讯玄武实验室 长亭安全课堂 FreeBuf 绿盟科技 nmask
Ripple 20:Treck TCP/IP协议漏洞技术分析
本文来自公众号:合天智汇   2020.07.14 10:33:38


Ripple20是一系列影响数亿台设备的0day(19个),是JSOF研究实验室在Treck TCP/IP协议栈中发现的【1】,该协议栈广泛应用于嵌入式和物联网设备。而由于这些漏洞很底层,并且流传版本很广(6.0.1.67以下),通过供应链的传播,使得这些漏洞影响十分广泛和深远。这也是这个漏洞代称 Ripple 的由来:The damaging effects of a these vulnerabilities has been amplified like a ripple effect to a dramatic extent due to the supply chain factor. (Ripple意思就是涟漪,连锁作用)
我们在本文中,重点分析了Ripple20中的CVE-2020-11896(CVSS V3 10.0)漏洞原理和利用。

背景知识

IP 分片

IP分片的概念是为了解决数据包最大长度的限制,例如以太网 最大传输单元MTU 为1500字节。那么如果发送的IP packet超过这个长度,就需要将数据包分为小的片段fragments。分片之后,想要将分片的数据包,在接收端重组,还需要一些标志位,用于表示接下来是否还有分片,该分片在整个数据包的偏移offset和是否是同一数据包等信息。在IP头部有4个字节(4-8字节)是用于存储这些分片信息的:

其中Flags字段有3部分:Reverse位、DF(Don't Fragment)和MF(More Fragments)。

IP隧道

IP隧道是将IP报文封装在另一个IP报文中的技术,如果外层和内存协议都是IP协议,那么我们将之称为 ip-in-ip ,结构如下图所示:


Treck TCP/IP的内部实现

Treck TCP/IP中,通过tsPacket结构表示数据包Packet:

struct tsPacket {    ttUserPacket pktUserStruct;    ttSharedDataPtr pktSharedDataPtr; // Point to corresponding sharable ttSharedData     struct tsPacket * pktChainNextPtr; // Next packet (head of a new datagram in a queue)     struct tsDeviceEntry * pktDeviceEntryPtr; // pointer to network Device struct     union anon_union_for_pktPtrUnion pktPtrUnion;     tt32Bit pktTcpXmitTime;     tt16Bit pktUserFlags;     tt16Bit pktFlags;     tt16Bit pktFlags2;    tt16Bit pktMhomeIndex;    tt8Bit pktTunnelCount; // Number of times this packet has been decapsulated. Initially setto zero.    tt8Bit pktIpHdrLen; // Number of bytes occupied by the IP header.    tt8Bit pktNetworkLayer; // Specifies the network layer type of this packet (IPv4, IPv6, ARP, etc).    tt8Bit pktFiller[1];};

该结构重要字段包括:tsShareData字段:指向存储处理数据包所需信息的Buffer的指针pktChainNextPtr字段:指向下一个PacketpktUserStruct字段:表示数据包分片pktUserStruct字段是另外一个重要的数据结构 ttUserPacket(typedef struct tsUserPacket)

struct tsUserPacket {    void * pktuLinkNextPtr; // Next tsUserPacket for fragmented data 指向下一个分片    ttUser8BitPtr pktuLinkDataPtr; // Pointer to data     ttPktLen pktuLinkDataLength; // Size of data pointed by pktuLinkDataPtr 当前buffer的大小    ttPktLen pktuChainDataLength; // Total packet length (of chained fragmented data). Valid in first link only. 整个packet的长度,如果没有分片,等于pktuLinkDataLength    int pktuLinkExtraCount; // Number of links linked to this one (not including this one). Valid in first link only.};


该结构重要字段包括:pktuLinkNextPtr字段:指向下一个分片的指针pktuLinkDataPtr字段:指向数据buffer的指针pktuLinkDataLength字段:表示当前分片长度pktuChainDataLength字段:表示整个Packet长度,当不存在分片的时候,这个长度与pktuLinkDataLength长度相等

数据包在不同层级中进行处理的的时候,需要调整 pktuLinkDataPtr 指针。例如一个ICMP请求包,该数据包有3层:Ethernet、IPv4和ICMP。在以太网层处理过程中( tfEtherRecv ),pktuLinkDataPtr字段指向的是以太网头部,当下一层处理的时候,该字段和一些其他字段将会做以下调整:

在这个例子中,以太网包头长度为0xE,所以pktuLinkDataPtr指针向前调整0xE,而长度字段则减少0xE。在接下来,IPv4层( tfIpIncomingPacket )开始处理数据包,pktuLinkDatatr此时指向的是IP头部。Treck协议栈通过在tfIpIncomingPacket调用tfIpReassemblePacket函数来重组分片,每接受到一个分片,该函数将该分片插入到链表中,链表之间通过pktuLinkNextPtr指针连接起来:

如果分片有遗漏,那么最终返回Null;如果没有,那么该函数将会把分片链表交给下一层去处理。

CVE-2020-11896漏洞原理

下图是IP头部的结构:

其中4字节到8字节表示的是IP分片相关信息,包括表示,Flags和Fragment Offset。而前4字节则有两个重要字段:IP Header Length:IP头部长度Total Length:IP数据包总长度

接着输入理解一下tfIpIncomingPacket函数的实现,该函数首先对数据包进行一些简单的check:

接下来检查IpTotalLength是否小于等于pktuChainDataLength字段,这意味着实际接收到的数据比IP头部标识的IP数据包总长度要长,在这种情况下,就对多余数据进行裁剪:

裁剪(trimming)的方式很简单,就是将pktuChainDataLength和pktuLinkDataLength设置为ipTotalLength的长度。注意,漏洞就在这个裁剪这里。回顾一下先前所说:pktuChainDataLength代表的是整个数据包的长度,pktuLinkDataLength代表的是当前分片的长度,如果上述操作成功,那么就会出现: pktuLinkDataLength = pktuChainDataLength = IpTotalLength 可是如果这个时候,如果pktuLinkNextPtr还是指向其他的分片呢,那么就会存在 不一致现象 ,这种不一致的现象将会导致后续处理数据包产生错误。但是还是有问题,因为在tfIpIncomingPacket函数处理中,首先进行的是trimming操作,接着调用tfIpReassemblePacket对根据pktuChainDataLength大小,建立分片链表,而在tfIpReassemblePacket函数中并不会将分片数据包复制到buffer中。也就是说,先进行trimming操作,接着调用tfIpReassemblePacket建立分片链表,最终将分片链表返回给下一层进行处理,而下一层并不会再次调用 tfIpIncomingPacket 。这种情况下,上述不一致现象实际上并不能利用。

使用IP隧道

为了使得分片在IP层被处理,并且可以到达脆弱代码位置,我们使用IP隧道。tfIpIncomingPacket函数在处理内层IP数据包时,将之作为没有分片的数据包进行处理,也就是说MF标志位=0,此时将不会调用tfIpReassemblePacket函数进行处理。这个tfIpIncomingPacket函数将会在两个地方被调用,一次是在内层的IP packet(没有分片,只调用一次),一次是在外层的ip packet(多次,每个分片调用一次)。在这个处理过程中, tfIpIncomingPacket 首先将会接收所有的外层ip分片,对于每个分片,都会调用 tfIpReassemblePacket 函数。在接收全部的分片之后,将会进入下一网络层的处理,这里由于 ip-in-ip ,tfIncomingPacket函数将会被本身所递归调用,去处理内层ip数据。ip-in-ip结构如下图所示:

考虑如下这个场景:内层IP数据包:IPv4{len=32, proto=17}/UDP{checksum=0, len=12}  payload为:’A’*1000外层IP数据包(分片1):IPv4{frag offset=0, MF=1, proto=4, id=0xabcd} 外层IP数据包(分片2):IPv4{frag offset=40, MF=0, proto=4, id=0xabcd}整体数据包及分片情况如下图所示:

当tfIpIncomingPacket函数(该函数被处理外层数据包的tfIpIncomingPacket所递归调用)处理内层IP数据包的时候,此时已经完成了分片的重组,这两个ip分片被 tsUserPacket->pktuLinkDataPtr 所连接起来。那么接下来在该函数接下来的流程中,内层IP数据包的 total length(32) ,是小于 pktuChainDataLength(1000+ 8 + 20 = 1028) 的,进入trimming分支进行裁剪操作,将pktuChainDataLength设置为32。

if ((uint)ipTotalLength <= pkt->pktuChainDataLength) {    if ((uint)ipTotalLength != pkt->pktuChainDataLength) {         pkt->pktuChainDataLength = (uint)ipTotalLength;         pkt->pktuLinkDataLength = (uint)ipTotalLength;    }}


现在我们新的问题出现了:如何将这个不一致问题( inconsistency )转为一个内存破坏( memory corruption )?


UDP 2.3.2中的heap overflow

在UDP数据处理中,可以确定的是,至少有一条代码路径是将IP分片复制到自定义的buffer里面,那么就存在漏洞的可能性。这个过程需要malloc一个堆空间,堆空间的大小取决于 pktuChainDataLength 字段,然后将ip分片复制到heap中。做复制这个事情的函数是 tfCopyPacket ,该函数逻辑抽象如下:

可以看到,这个memcpy的过程并不考虑长度。而堆块本身的大小是取决于 pktuChainDataLength ,这个字段在先前漏洞trimming被触发后,实际上是小于实际IP数据包总大小的,heap overflow就这样出现了。接下来是一些UDP本身字段的校验,这部分就不再详细叙述了,为了解决接收队列非空的要求,还需要快速的发数据包保持接受队列中存在数据包。

CVE-2020-11896漏洞利用

接下来作者利用 Digi Connect ME 9210 进行了验证,证明可以做到远程代码执行。这个设备如下图所示:


这是一个极小的嵌入式设备,用于完成串口到以太网的转化,串口常包括SPI、I2C和CAN等,里面有嵌入式的ARM CPU,有一个NET+OS操作系统。本次实验的目标是通过远程执行shellcode,将开发板上面的LED灯点亮。

exploit编写策略

heap overflow的利用大概有两种方式:

  1. 覆盖堆中的meta-data信息。所谓 meta-data 就是堆中的元信息,包括堆块的大小、空闲位的标志等等堆本身自带的信息,这种利用方式和libc pwn中的 off by one 原理类似,溢出到下一个堆块的重要信息。

  2. 覆盖堆上面的数据结构。这种攻击方式是希望堆上面存着一些数据结构,该结构中存着一些函数指针可以被覆盖。

第二种利用方式与特定应用程序相关,第一种则适用面更加广泛,本文选择第一种利用方式。

理解Treck heap的内部实现

Treck中实现了一套自己的堆分配机制,在内部使用的是固定大小的堆分配模式,一个分配单元被称之为 bucket 。在Treck实现中,有如下几种固定大小:

而释放后的bucket,有自己对应的free buckets list,每次释放一个bucket后,就将其插入到对应大小list的头部,这里可以类比于ptmalloc中的 tcache机制 。在Treck协议栈中通过 tfGetRawBuffer ,该函数参数为一个4字节大小的size,返回一个指向分配内存的指针,这个分配的内存我们称之为 raw buffer raw buffer 的释放通过 tfFreeRawBuffer 。分配的过程就是从对应空闲链表中取出一个堆块,然后转为 ttRawBufferPtr 类型指针返回给用户:

如果空闲链表中没有空闲的bucket,那么就调用 tfBufferDoubleMalloc 分配一个新堆块返回给用户:

释放的时候将其插入到对应的空闲链表中去


可以看到,空闲bucket插入到空闲链表后,将会利用 rawNextPtr 指针链接起来,rawNextPtr指向上一个空闲bucket,结构如下图所示:


到目前为止,我们搞清楚了Trec中的堆结构,堆的分配和释放过程,特别是空闲bucket也是类似ptmalloc一样通过链表利用堆中的指针链接起来的。那么我们自然可以想到,如果可以覆盖掉 rawNexPtr
指针为可控值,就可以做到任意地址分配了:


就像上图所示,通过两次分配之后,我们就可以在栈上分配一个可控的堆块。这种攻击方式,在libc Pwn中是非常常见的,例如fastbin attack打malloc_hook改为one_gadget。这个嵌入式设备是ARM v9,并且这个堆实现过程基本没有check,甚至没有开NX,最后白皮书中采取的方法也是通过ROP跳到shellcode来做到RCE的。目前笔者手头还没有实际运行Treck的设备,所以没法实际动手调试和写exp,运行效果可以看演示效果【1】【2】。

总结

  1. Ripple 20存在于Treck TCP/IP协议栈中,该协议栈广泛存在于嵌入式设备中,波及的行业包括:工业设备、电力设备、企业网络设备、交通、能源等等,而上述设施我们称之为:关键基础设施

  2. Ripple 20不好修补,首先由于供应链的广泛传播,漏洞存在形式又很底层,所以排查设备是否运行Treck TCP/IP协议栈容易疏漏。其次虽然Treck官方已经提供了最新稳定版本6.0.1.67,但是这种基础协议栈软件的更新还是比较麻烦的

  3. 从白皮书披露来看,漏洞利用技术并不十分复杂,所以漏洞利用门槛可能不会很高,例如这个漏洞,即使无法做到RCE,至少远程令设备crash是比较容易做到的,而关键基础设施的crash也已经算是大问题了

引用:【1】 https://www.jsof-tech.com/ripple20/ 【2】https://mp.weixin.qq.com/s/2F1-35HIk126crowAh9LLw



相关实验:

TCP攻击实例分析

https://www.hetianlab.com/expc.do?ec=ECID73dc-e03a-4ad3-8312-53fb6b3a4499

(通过该实验了解SYN-Flooding攻击,RST攻击,TCP会话劫持,并通过会话劫持拿到服务器shell 权限。)



欢迎投稿至邮箱: edu@heetian.com

有才能的你快来投稿吧!

投稿细则都在里面了,点击查看哦

重金悬赏 | 合天原创投稿涨稿费啦!




点击这里提升自己