MISC之图像处理

1. 附加式图片隐写

顾名思义,附加式图片隐写就是使用某种方法在载体文件上附加上需要被隐写的内容,而我们要做的就是根据方法提取出载体文件中被隐藏的内容。在CTF赛事中,常见的附加式图片隐写有两种方式,一种是直接附加字符串,另一种是图片作为其他文件的载体呈现。

1.1 附加字符串

附加字符串就是直接在图片内容中增加字符串,一般我们可以使用strings命令进行查看,也可以使用十六进制文件编辑工具进行附加内容查看。

● strings

strings命令在对象文件或二进制文件中查找可打印的字符串。字符串是4个或更多可打印字符的任意序列,以换行符或空字符结束。strings命令对识别随机对象文件很有用。

命令用法:strings + 文件名

● 十六进制工具

使用winhex或者010editor也是可以看到图片中附加的内容。

1.2 文件载体

图片文件的附加式隐写还会作为其他文件的载体的形式出现,最常见的就是图种了。图种就是利用些特殊的方式将图片文件与压缩包文件结合起来的文件,这类的文件一般保存为图片文件格式,可以正常显示图片。由于操作系统识别的过程中是,从文件头标志,到文件的结束标志位,当系统识别到图片的结束标志位后,默认是不再继续识别的,所以我们在通常情况下只能看到它是只是一张图片。对于这类的隐写,我们可以通过十六进制文件编辑工具进行文件提取也可以使用binwalk分离文件。

2. 基于文件结构的图片隐写

下面对PNG图片展开进行介绍 :

PNG图像格式文件由一个8字节的文件头和按照特定结构组织的3个以上的数据块(chunk)组成。

PNG文件格式:文件头(89 59 4E 47 0D 0A 1A 0A)+数据块+数据块+数据块+……

PNG图片定义了两种类型的数据块,一种是称为关键数据块,这是必须的数据块,另一种称为辅助数据块,这是可选的数据块。关键数据块定义了4个标准数据块,分别为IHDR、PLTE、IDAT和IEND,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。每个数据块都会包含长度、数据块类型码、数据块数据和循环冗余校验(CRC)四个部分。这里需要注意的是CRC域的值是数据块类型码和数据域的数据计算而来的。

2.1 IHDR

文件头数据块IHDR是PNG文件中的第一个数据块,而且一个PNG文件中只能有一个文件头数据块,它包含了PNG文件中存储的图像数据的基本信息,包括了图片的宽,高,图像深度,图像类型,压缩方法等。


如图所示,标蓝部分为IHDR数据块,00 09 00 0D代表IHDR头块长13,49 48 44 52代表IHDR标识,00 00 06 76代表图片的宽,00 00 01 F5代表图片的高。其中我们主要关注图片的宽和高。同时,00 09是加密,00 00是未加密。(当存在伪加密时,需要将09改为00)

在CTF中,经常通过改变宽和高使得图片显示不完整或者无法显示从而达到隐藏信息的目的。对于这种情况,我们不能轻易修改图片的宽或高的值,应该通过CRC的值推算出宽或高的值;或者是修改宽或高的值后,使用CRC计算工具,计算得出新的CRC值替换原本的值,以防图片报错打不开。

例如:现有某CTF题目如下,打开为一张图片,可以在Windows下正常打开。

使用tweakpng打开图片可看到提示报错。

由于CRC值计算的是数据块类型码及数据块数据,因此可以通过以下脚本计算出图片的原始宽和高。

1
2
3
4
5
6
7
8
9
import os
import binascii
import struct
misc = open("2.png","rb").read()
for i in range(1024):
data = misc[12:16] + struct.pack('>i',i)+ misc[20:29]
crc32 = binascii.crc32(data) & 0xffffffff
if crc32 == 0xCBD6DF8A:
print i

使用十六进制文件编辑器修改图片的宽和高和得出的结果一致,可得出隐藏在图片中的flag。

2.2 PLTE

调色板数据块 PLTE(palette chunk):它包含有与索引彩色图像(indexed-color image)相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。真彩色的 PNG 数据流也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。

2.3 IDAT

图像数据块 IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。这是一个可以存在多个数据块类型的数据块。它的作用就是存储着图像真正的数据。IDAT采用 LZ77 算法的派生算法进行压缩,可以用 zlib 解压缩。值得注意的是,IDAT 块只有当上一个块充满时,才会继续一个新的块。

例如:某CTF题目相关IDAT的部分解题过程:

使用pngcheck识别PNG图片文件(或者使用Stegsolve.jar、tweakPNG查看),可以看到正常充满的长度应该是65524,而倒数第二个IDAT并没有充满,就又起了一个IDAT,明显最后一个IDAT有问题。

取出有问题的IDAT块的内容。

去掉长度、数据块类型码(IDAT)以及CRC,只留取数据部分:

1
789C5D91011280400802BF04FFFF5C75294B5537738A21A27D1E49CFD17DB3937A92E7E603880A6D485100901FB0410153350DE83112EA2D51C54CE2E585B15A2FC78E8872F51C6FC1881882F93D372DEF78E665B0C36C529622A0A45588138833A170A2071DDCD18219DB8C0D465D8B6989719645ED9C11C36AE3ABDAEFCFC0ACF023E77C17C7897667 

可查找到789C是zlib的文件头,使用脚本进行解压缩。

1
2
3
4
5
import zlib
import binascii
IDAT = "789C5D91011280400802BF04FFFF5C75294B5537738A21A27D1E49CFD17DB3937A92E7E603880A6D485100901FB0410153350DE83112EA2D51C54CE2E585B15A2FC78E8872F51C6FC1881882F93D372DEF78E665B0C36C529622A0A45588138833A170A2071DDCD18219DB8C0D465D8B6989719645ED9C11C36AE3ABDAEFCFC0ACF023E77C17C7897667".decode('hex')
result = binascii.hexlify(zlib.decompress(IDAT))
print result

根据得出的结果在进行分析结合其他内容得出flag。

2.4 IEND

图像结束数据 IEND(image trailer chunk):它用来标记 PNG 文件或者数据流已经结束,并且必须要放在文件的尾部。IEND 数据块的长度总是 00 00 00 00,数据标识总是 IEND 49 45 4E 44,因此,CRC 码也总是 AE 42 60 82。

转载来自FreeBuf.COM