由于图像格式多种多样,考查方式五花八门,因此CTF比赛中经常会出现图像隐写题目。本节将介绍PNG、JPG和GIF这三种常见的文件格式,并重点讲解“图像隐写三板斧”的解题思路。
图像中的隐写术大致分为两类:插入隐藏和替换隐藏。
插入隐藏 是指利用文件的特性与文件格式的冗余性,在不影响载体正常使用的情况下,将秘密信息插入到载体中,实现信息隐藏。这种方法一般会导致载体文件的大小发生变化。根据秘密信息插入的位置,插入隐藏又可分为追加插入法和前置插入法。
追加插入法 是隐写术中最常用、最简单的一种方法,通常是在文件末尾附加数据。其基本原理是:对于Windows系统而言,根据文件后缀解析载体文件,大部分文件格式都有文件结束标识,在文件结束标识后附加的数据不会被解析,也不会影响载体文件的使用;对于Linux系统,其文件解析方法与Windows相似,只是根据文件头识别文件类型而不是根据后缀来识别。
【练习】文件拼接
现在有两个文件lizi.jpg和test.zip,在Windows系统下使用copy命令对其进行拼接,如图2.1.1所示,并生成新文件litest.jpg。双击新文件,会打开一张图片,它和lizi.jpg完全一样。把新文件litest.jpg重命名为litest.zip,解压后得到的内容和test.zip中的内容完全一样。语句“copy/b lizi.jpg+test.zip litest.jpg”将test.zip压缩包拼接到lizi.jpg文件结尾,通过修改后缀名可以按照不同文件格式对其进行解析。读者可以考虑一下,为什么后缀改为.zip后,原JPG文件内容对压缩包解压没有影响呢?
图2.1.1 使用copy命令拼接文件
前置插入法 是利用文件头部的冗余部分进行插入,且丝毫不影响原始文件的使用效果。冗余部分可以是批注内容、注释信息等,一些文件格式在设计时都会保留一块数据作为批注信息,此类信息修改后对原始文件不会产生影响。前置插入法的使用会在JPG格式中详细讲解。
元数据 (Metadata)是描述数据的数据,主要用于描述数据的 属性 (property),例如数据的名称、大小、数据类型、拥有者等。在元数据中隐藏信息是CTF比赛中的一种基本手法,通常会隐藏一些关键的提示信息。元数据一般可以通过单击鼠标右键,再单击“属性”去查看,也可以通过strings命令查看。实际上,元数据中隐藏的信息常常位于文件头部或者尾部,其本质仍然是插入隐藏。
替换隐藏 也是一种主要的隐写方法,它通过修改载体文件的数据,将原始载体内不重要的数据替换成所要隐藏的信息,若只修改部分字节(比特),人眼几乎无法发现原图像发生了变化。最典型的替换隐藏方法是 最低比特 (Least Signif icant Bit, LSB) 隐写 。替换隐藏较插入隐藏更为隐蔽,是CTF比赛中的一个常见考点。
为了说明LSB隐写的原理,我们先介绍一些关于图像的基础知识。
图像由像素组成,在黑白图像或灰度图像中,每个像素只有一个像素值;在彩色图像中,每个像素可以有多个像素值。像素值是0~255之间的整数,0代表黑色,255代表白色,像素值越大表示亮度越高。在图像中,一个像素值通常用8比特表示,其最高位对图像的影响最大,最低位对图像的影响最小,最低位就是最低比特。8比特一般用1字节表示,最低比特就是一个字节的最右位置的比特。将图像中所有像素的比特按位置抽取出来,就构成了8个不同的 位平面 (Bit Plane)。
LSB隐写的原理是 ,在图像中随机选择区域,提取该区域像素点的像素值,将要隐藏的信息转换为二进制,逐比特替换该区域像素值的最低位,从而实现秘密信息的嵌入。通过此种方式嵌入的信息,人眼几乎无法识别,原文件大小也没有变化。图2.1.2给出了LSB隐写过程的一个例子,提取一张图像中某个3×3区域像素点的像素值,并转为二进制,嵌入信息“011010”,像素值发生微小改变;当接收方收到图像后,从对应区域像素点的像素值最低位提取信息,就能恢复出“011010”。
图2.1.2 LSB隐写的过程
目前,已经有很多关于图像隐写的学术研究工作,上面提及的方法均属于空域隐写方法,此外,还有基于变换域的隐写方法(DCT域)等,限于篇幅,我们就不一一介绍了,这里只介绍一些适合初学者快速入门且比赛中经常出现的隐写方法。
PNG(Portable Network Graphics,可移植网络图像)是一种 位图文件 (Bitmap File)存储格式,既可以存储灰度图像(只有黑白灰,没有彩色),也可以存储彩色图像。PNG使用从LZ77派生的无损算法压缩图像信息,能获得较高的压缩比,并利用特殊的编码方法标记重复出现的数据,因而对图像的颜色没有影响,也不可能产生颜色的损失,这样就可以重复保存而不降低图像质量。
PNG图像格式文件由文件头(PNG Signature)和数据块(Chunk)组成。文件头总是由固定的8字节来表示,即 89 50 4E 47 0D 0A 1A 0A ,如果记不住这8字节的内容,至少要记住前4字节。PNG图像整体格式如下。
PNG定义了两种类型的数据块,一种为关键数据块(Critical Chunk),另一种为辅助数据块(Ancillary Chunk),辅助数据块是可选的。每个数据块都由4部分组成,如表2.1.1所示。
表2.1.1 PNG中数据块的数据结构
数据块类型码(Chunk Type Code)既说明数据块类型,也是数据块对应的名字。CRC是对数据块类型码和数据域进行计算得到的结果,注意,不包括长度字段。常见的数据块类型如表2.1.2所示。
表2.1.2 PNG常见的数据块类型
我们重点介绍关键数据块中的4个标准数据块:IHDR、IDAT、PLTE和IEND。
IHDR(Header Chunk)是文件头数据块,包含PNG文件中存储的图像数据的基本信息。它由13字节组成,并作为第一个数据块出现在PNG图像中,且一个PNG图像中只能有一个IHDR。IHDR的格式如表2.1.3所示。
表2.1.3 IHDR的格式
(续)
IDAT(Image Data Chunk)是图像数据块,用于存储实际的像素值数据。在数据流中可包含多个连续顺序的图像数据块,采用LZ77算法的派生算法进行压缩,可以用zlib对IDAT解压。值得注意的是,对于IDAT块,只有当前一个块充满时,才会继续一个新的块。当IHDR中的颜色类型为真彩色图像时,IDAT中保存的是每个像素的RGB值。如果颜色类型是索引彩色图像,则PNG图像中必须有PLTE块,IDAT中存放的是PLTE调色板颜色索引数据。
PLTE(Palette Chunk)是调色板数据块,包含与索引彩色图像(Indexed-Color Image)相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(IDAT)之前。真彩色的PNG数据流也可以有调色板数据块,目的是方便非真彩色显示程序用它来量化图像数据,从而显示该图像。在大部分比赛中,PNG图像没有该数据块,如果出现,则值得关注。
IEND(Image Trailer Chunk)是结束数据块,用来标记PNG文件的结束,是所有数据块的最后一块,必须放在文件尾部。标准的IEND块数据占12字节,即00 00 00 00 49 45 4E 44 AE 42 60 82。根据数据块结构的定义,IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据块类型总是IEND(49 45 4E 44),因此,CRC也总是AE 42 60 82。
使用010 Editor解析PNG图像的结果如图2.1.3所示。除了PNG文件头,所有数据块构成一个数组,chunk[0]就是IHDR块,大家要熟悉PNG的解析结果。
为了方便读者更容易理解后续内容,我们对图像中的一些基础概念再做一些补充说明。
图像深度 是指像素值由几个比特的数据来表示。如果图像深度是8,也就是用8比特无符号整数表示一个像素值,那么像素值的范围是0~255。
图2.1.3 用010 Editor解析PNG图像
通常将图像分为 单通道、三通道、四通道 。单通道也就是通常所说的灰度图,每个像素点的像素值只用一个值表示。三通道也就是通常所说的彩色图,每个像素点有三个像素值。在RGB色彩模式下,一幅完整的图像是由红色、绿色、蓝色三个通道组成的。四通道是在三通道图像基础上再加上alpha通道组成的,表示图片的透明和半透明度,当用8比特表示时,0表示完全透明,255表示完全不透明。很多情况下,alpha通道的值是全0。
好了,掌握了上述知识后,现在假设我们是出题人,结合上述PNG格式信息,在PNG图像中可以设置什么样的隐写题目呢?
1)显然,对于任何文件都可以做追加插入隐藏,针对PNG的追加插入隐写在题目中经常见到。
2)由于在文件头部不存在冗余设计,因此在目前的CTF比赛中,PNG几乎不存在前置插入隐藏,但是考虑到在tEXt块中可以保存文本信息且对位置没有要求,这意味着在PNG头部也能插入秘密信息,只是较为少见。
3)PNG采用无损压缩方法保存像素值信息,也就意味着通过修改像素值可以实现LSB隐写,这也是目前考查的一个重点,尤其针对彩色图像。PNG图像像素一般是由RGB三原色(红绿蓝)组成的,alpha通道默认为全0,每一种颜色占8比特,一个像素点可以有256×256×256=16 777 216种颜色,而人类的眼睛可以区分约1000万种不同的颜色,也就是说还有很多种颜色(即便像素值不同)人眼无法区分,这就意味着,单纯通过人眼无法识别图像中是否存在LSB隐写。假设在每个像素的RGB值的最低位已经隐写消息,如图2.1.4所示,那么在提取时就会涉及顺序问题,即每个像素点提取出的3比特信息应该按照怎样的顺序排列?或者,发送方在红色通道次低位和蓝色通道最低位隐写了信息,那么攻击者该如何发现并提取?对于提取出的比特信息如何进行排列组合也是PNG隐写中的难点。
4)通过PLTE块也能实现信息隐写,调色板数据块没有顺序要求,因此,以调色板保存颜色时,可以对信息进行编码从而实现信息隐藏;也可以改变调色板中颜色的顺序,使相近的颜色相邻,然后进行LSB替换,实现消息的隐写。调色板隐写的算法很多,属于较为高级的隐写方法。
图2.1.4 彩色图像中的LSB隐写
5)如果图片本身非常模糊,甚至呈现“雪花状”,那么可以利用PNG的alpha通道隐写信息。
6)其他。
通过剖析出题人出题时的心路历程,不难发现PNG图像的隐写方式灵活多样,因而深受广大出题人的喜爱。读者在遇到PNG图像隐写题目时,务必小心谨慎。
JPEG(Joint Photographic Experts Group)是一种面向连续色调静止图像的压缩标准,该标准由国际标准化组织(ISO)制定。JPEG格式是最常用的图像文件格式,也称为JPG格式,其文件后缀名为.jpg或.jpeg。JPG格式没有直接保存图像的像素值信息,而是将其转换为YUV色彩空间(Y表示像素的亮度,U和V一起表示色调与饱和度)保存到图像信息中,保存时既可以选择无损压缩也可以选择有损压缩。
JPG文件按照“段”的格式来组织存储,一个文件由多个段组成,每个段代表不同的信息。同时,每个段也有自己唯一的标识符。标识符由两个字节组成,格式形如0x FF XX,其中XX代表不同的类型。例如,SOI(Start Of Image)表示图像的开始,其段的标识符为0X FF D8。整个JPG图片的组织便是由这些不同类型的段和经过JPG压缩后的数据组成的。常见JPG图片各段的说明如表2.1.4所示。
表2.1.4 JPG图像常见数据段
每个JPG段的结构如表2.1.5所示。
表2.1.5 JPG段的结构
(续)
JPG的隐写题目具有两极化的特点,要么比较简单,要么非常复杂,需要专门的工具才能完成。让我们再化身为出题人,看看JPG图像隐写中的“套路”。
❑ 套路1: 针对JPG图片可以采用追加插入法隐写信息,这里不再赘述。
❑ 套路2: 采用前置插入法隐藏信息。这里我们先介绍Exif。Exif(Exchangeable Image File)即可交换图像文件,本质上是JPG文件的一种,其设计遵从JPEG标准,只是在文件头信息中增加了有关拍摄信息的内容和索引图。Exif可以附加于JPEG、TIFF、RIFF等文件之中,为其增加有关拍摄的详细信息和处理软件的版本信息等。利用exiftool.exe可以查看和修改文件的Exif信息,而新增的Exif数据一般存储在JPG图像的APP或COM段。
【例题】cat.jpg
【题目来源】 2017 Xman选拔赛。
【题目描述】 找到文件中隐藏的信息。
【解题思路】 利用010 Editor查看文件信息,如图2.1.5所示,文件头部的APP1和COM段插入了字符串,将两个字符串进行Base64解码与拼接,就得到flag{U5e_3x1ftoo1}。利用记事本打开图片,也能在文件头部发现这两个字符串,如图2.1.6所示;或者使用exif工具exiftool.exe也能看到相关信息,如图2.1.7所示。
图2.1.5 利用010 Editor解析JPG图像
图2.1.6 利用Notepad++查看图像
图2.1.7 利用exiftool.exe查看图像Exif信息
❑ 套路3: JPG图像没有保存RGB信息,所以一般不考虑像素值LSB隐写。但是,针对JPG有很多专门的隐写算法和工具,这里介绍一些常见的JPG隐写工具,大部分工具从JPG提取信息时需要使用密钥,这也是JPG隐写题目的特点。
1.JSteg隐写算法
该算法的原理是:使用JPEG图像量化后的离散余弦变换(Discrete Cosine Transform,DCT)系数的最低比特来承载秘密信息。在信息嵌入过程中,第一步是需要确定的就是当前位置的DCT系数不能为0或者1,不满足该条件则跳过,若满足条件则进行下一步操作;第二步是判断该位置的DCT系数的最低比特是否与秘密信息的比特数值相同,相同的话就结束本次操作,不相同就使用秘密信息比特替换。
2.OutGuess隐写算法
JSteg算法能够被卡方检验发现,为解决该弊端,人们提出了新的OutGuess隐写算法。OutGuess算法的创新点在于,在进行信息嵌入后增加了纠正操作。OutGuess的信息嵌入过程和JSteg完全一样,纠正阶段会修改DCT系数以消除在嵌入过程中造成的统计特征的变化。OutGuess 0.2中加入了用户密钥设置,在整幅图像中根据密钥的不同按照一定的原则挑选DCT系数进行修改,使得嵌入的信息随机化,难以被检测出来。其官方网站为https://github.com/eribertomota/outguess。
3.Jphide隐写算法
Jphide隐写算法使用Blowf ish算法加密信息,利用控制表确定信息的隐藏位置及嵌入秘密的方式,同时结合Blowf ish生成的随机序列控制具体位置的嵌入。隐写过程大致为:先解压JPG图像,得到DCT系数;然后根据用户给定的密码对隐藏信息进行Blowf ish加密;再利用Blowf ish算法生成伪随机序列,并据此找到需要改变的DCT系数,将其末位变为需要隐藏的信息的值;最后,把DCT系数重新压缩成JPG图片。这种方法隐蔽性更强,更不容易被检测到。
4.JPHS隐写工具
JPHS(JPEF Hide&Seek)隐写工具采用Jphide算法隐写信息,软件里主要包含两个程序:JPHIDE和JPSEEK。JPHIDE将信息文件加密隐藏到JPEG图像中;针对经JPHIDE程序加密隐藏得到的JPEG图像,JPSEEK可以从其中探测并提取信息文件。Windows版本下的JPHSWIN程序具有图形化操作界面且包含JPHIDE和JPSEEK的功能。该工具的官网为http://io.acad.athabascau.ca/~grizzlie/Comp607/programs.htm。
5.Steghide隐写工具
Steghide隐写工具是一种基于图匹配的隐写算法,通过交换像素完成嵌入,因而可以保证隐写后的图像与原图像相比,某些统计特征不发生变化。为了提高像素交换的可能性,Steghide算法在 k 个像素中嵌入一位 m 进制数,从而有 k 种可能的交换供选择。Steghide算法的嵌入过程可以看作一个寻找最大匹配的图论问题。该工具的官网为https://github.com/StefanoDeVuono/steghide。
6.Stegdetect工具
Stegdetect是一个针对JPG隐写算法的自动化检测工具,可以检测图像中是否存在JSteg、OutGuess、Jphide等算法隐写的信息,它通过命令行交互,同时包括Stegbreak功能,Stegbreak可以通过字典对信息嵌入前被加密时的密钥进行爆破。该工具的官网为http://www.outguess.org/download.php。
【例题】can_U_f ind_f l4g.jpg
【题目描述】 找到文件中隐藏的信息。
【解题思路】 利用Stegdetect工具检查隐写算法,如图2.1.8所示。stegdetect.exe的使用方法如下:
❑-s修改检测算法的敏感度,默认值为1。检测结果的匹配度与检测算法的敏感度成正比,算法敏感度的值越大,检测出的可疑文件包含敏感信息的可能性越大。
❑-d打印带行号的调试信息。
❑-t设置要检测哪些隐写工具(默认检测jopi),可设置的选项如下:
● j检测图像中的信息是否用JSteg嵌入的。
● o检测图像中的信息是否用OutGuess嵌入的。
● p检测图像中的信息是否用Jphide嵌入的。
● i检测图像中的信息是否用Invisible Secrets嵌入的。
图2.1.8 利用stegdetect.exe检测JPG图像
检测结果显示,JPG图片中使用Jphide算法隐写了信息,星号表示概率,3个星号说明概率最大。利用Jphide算法隐写信息时需要使用密钥对信息加密,于是我们使用stegbreak.exe爆破密钥,如图2.1.9所示。
图2.1.9 利用stegbreak.exe爆破密钥
stegbreak.exe的使用方法如下:
❑-r包含应用于单词列表中单词的转换规则。这些规则遵循与John The Ripper相同的语法。默认为rules.ini。
❑-f指定包含用于字典攻击的文件。
❑-t设置需要检测的算法,和stegdetect.exe中的含义完全一样。
当攻击成功时,stegbreak.exe会打印文件名、嵌入系统和密钥。对于JSteg和OutGuess,它还会打印来自内置文件实用程序的分析结果。
小括号中的“flag”就是隐写时的密钥,我们利用JPHS提取隐写信息,如图2.1.10所示,单击“Seek”按钮,输入两遍密码“flag”,再选择保存路径,打开提取出的文件就可以找到flag{Hi_I_4m_H3r3!}。
GIF(Graphics Interchange Format,图形互换格式)是一种以 超文本标记语言 (Hypertext Markup Language)方式显示的索引彩色图像文件格式,已在互联网上得到广泛应用。GIF采用的是Lempel-Zev-Welch(LZW) 无损压缩 算法,最高可支持256种颜色。由于具有这种特性,GIF适用于色彩较少的图片,例如卡通造型、公司标志等。如果遇到需要用真彩色的场合,那么GIF的表现力就有限了。GIF文件格式可在一个文件中存放多幅彩色图像,它们可以像幻灯片那样显示或者像动画那样演示,平时看到的动图和微信表情包都是GIF格式的文件。
图2.1.10 利用JPHS提取隐藏信息
一个GIF文件的结构可分为 文件头 (File Header)、 GIF数据流 (GIF Data Stream)和 文件结尾 (Trailer)三个部分,GIF文件结构如表2.1.6所示。
表2.1.6 GIF文件结构
如果GIF中有多张图片,格式如图2.1.11所示,则图像部分会出现多次循环。
图2.1.11 GIF文件格式
文件头包括署名和版本,署名为 47 49 46 ,其对应的ASCII码是“GIF”;版本号也由3字节组成,可以为“ 87a ”或“ 89a ”。
逻辑屏幕标识符紧跟在文件头之后,这一部分由7字节组成,定义了GIF图像的大小、颜色深度、背景色以及有无全局颜色列表和颜色列表的索引数。
全局颜色列表不是必需的数据块,如果出现,则会紧跟在逻辑屏幕标识符后面。每个颜色列表索引条目由3字节组成,按R、G、B的顺序排列。
图像标识符位于具体的图像数据前,第1个字节为0x2C。一个GIF文件中可以有多个图像块,每个图像块都会有图像标识符,每个图像标识符都由10字节组成,它包括图像相对于逻辑屏幕边界的偏移量、图像大小以及有无局部颜色列表和颜色列表大小等信息。
图像数据使用LZW压缩算法存储图像信息,如果想从GIF中提取全部图像,需要对这一部分进行解压并提取,提取出的图像可以保存成PNG、BMP等格式。
在89a版本中,GIF添加了图形控制扩展块,它是可选的,可以放在一个图像块或文本扩展块的前面,用来控制跟在它后面的第一个图像(或文本)的渲染形式。这一部分有一个“延迟时间”字段,其单位为1/100s(也就是10ms),如果“延迟时间”字段的值为 n ,则表示暂停10 n ms后再继续处理数据流。
在89a版本中,注释扩展是可选的,用来记录图形、版权、描述等非图形和控制的纯文本数据(7比特ASCII字符)。注释扩展并不影响对图像数据流的处理,解码器完全可以忽略它。它可以存放在数据流的任何位置。
文件结尾是一个字节的固定值0x3b,用来指示整个文件的结束。
在CTF比赛中,GIF也是高频考点。我们重点介绍三种常见的GIF隐写方法:
1)追加插入法隐写,就是在GIF文件后插入其他文件。这种隐写非常容易识别。在010 Editor中利用GIF模板进行解析,如果文件结尾后还有其他数据流,那么很有可能在GIF后附加了其他文件,将附加数据提取出来做进一步分析即可。
2)基于图像的隐写。GIF中可以包含多个图像,出题人可以在某幅图像上隐写信息。我们需要分离出GIF的每幅图片,并针对每幅图片进一步分析。
【例题】Basic.png
【题目来源】 2019中石油集团决赛。
【题目描述】 找到文件中隐藏的信息。
【解题思路】 把图像放入010 Editor,根据文件头“GIF89a”,发现文件应该是GIF文件,但原文件后缀错误。修改后缀后,使用工具包中的工具gifsplitter.exe分离GIF中的图像,得到三幅图片,在第2幅图片的底部看到flag{cad39e0e-46cf-498e-a971-84863365f13a}。
3)基于时间的隐写。这种方式非常隐蔽,因为GIF中的每幅图像都会有延迟时间,可以约定不同的延迟时间代表不同的含义,例如延迟100ms代表1,延迟50ms代表0,从而达到传输秘密信息的目的。
【例题】SimpleGIF.gif
【题目来源】 2017 Xman选拔赛。
【题目描述】 找到文件中隐藏的信息。
【解题思路】 GIF文件不能正常打开,将其拖入010 Editor查看,发现缺少头部数据。根据GIF文件头格式,GIF的文件头是“GIF87a”或者“GIF89a”。由于“89a”版本是对“87a”版本的扩充,当不确定GIF版本时,统一按“89a”版本处理即可。因此,我们在头部插入6字节:47 49 46 38 39 61。
这里我们详细讲解一下如何利用010 Editor在文件中插入数据。在要插入字节处单击鼠标左键,光标会在该位置闪烁,如图2.1.12所示。在“Edit”选项卡中选择“Insert/Overwrite”,再选择“Insert Bytes”,如图2.1.13所示。单击“Insert Bytes”,出现如图2.1.14所示的窗口。“Start Address”表示字节插入的位置,因为我们要在文件起始位置插入,所以偏移值是0,这个值会根据光标的位置自动修改。在“Size”处要填写插入的字节数,此时要填6,然后单击窗口右下角的“Insert”,结果如图2.1.15所示。
图2.1.12 光标置于要插入字节处
图2.1.13 通过“Edit”选项卡插入字节
图2.1.14 插入字节设置窗口
图2.1.15 在文件头插入6字节
接下来,我们要把6字节的“00”改为“GIF89a”,具体方式是:在要修改的第1个字节前单击鼠标,然后直接在键盘上输入“47 49 46 38 39 61”即可,字母可不区分大小写,但注意不要误改了后面的其他字节,修改后结果如图2.1.16所示,将修改后的文件另存为新文件SimpleGIF-ch.gif。双击新的GIF文件能够正常显示,说明我们已正确修复。
图2.1.16 插入并修改文件头
通过工具gifsplitter.exe分离GIF的图像,在图像中没有发现明显的flag。查看图像延迟时间,如图2.1.17所示,前两幅图像的延迟时间是660ms,从第3幅图像开始,有的是200ms,有的是100ms,我们猜测可能是利用延迟时间的不同来隐藏消息。
图2.1.17 利用模板查看GIF延迟时间
我们介绍两种提取延迟时间的方法。
方法1 :在Linux系统下使用命令:
方法2 :修改010 Editor的GIF模板,在GIF模板中添加打印命令,如图2.1.18所示。保存修改后,选择Open Template,可以打开修改好的模板文件(见工具包中的GIF.bt文件),如图2.1.19所示。打开后选择Run Template,运行修改后的模板解析文件,解析结果会显示在Output窗口,如图2.1.20所示。显示Output就能看到解析结果,如图2.1.21所示。
图2.1.18 修改GIF解析模板
图2.1.19 打开修改后的模板解析文件
图2.1.20 显示Output窗口
我们猜测不同的延迟间隔代表不同的含义,一般默认的间隔是相同的。使用文本编辑器的替换功能,利用正则表达式替换,将“\d+delay time:”替换为空字符串;再使用一般替换把66替换为空字符串,尝试将20替换为0,10替换为1,替换后的结果如图2.1.22所示。这里没有去除换行符,将替换后的01字符串和换行符全部复制、粘贴到Koczkatamas工具包的BIN行,即可按8比特为一组进行解析,得到的flag是:XMAN{96575beed4dea18ded4735643aecfa35}。
图2.1.21 利用修改后的GIF模板得到的解析结果
图2.1.22 替换为二进制
通过上面的讲解,大家不难发现图像隐写类题目的特点:考法多样,思路清奇,工具众多。很多初学者往往会面临这样一种场景:拿到一幅图片后,大脑一片空白,不知道如何下手。总感觉自己的操作“东一榔头西一棒子”,却找不到flag。受“要你命3000”系统的启发,为解决大家解题时的困扰,本小节提出一套“图像隐写三板斧”的解题套路。“三板斧”中的每一“板斧”皆可独当一面,应对图像隐写题目均有奇效。
在详细介绍前,有一些要点需要提前向大家解释清楚,若没有阅读本段使用说明就使用“图像隐写三板斧”,那么出现任何问题,本书概不负责。首先,“三板斧”重点针对简单和中等难度题目,并不能覆盖所有隐写考点,但从目前的CTF比赛实践来看,该方法可以解决大概70%的图像隐写题目;其次,“三板斧”主要为初学者提供一种解题思路,有一定经验的读者要批判性吸收,切不可“走火入魔”;然后,初学者在使用“三板斧”时要严格按照步骤使用,既不要漏步也不要跳步;最后,如果用完“三板斧”还没有找到flag,那么有两种可能:要么是题目考查难度较大,需要更深入地分析,当然更大的可能性是读者不细心,错过了flag。此时,可以选择更细心地重来一遍,也可以选择放弃,先做其他题目,做完后如果还有时间,再重新审视这个题目。
图像隐写“三板斧”中的每一“板斧”均对应着若干工具:
❑第一板斧:010 Editor、strings。
❑第二板斧:StegSolve、zsteg。
❑第三板斧:binwalk、foremost。
第一板斧 的目的是发现插入隐藏,主要使用工具010 Editor和Linux系统下的strings命令来完成。这两个工具的地位平等,使用时不分先后。我们推荐优先使用010 Editor,这里因为利用其自带模板解析后的结果,可以快速发现所有的前置插入隐写和追加插入隐写。前置插入隐写的例子参见2.1.2节中的例题cat.jpg,拖入010 Editor的结果如图2.1.23所示,通过仔细观察就可以在文件头部看到插入的字符串。
图2.1.23 通过第一板斧发现前置插入信息
下面来看一个追加插入的例子。
【例题】welc0me.jpg
【题目来源】 2017央企大赛。
【题目描述】 找到文件中隐藏的信息。
【解题思路】 从文件后缀可以看出这是JPG文件,但是图片不能打开。一些读者会尝试修复文件,但这里需要明确一点:图像能否打开和图像是否存在隐写没有必然联系。建议初学者在解决图像隐写题目时,优先采用“三板斧”方法。按照第一板斧的步骤,先在010 Editor中打开文件,打开后会弹出报错信息,这是因为图像损坏导致模板解析错误,这类报错可能暗示着图像中存在追加插入隐写。观察解析结果,如图2.1.24所示,在文件末尾有字符串,利用Base64解码后就得到flag{gr3at0faLLtime}。
图2.1.24 第一板斧:发现追加插入信息
通过第一板斧可以发现所有较为明显的插入隐藏。如果在图像中没有发现插入隐藏,就需要使用第二板斧来发现替换隐藏,这主要针对LSB隐写。
在 第二板斧 中,要用到两个工具:StegSolve和zsteg,使用时 先用StegSolve再用zsteg 。StegSolve被称为“隐写神器”,很多图像隐写题目都会用到这个工具。它是一个jar文件,需要提前安装并配置好JRE才能运行。一般情况下,配置好JRE后双击StegSolve.jar就可以正常打开;如果双击后不能运行,则可以在命令行下通过JRE中的java.exe调用jar文件,如图2.1.25所示。
图2.1.25 通过java.exe调用jar文件
打开StegSolve后不能直接拖入图片,需要通过“File”菜单中的“Open”来选择文件后,才能看到图片,如图2.1.26所示。在程序下方还可以看到“<”和“>”两个按钮,多次单击“>”按钮,就能看到图2.1.27中的情况。在程序左上角可以看到“Red plane 7”,其含义是提取所有像素红色通道像素值的最高位组成的黑白图像(在StegSolve中,默认每个通道为8比特),“Red plane 0”代表红色通道像素值的最低位,每个通道都能构成8个不同的位平面。持续单击“>”按钮,不仅能看到Alpha、Red、Green和Blue共4个通道32个位平面,还能看到经过不同处理的其他图像。因此,“<”和“>”按钮是第二板斧中的重要功能。
图2.1.26 利用“File→Open”打开图片
图2.1.27 多次单击“>”按钮
单击图2.1.27中的“Analyse”(分析)按钮,出现如图2.1.28所示的项目,从上到下依次为:
❑File Format:文件格式及属性信息。
❑Data Extract:数据提取,主要用于提取比特并实现排列组合。
❑Stereogram Solver:立体图水平偏移控制,如图2.1.29所示,将图片水平偏移后再与原图叠加。
❑Frame Browser:帧浏览器,主要是对GIF之类的动图进行分解,可以查看每一帧的图像。
❑Image Combiner:拼图,图片拼接。
图2.1.28 “Analyse”按钮的功能
图2.1.29 图片平移后叠加
“Data Extract”是StegSolve中的重要功能,单击该项会打开如图2.1.30所示的界面。左侧的“Bit Planes”是各通道以及可以选择的比特,“0”代表最低比特,“7”代表最高比特。右侧的“Order Settings”用于设置提取时的顺序,Extract By为按row(行)或column(列)提取像素;“Bit Order”中的MSB First指最高位是第一位,LSB First指最低位是第一位;“Bit Plane Order”用于设置RGB通道的顺序。选择好所有顺序后,单击“Preview”按钮,就能在窗口中预览提取出的信息。
图2.1.30 “Data Extract”数据提取
注意: 当我们使用第二板斧打开StegSolve后,应优先使用“<”和“>”两个按钮,目的是发现通过LSB隐写的图像。如果没有任何发现,再考虑使用StegSolve的其他功能。
【例题】dandelion.png
【题目来源】 2016华山杯。
【题目描述】 找到文件中隐藏的信息。
【解题思路】 利用第一板斧,将文件拖入010 Editor,在文件头和文件尾都没有发现插入隐藏;再采用第二板斧,用StegSolve打开文件,单击“>”按钮,如图2.1.31所示,在红色通道最低位通过LSB隐写看到一个二维码。这个二维码并不是标准二维码,这时我们采用离线工具QR Research来处理。双击CQR.exe打开工具,单击鼠标按钮选择二维码区域,即可得到二维码扫码结果,再进行Base32解码,就得到flag:flag_Xd{hSh_ctf:U2s0_coo1}。
图2.1.31 发现LSB隐写的二维码
【例题】AsianCheetah.png
【题目来源】 2016 Sharif CTF。
【题目描述】 找到文件中隐藏的信息。
【解题思路】 先用第一板斧,通过010 Editor没有发现插入隐藏;再用第二板斧,在StegSolve中多次单击“>”按钮,也没有任何收获。因此,本题需要用到StegSolve中的Data Extract功能,选择蓝色通道最低位、LSB First、row,因为只有蓝色通道,故RGB顺序随意,设置如图2.1.32所示。在预览窗口看到flag:SharifCTF{e8e12db2fc654f3b50f3da4901ab986e}。这个题目本质上也是LSB隐写,和上个例题的区别是,上个例题隐写了一个二维码,通过“>”按钮可以看到;本题隐写了一个字符串,需要在Data Extract中才能看到。但是,这里有一个小问题,我们如何知道应该从蓝色通道最低位提取信息,而不是从其他位获取信息?如果没有其他工具的配合,我们只能手工多次尝试,没有其他更好的方法了。但有了zsteg工具,提取数据时就不需要反复进行人工尝试了。
图2.1.32 LSB隐写字符串
zsteg工具可以检测 PNG和BMP 图片里的隐写数据,功能非常强大,但需要在Linux系统中通过命令行运行。打开Linux终端,输入“gem install zsteg”命令就可以完成其安装。使用方法如图2.1.33所示,在命令“zsteg”后加入文件的相对路径或绝对路径,就能得到结果。zsteg还会把重要字符串自动标红并识别提取出的文件的类型。在图2.1.33中,flag字符串前有“b1,b,lsb,xy”,其含义如下:b1表示最低位(b8表示最高位,这与StegSolve略有不同),b表示蓝色通道,lsb表示最低位是第一位,xy表示按行提取,与图2.1.32中的设置完全一样。各字段的含义需要大家熟练掌握。
图2.1.33 zsteg的使用
【例题】easyimg.zip
【题目描述】 找到文件中隐藏的信息。
【解题思路】 对文件进行解压后,我们得到一个easy.bmp文件,双击该图片不能正常显示,猜测可能文件受损,需要修复。在010 Editor中打开该文件,如图2.1.34所示,但在本节中我们并没有学习BMP文件的文件格式,那么该如何对BMP文件进行修复呢?事实上,在CTF比赛中经常会遇到这种情况,即使没有学过相应的文件格式,我们通过“照猫画虎”的方法也能修复受损文件,只需要找一个与受损文件相同格式的正常文件进行对比即可。
图2.1.34 打开easy.bmp文件
对于图像类文件,利用计算机自带的“画图”程序可以生成多种格式的正常图像。对于本题,我们打开“画图”后,选择“文件→另存为”,将文件保存成BMP图像。在010 Editor中打开新保存的文件,如图2.1.35所示。通过仔细观察可以发现,正常的BMP文件以“BM”作为文件起始,且“36 00 00 00”位于第11~14字节处。而在easy.bmp文件中,文件起始不是“BM”,“36 00 00 00”位于第9~12字节处。由此,我们可以推测出,easy.bmp文件缺少了文件头“BM”,接下来我们要在文件头插入2字节,然后再做进一步分析。
图2.1.35 打开BMP文件
修改后的结果如图2.1.36所示,将修改后的文件另存为新文件easy-ch.bmp。
图2.1.36 插入并修改后的结果
利用第一板斧,在文件easy-ch.bmp中没有任何发现,利用StegSolve也没有发现LSB隐写的图像,因此考虑使用zsteg,结果如图2.1.37所示,并没有发现flag。这里,我们介绍一个关于zsteg的重要使用技巧:在命令中添加“-a”选项,可以尝试提取信息所有的排列组合。 一些题目要通过添加“-a”选项才能找到flag 。如图2.1.38所示,得到flag{44544427-2a95-4936-bcc1-47c95268ca4c}。
图2.1.37 zsteg的检测结果
图2.1.38 通过“zsteg-a”找到flag
在上述例题中,我们通过zsteg可以发现LSB隐写的字符串信息。实际上,zsteg还能发现利用LSB隐写的文件,并从图像中提取出对应的文件,zsteg的这种使用方法大家也要掌握。
【例题】try1try.png
【题目来源】 原创。
【题目描述】 找到文件中隐藏的信息。
【解题思路】 在010 Editor中打开图片,没有发现明显的插入隐藏信息,在StegSolve中也没有明显的发现,于是我们考虑使用zsteg工具,检测结果如图2.1.39所示,可以发现在RGB通道的最低位以LSB的方式隐写了一个RAR文件。接下来,我们通过命令“zsteg-E b1,rgb,lsb,xy try1try.png>ex.rar”把RAR文件提取出来。其中,“-E”表示提取,“b1,rgb,lsb,xy”是上一步操作得到的LSB隐写信息,“> ex.rar”表示利用输出重定向将提取出的信息保存为ex.rar,否则提取出的信息会打印在终端上。结合我们从图像上看到的“WEAK P@SS”信息,猜测提取出的RAR文件使用弱口令加密。因为ex.rar是RAR4,所以我们可以利用ARCHPR爆破RAR4文件的密码,利用字典或者6位纯数字爆破后发现解压密码是654321,解压后得到aaa.txt,打开文件即可看到flag{ZstegV587!}。
图2.1.39 zsteg检测发现文件
注意: 在第二板斧中,StegSolve和zsteg的使用有先后顺序。应先使用StegSolve中的“>”按钮查看是否存在LSB隐写图像,如果没有任何发现,再利用zsteg检测是否有LSB隐写字符串或文件。zsteg的提取结果可以在StegSolve中利用Data Extract按同样的设置进行验证。
【例题】taowa.zip
【题目来源】 2018年网鼎杯。
【题目描述】 找到文件中的flag。
【解题思路】 解压后发现每一层文件夹中都有一个PNG文件和一个子文件夹,针对每张图片使用三板斧的前两板斧(一种快捷操作是对每张图片依次使用strings和zsteg命令,strings命令对应第一板斧,zsteg命令对应第二板斧),最终在对6.png使用zsteg提取时可以找到flag{03a253f5-8e93-4533-bcfc-af908830095d},如图2.1.40所示。
图2.1.40 利用第二板斧找到flag
有些题目仍然通过插入隐藏来隐写消息,但是插入位置非常隐蔽,如果使用第一板斧和第二板斧没有任何发现,那么就需要使用第三板斧对题目再“抢救”一下。
第三板斧 用到的工具包括binwalk和foremost。binwalk在1.5.1节有过详细说明,主要是根据文件头特征检测有哪些文件;foremost的原理和binwalk类似,一般在Kali中预装,或者通过命令“sudo apt-get install foremost”进行安装。
例如,我们有一个abc.png文件,使用binwalk和foremost的区别在于,单独使用binwalk命令只能检测文件,要提取文件就要加“-e”选项,提取出的文件位于“_abc.png.extracted”文件夹下;foremost不需要添加选项就可完成提取,提取后的结果统一放到“output”文件夹中。在output文件夹中,会根据提取出的文件类型创建多个子文件夹。但是,如果执行foremost命令的路径已经有output文件夹,那么再次运行foremost命令会报错。因此,每次运行foremost前都可以先删除或重命名output文件夹,再执行foremost命令。
在比赛中,可以根据个人喜好决定binwalk和foremost的使用顺序,一般可以先利用binwalk检测,再使用foremost提取。有些文件利用binwalk能提取成功,但利用foremost提取不成功,或者反之。总之,需要大家根据情况灵活搭配使用。
【例题】3333.png
【题目描述】 找到文件中的flag。
【解题思路】 根据三板斧解题套路,使用第一板斧和第二板斧都没有发现隐写内容。因此,对文件使用binwalk,检测结果如图2.1.41所示,在0x29偏移处检测到一个zlib压缩文件,但是这个检测结果并不准确,其实是一个误报。对于所有PNG图片使用binwalk检测,都能在0x29偏移处发现一个zlib压缩包。这里我们使用 例题AsianCheetah.png 进行说明。
之所以所有PNG都能检测出zlib文件,是因为PNG格式中IDAT块的特征和zlib文件特征一致,所以利用binwalk检测PNG文件时经常能看到这个误报。误报并不意味着binwalk不能使用,对这两个图片都使用binwalk-e命令提取,发现都生成了新文件夹,查看文件夹中的内容,如图2.1.42和图2.1.43所示。对于29.zlib文件,binwalk会自动解压(即29文件),AsianCheetah.png对应的解压文件大小为0字节,3333.png对应的文件解压后不为空,这说明3333.png的IDAT块中插入了其他信息。对从3333.png中提取出的29文件使用file命令,结果是“data”,说明并没有识别出是什么文件。这里一个重要的思路是: 凡是不认识或无法识别的文件均可以使用binwalk检测 。检测结果如图2.1.44所示,29文件中还有一个zlib文件,继续提取,从图2.1.45中可以看到最终提取出的结果,“504b0304”是ZIP文件的开头,需要我们进行Base16解码(见1.2.2节关于Base16的介绍)并保存成文件。压缩包解压需要密码,但并不是伪加密,爆破也没有找到密码,用WinRAR打开压缩包或者对压缩包再次使用binwalk,可以发现压缩包中的注释:M63qNFgUIB3hEgu3C5==,对该字符串进行Base64解码后有不可打印字符,实际上这行注释就是解压缩密码。解压后得到flag{PnG_zLiB_dEc0mPrEsS}。
图2.1.41 binwalk检测结果
图2.1.42 PNG有隐藏内容的binwalk提取结果
图2.1.43 正常PNG图像使用binwalk提取后的结果
图2.1.44 对29文件继续检测
图2.1.45 最终的提取结果
上一小节介绍的是图像隐写三板斧1.0版本,虽然能解出一些图像隐写题目,但还有一些常见考点并没有涉及。为了解出更多题目,我们在1.0版本基础上进行升级得到图像隐写三板斧2.0,以便解决更多的图像隐写问题,希望读者可以在2.0版本的基础上举一反三,构建适合自己的解题套路。
1.第一板斧升级
一些图像会在图像的靠下部分显示重要信息,比如,出题人会故意减小图像高度使得图像重要信息不显示,从而达到隐写的目的。通过利用010 Editor不仅能发现插入隐藏,还可以帮助我们修改图片高度。
PNG图像的宽高在IHDR块中。出题人会直接修改原图的高度而不修改其他值,但是PNG每个块都有CRC值,强行修改高度将导致根据新的高度值计算出的CRC值和IHDR块中的原CRC值不匹配。在Windows系统下,CRC值不匹配的PNG图像可以正常打开,并且只显示IHDR块中指定的高度;但在Linux和mac OS系统下,CRC值不匹配的PNG图像不能正常打开。这是这类题目的一个重要特征。如果我们只有Windows系统,也能在010 Editor中发现CRC值不匹配的提示信息。
当我们发现PNG图像高度被修改时,一种方法是利用010 Editor把高度改成一个较大值。
【例题】hi.png
【题目描述】 找到文件中的flag。
【解题思路】 把图像拖入010 Editor,重点关注界面左下角的提示。如图2.1.46所示,把图像拖入010 Editor后先不要做任何其他操作,可以看到有报错信息,其含义为chunk[0]的CRC不匹配。从图中不难发现,chunk[0]是所有块的第一个块,即IHDR。 当出现“chunk[0]的CRC不匹配”时,我们就要想到很可能是图像的宽高被人为修改了 。接下来的思路是在010 Editor中调整图像宽高,一般情况下,只需要修改高度即可。如图2.1.47所示,展开chunk[0]中的ihdr结构体,能看到详细的宽高信息(width表示宽度,height表示高度,单位是像素)。如果随意修改宽度值,图像会出现“花屏”的效果,因此一般只修改高度值。问题是高度值应该改为多少比较合适呢?一种简单粗暴的方法是改成一个较大值,例如修改成1000。用鼠标左键双击height字段,把400改为1000,然后将图片“ 另存为 ”一张新的图片(注意不要直接保存,直接保存会覆盖原图像,而修改后还需要和原图比较)。修改后的结果如图2.1.48所示,在图像下方显示出了flag,同时出现黑色区域,这是因为高度值过大,Windows的照片查看器默认填充为黑色。
图2.1.46 PNG图像宽高被修改的提示
图2.1.47 在ihdr结构体中修改宽高值
图2.1.48 修改高度后的PNG图像
上面的例题是最简单的情况,只需要随便调大高度值即可,但在某些情况下,需要把图像的宽高修改为准确值。例如,当修改高度过大时,修改后的图像不能用StegSolve打开。由于在IHDR中保存着根据原宽高值计算得到的CRC,因此可以利用CRC值爆破出原始宽高的准确值。
【例题】plum.png
【题目来源】 2019中石油集团决赛。
【题目描述】 找到文件中的flag。
【解题思路】 将图片拖入010 Editor后,系统报出chunk[0]的CRC不匹配的错误,因此需要修改高度。将高度改为1000后,没有在图像下方发现flag。于是继续利用第二板斧,但是修改后的图像不能用StegSolve打开。利用脚本breakwidthheight.py根据CRC值爆破原始宽高,该脚本只针对PNG图像有效,具体方法如图2.1.49所示。图像准确的宽是1024像素,高是625像素,将修改后的图像放到StegSolve中,如图2.1.50所示,在红色通道的最低位发现一个二维码,扫码得到flag{affe5937-51d1-4182-87a4-bb4bcf038aea}。当没有修改高度时,原图在StegSolve中是看不到二维码的。但若高度设置过大,StegSolve将会崩溃,这就是要用脚本得到该图像准确宽高值的原因。
图2.1.49 利用脚本爆破PNG的正确宽高
除了PNG图像之外,也可以修改JPG图像的高度。JPG图像的SOF0段中有图像的宽高信息,但是由于段中没有CRC信息,因此用010 Editor加载JPG时不会有任何提示。而且,即使修改了高度,JPG图像也能在不同操作系统下打开,因此修改JPG高度实现隐写的方法效果较好且不太容易被发现。但是,我们有了升级后的第一板斧,当拿到一个JPG文件并放入010 Editor后,就可以考虑修改高度以查看图片底部是否存在隐写信息了。
图2.1.50 发现LSB隐写
【例题】Luff.jpg
【题目描述】 找到文件中的flag。
【解题思路】 将图像拖入010 Editor,可以看出JPG头部和尾部均没有插入信息。此时,尝试修改图片高度。如图2.1.51所示,将SOF0段中的Y_image值修改为1000,将文件“另存为”新的图片。双击打开新保存的图片,在图片底部就能看到flag。
图2.1.51 修改JPG文件的高度
实际上,其他格式的图像也可以通过调整高度实现信息隐藏。对于BMP图像,如图2.1.52所示,在bmih结构体中,biWidth和biHeight分别表示图像的宽度和高度,我们可以直接修改其数值。需要注意的是,高度值不要一次改得很大,可以每次增加20~50个像素值。
图2.1.52 修改BMP图像的高度
同样地,也可以修改GIF文件的高度,如图2.1.53所示。注意:修改的时候首先要修改逻辑屏幕标识符里面的高度值,再修改图像块中图像标识符的高度值。如果有多幅GIF图像,可能需要修改每幅图像对应的图像标识符,再做GIF图像拆分等操作。
2.第二板斧升级
图2.1.53 修改GIF文件的高度
第二板斧的目的是发现LSB隐写。在之前的例题中,都是把消息直接通过LSB隐写到PNG图像中,这种方法的隐蔽性有待提升。于是有人提出先将信息加密,再通过LSB隐写入图像,这样即使从图像中提取出信息,没有密钥的话也不能恢复出消息。这类题目也称为“带密钥的LSB隐写”,其典型工具是cloacked-pixel(https://github.com/livz/cloacked-pixel),主要用到脚本lsb.py。
破解带密钥的LSB隐写题目的关键是找到密钥。这类题目一般都提供提示,结合提示可以很快想到这个考点。如果没有任何提示,那么这类题目具备一定的难度,建议做完“三板斧1.0版”的基础操作后,再考虑该考点。
【例题】dong.png
【题目来源】 2020山东省科来杯。
【题目描述】 找到文件中的flag。
【解题思路】 若用过“三板斧”后没有找到任何线索,由于图片是PNG,因此猜测可能是带密钥的LSB隐写。但关键问题是密钥没有任何提示,要么写脚本用字典爆破,要么猜测弱口令。这里先猜测密钥是123456,使用cloacked-pixel,如图2.1.54所示,查看dongflag.txt,可以看到flag{6e9fbfe27c40bbad06db30c42c04c4d6}。密钥猜测正确。
图2.1.54 带密钥的LSB隐写
3.第三板斧升级
如果用完“三板斧1.0版”没有找到任何线索,且图片是JPG格式,那么可以在第三板斧中考虑JPG对应的专用工具,如stegdetect、JPHS、OutGuess、Steghide等。在线JPG图像隐写检测工具为https://lukeslytalker.pythonanywhere.com/。在下面的例题中,我们展示了OutGuess离线工具的使用方法。
【例题】UltimateSteg
【题目来源】 Th3Jackers CTF 2015。
【题目描述】 找到文件中的flag。
【解题思路】 文件名字是“终极隐写”,先掏出我们跃跃欲试的“三板斧”。把文件拖入010 Editor,发现文件头是GIF文件格式,使用GIF模板解析。如图2.1.55所示,单击模板解析结果中的GIF Trailer,可以快速跳转到文件结尾,很明显可以发现GIF文件末尾有一个ZIP格式的文件。这个ZIP文件可以通过binwalk提取,假如binwalk失效,就需要我们手动提取。接下来,我们重点演示如何手动快速提取文件。
图2.1.55 通过模板跳转到文件末尾
单击GIF解析结果的Trailer部分,对应字段会变为蓝底。用鼠标单击窗口右侧的滚动条,快速移动至GIF文件头,然后按住键盘上的<Shift>键,用鼠标单击GIF文件头的第一个字节,如图2.1.56所示,所有GIF文件数据都变为蓝底。
图2.1.56 利用滚动条和<Shift>键选中全部内容
接下来,按Delete键删除GIF数据,将剩余数据另存为ZIP压缩文件,ZIP文件的解压结果如图2.1.57所示。
图2.1.57 压缩包解压结果
对图像1.png~6.png逐个用“三板斧”分析。把1.png拖入010 Editor,在文件尾发现追加插入的字符串“Lets begin:3tKdX”,于是猜测可能每张图片都隐藏了一个字符串。
在2.jpg结尾发现字符串“Dont be stupid Use another method”。但是用“三板斧”没有找到任何线索,我们选择暂时放弃这张图片,先看看其他图片。
对3.jpg使用第一板斧后发现一个ZIP压缩包,解压后有1000个文本文件,绝大部分文件的内容是“NotHiNg HeRe”。我们可以写脚本逐个读文件中的内容,把不是“NotHiNg HeRe”的内容打印出来;或者在解压后的文件夹中,采用“cat *”方法。具体操作为:首先将3.jpg后的ZIP提取出来,并命名为ex3.zip;然后把该压缩包解压到ex3文件夹下;再把ex3文件夹复制到Kali虚拟机的桌面上;打开终端,修改路径为ex3文件夹,输入“cat*”命令。cat命令可以在屏幕上打印文件内容,“*”是通配符,代表任何字符串,“cat*”的含义是打印当前路径(也就是ex3文件夹)下的所有文件内容,结果如图2.1.58所示。这时,我们可以发现“GoOd 3RlZ30=”,它位于文件368中。
图2.1.58 查看所有打印结果
对于4.jpg,仅用第一板斧就能在文件头发现前置插入了“If you insist here is your gift:ZmxhZ”,看到熟悉的“Zmxh”,我们就知道是与Base64编码有关,那么每张图片中可能有Base64字符串的一部分,需要都提取出来后再拼接。
5.jpg文件的末尾告诉我们“So easy Just Look Carefully”,这个提示含义不明,于是接着用第二板斧。使用Stegsolve打开图片,单击“>”按钮,如图2.1.59所示,仔细查看发现字符串“TaU1w”。
6.jpg也非常简单,在文件末尾可以发现“Almost done! Here is your last gift TGUtU Now form the flag:)”。
图2.1.59 用StegSolve发现JPG中的隐写
不难发现,除了2.jpg,其他图片用“三板斧”都能解决。针对2.jpg尝试应用stegdetect、JPHS、OutGuess、Steghide等工具,发现利用OutGuess可以提取出有效信息,如图2.1.60所示。
图2.1.60 利用OutGuess提取JPG隐写内容
整理提取到的信息,如下所示:
❑1.png 3tKdX
❑2.jpg N0LWF
❑3.jpg 3RlZ30=
❑4.jpg ZmxhZ
❑5.jpg TaU1w
❑6.jpg TGUtU
接下来对这6个字符串进行排列组合,4.jpg肯定是第一个,3.jpg肯定是最后一个。可以写脚本进行排列组合,也可以手工尝试,按照4,1,2,5,6,3的顺序拼接字符串,得到:ZmxhZ3tKdXN0LWFTaU1wTGUtU3RlZ30=,对其进行Base64解码,得到flag{Just-aSiMpLe-Steg}。
总结“三板斧2.0版”的步骤如下:
第一板斧:应用010 Editor、strings。
第二板斧:应用StegSolve、zsteg(lsb.py)。
第三板斧:应用binwalk、foremost(stegdetect、JPHS、OutGuess、Steghide)。
“三板斧2.0版”的使用方法如下:
1)第一板斧可解决肉眼可见的插入隐藏和图像高度隐写。
2)第二板斧用StegSolve和zsteg发现图像是否有LSB隐写。
3)用第三板斧的binwalk和foremost查找较为隐蔽的插入隐藏。
如果还没有找到线索,就需要使用括号中的工具。针对PNG图像,可以考虑“带密钥的LSB隐写”,可使用lsb.py工具;针对JPG图像,可以检测JPG隐写算法,使用的工具包括但不限于stegdetect、JPHS、OutGuess、Steghide等。
最后,我们介绍一下图像隐写中经常出现的“双图”类题目。这类题目一般无法用“三板斧”套路解决,但也有一定的规律性。所谓“双图”是指题目一般会提供两张图片,一张是原图,另一张是有隐写消息的图片,我们需要对这两张图片进行处理,从而提取隐写信息。常见的“双图”解题套路包括:对两张图片对应像素值进行异或、相减、相加或相乘等,还可能涉及盲水印考点。关于盲水印的知识点,读者可以自学(项目网址:https://github.com/fire-keeper/BlindWatermark)。下面我们通过一个例子来展示较为简单的“双图”隐写套路。
【例题】star.bmp
【题目来源】 原创。
【题目描述】 找到文件中的flag。
【解题思路】 将图像文件在001 Editor中打开,使用BMP模板解析文件,发现在BMP文件尾还有一个JPG文件,如图2.1.61所示。将JPG图像手动提取出来,保存为ex.jpg。接下来,我们对两幅图像做异或、加减乘等操作。
图2.1.61 发现JPG图像
在StegSolve中打开题目原图,如图2.1.62所示,单击“Analyse→Image Combiner”,打开图像组合器。然后,选择我们提取出的图像ex.jpg,这时会弹出一个新窗口,新窗口的左上角会显示两幅图像的操作。我们多次单击“>”按钮,会发现图2.1.63中的隐写信息。两幅图像相减,会发现一个二维码,扫码得到flag{doublePic}。
从上面的例子中不难发现,看起来滴水不漏的“三板斧2.0版本”在实际比赛中也依然可能“漏”得一塌糊涂,仍有一部分考点是利用“三板斧2.0版”无法解决的。这是因为“三板斧2.0版本”只能解决大部分简单和中等难度的常见图像隐写题目。随着考查难度的加大或者引入新的考查套路,“三板斧2.0版本”可能就不再适用。
图2.1.62 选择“Image Combiner”
这里我们再次强调,“三板斧2.0版本”只是为初学者提供一种解题思路,随着以后比赛和学习的深入,希望读者不断总结和调整,最终建立起自己的解题套路。
还有一类比较有意思的隐写题目,需要从图像中提取像素值,或者题目只提供像素值,需要我们画出原图像。这里读取像素值或根据像素值画图,均要使用Python的PIL库。脚本已经在本书工具包中,其中getRGB.py用于读取像素点的像素值,RGB2pic.py用于根据像素值画图。
【例题】top.zip
图2.1.63 两张图像相减
【题目描述】 找到文件中的flag。
【解题思路】 解压后有两个文件,一个是加密脚本,另一个是加密后的图像。脚本中使用异或算法对图像加密。我们修改加密脚本,得到解密脚本top-dec.py,解密脚本中复用了加密脚本中的很多函数,希望读者认真比对两个脚本,并掌握这种快速复用关键代码的方法。
运行解密脚本时,需要把该脚本和flag_enc.hex放到同一个路径下,使用Python3运行,即可得到解密后的图像,如图2.1.64所示。
图2.1.64 图像提示信息
图像提示,左上角对角线隐藏消息,需要读取像素值。可以用StegSolve查看,感觉绿色通道有异常,于是修改getRGB.py如下,提取出所有绿色通道像素值:
运行修改后的getRGB.py,得到结果如下:[90, 109, 120, 104, 90, 51, 116, 111, 100, 88, 74, 121, 101, 86, 57, 49, 85, 70, 56, 48, 98, 109, 82, 102, 90, 106, 70, 110, 97, 72, 82, 102, 100, 122, 70, 48, 97, 70, 57, 116, 77, 51, 48, 61, 89, 91, 92, 91, 89],将其转换为ASCII码,得到:ZmxhZ3todXJyeV91UF80bmRfZjFnaHRfdzF0aF9tM30=Y[\[Y,再对等号和等号前的字符串进行Base64解码,得到flag{hurry_uP_4nd_f1ght_w1th_m3}。
【例题】misc-1.pcapng
【题目描述】 找到文件中的flag。
【解题思路】 在Wireshark中打开文件,进行协议分级,优先查看HTTP,在HTTP请求中发现flag.zip,如图2.1.65所示。输入过滤规则:http contains "flag.zip",只有一个数据包,再追踪HTTP流,从服务器的返回包中提取出一个压缩包,解压后是ce.txt。查看TXT文件,共98457行,每行3个正整数(以逗号分隔),且其值均在0~255范围内,猜测每行是像素点的RGB值,共98457个像素。我们需要根据像素值画图,但是缺少图像的宽高信息。于是对98457进行因数分解,得到98457=3×37×887,但一般图像的高或宽不会是30+像素,因此猜测图像宽高为111×887。
对RGB2pic.py脚本修改如下:
图2.1.65 查看HTTP请求信息
只需要修改x和y就能生成正确的图像,图2.1.66所示就是flag。
图2.1.66 利用像素值画图