浅谈zip格式处理逻辑漏洞

前言:zip压缩格式应用广泛,各个平台都有使用,Windows平台使用来压缩文件,Android平台使用来作为apk文件的格式。由于zip文件格式比较复杂,在解析zip文件格式时,如果处理不当,可能导致一些有意思的逻辑漏洞,本篇文章将挑选有意思的漏洞进行解析。

一、文件扩展名欺骗漏洞

很早之前,国外安全研究人员爆料Winrar 4.x版本存在文件扩展名欺骗漏洞(https://www.exploit-db.com/papers/32480/),黑客可以通过该漏洞诱骗受害者执行恶意程序。该漏洞的主要原理是:Winrar在文件预览和解压缩显示文件名使用的是不同结构体的字段导致的。

1.1 zip格式文件的结构

在了解漏洞的原理前,先熟悉下zip格式的文件结构。

如果一个压缩包文件里有多个文件,可以认为每个文件都是被单独压缩,然后再拼成一起。

一个 ZIP 文件由三个部分组成:压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志,如下图:

1)文件头(压缩源文件目录区)在文件末尾,即图1中的File Header,记录了索引段的偏移、大小等等。

2)数据段(压缩源文件数据区)在文件开头,即图1中的Local Header,记录了数据的一些基本信息,可以用来跟File Header中记录的数据进行比较,保证数据的完整性。

3)Local Header还包含了文件被压缩之后的存储区,即图1中的Data区域。

4)图2和图3为Local Header(图2中的ZIPFILERECORD)和File Header(图3中的ZIPDIRENTRY)的数据对比,两者数据是一致的。

1.2 漏洞产生原因

Winrar在文件预览的时候使用的是ZIPDIRENTRY下面的deFileName字段来显示文件名,解压缩的时候使用的是ZIPFILERECORD下面的frFileName字段来显示文件名。如果将deFileName字段文件扩展名改成jpggif等图片的文件扩展名,可以欺骗用户运行恶意程序。

Winrar文件预览示意图:

用户看到的是jpg图片,打开的确实exe文件,真坑啊!

Winrar解压缩文件示意图:

解压缩之后显示的exe,两处显示的不一样。

二、Android Master Key漏洞

之前,国外安全研究人员爆出第三个Android Master Key漏洞(http://www.saurik.com/id/19),该漏洞的主要原理是:android在解析Zip包时,没有校验ZipEntryHeader中的FileNameLength是否一致。

2.1 zip文件格式的结构

在了解漏洞的原理前,还是先熟悉下zip格式的文件结构。

如果一个压缩包文件里有多个文件,可以认为每个文件都是被单独压缩,然后再拼成一起。

一个 ZIP 文件由三个部分组成:压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志,如图1所示:

1)文件头(压缩源文件目录区)在文件末尾,即图1中的File Header,记录了索引段的偏移、大小等等。

2)数据段(压缩源文件数据区)在文件开头,即图1中的Local Header,记录了数据的一些基本信息,可以用来跟File Header中记录的数据进行比较,保证数据的完整性。

3)Local Header还包含了文件被压缩之后的存储区,即图1中的Data区域。

4)图2和图3为Local Header(图2中的ZIPFILERECORD)和File Header(图3中的ZIPDIRENTRY)的数据对比,两者数据是一致的。

2.2 漏洞产生原因

先来看一下是如何定位到Local Header中的Data数据:

off64_t dataOffset = localHdrOffset + 
                     kLFHLen + 
                     get2LE(lfhBuf + kLFHNameLen) +

Data的偏移是通过Header的起始偏移+Header的大小(固定值)+Extra data的大小+文件名的大小,如下图

回头看一下,java在获取Data偏移的处理,在读取Extra data的长度的时候,它已经预存了文件名在FileHeader中的长度。

// We don't know the entry data's start position. 
// All we have is the position of the entry's local 
// header. At position 28 we find the length of the 
// extra data. In some cases this length differs 
// from the one coming in the central header. 

RAFStream rafstrm = new RAFStream(raf, 
         entry.mLocalHeaderRelOffset + 28); 
DataInputStream is = new DataInputStream(rafstrm); 
int localExtraLenOrWhatever = 

漏洞就在这里产生了,如果Local Header中的FileNameLength被设成一个大数,并且FileName的数据包含原来的数据,File Header中的FileNameLength长度不变,那么底层C++运行和上层Java运行就是不一样的流程。

C++ Header 64k Name Data 
+--------> +----------------------> +----------> 
length=64k classes.dex dex\035\A... dex\035\B... 
+--------> +---------> +----------> 

如上面所示,底层C++的执行会读取64k的FileName长度,而Java层由于是读取File Header中的数据,FileName的长度依旧是11,于是Java层校验签名通过,底层执行会执行恶意代码。

原文发布于微信公众号 - 我为Net狂(dotNetCrazy)

原文发表时间:2015-11-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木木玲

Netty in action ——— 异步和事件驱动

23080
来自专栏Java技术栈

Jodd - Java界的瑞士军刀轻量级工具包!

37220
来自专栏SpringBoot 核心技术

第四十三章: 基于SpringBoot & RabbitMQ完成TopicExchange分布式消息消费

434150
来自专栏龙首琴剑庐

原 微服务Spring Cloud Eur

18140
来自专栏NetCore

AppFuse项目笔记(1)

AppFuse项目笔记(1) 一、Appfuse简介 Appfuse是Matt Raible 开发的一个指导性的入门级J2EE框架,它对如何集成流行的Sprin...

34450
来自专栏Java后端生活

JavaWeb(七)JSP-2

17960
来自专栏逆向技术

调试器第二讲,单步步入/步过功能实现,以及基本的断点功能实现

           调试器第二讲,单步步入/步过功能实现,以及基本的断点功能实现 昨天,我们实现了调试器的基本框架,那么今天我们实现单步功能,还有断点功能,以...

25760
来自专栏性能与架构

Spring 5 响应式开发示例

24010
来自专栏开源优测

[接口测试 - 基础篇] 12 还是要掌握python日志管理模块的

python logging模块介绍 Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块提供不同的...

38380
来自专栏Java 源码分析

SpringBoot 笔记 ( 三 ):日志系统

SpringBoot 笔记 ( 三 ):日志系统 1、日志框架 日志框架就是防止我们再去像以前那样,一直进行System.out.println(“”)将关键数...

78550

扫码关注云+社区

领取腾讯云代金券