一、通用
1.Stegsolve与zsteg
解释:Stegsolve与zsteg 都可以提取基本的LSB,LSB隐写即最低有效位隐写,LSB隐写就是修改RGB颜色分量的最低二进制位也就是最低有效位,写入数据,这样做的目的是人眼最难分辨出来,但是通过工具可以很容易的检测出来
知识补充:
- PNG图片,png图片是一种无损压缩的图片,因为无损压缩所以可以实现LSB隐写。
- JPG图片,jpg图片是一种有损压缩的图片,修改的数据会在压缩中损失,不可以实现LSB隐写。
- BMP的图片,是没有经过压缩的。BMP图片文件大小一般较大,bmp图片也可以实现LSB隐写。
Stegsolve查看red blue green三通道最低二进制位的方法:
-
查看
注意:一般复杂是上图所示,有的简单的图需要认真查看通道看哪里有字出现了,来这里选择通道,比如下图(左上角有小字),还要根据出现字在行还是列选择Row还是Column,同时Bit Plane Order也要多切换
-
导出
普通情况:
- 选择图片
- 切换不同的颜色通道
Zsteg此工具比Stegsolve更推荐,其更强大更全面,不光解决LSB隐写,其它数据的隐藏也很好用(配合binwalk),适用于png与bmp文件
安装:gem install zsteg
使用:zsteg 1.png
尝试所有方法:zsteg -a 1.png
提取出发现隐写的文件:zsteg -e extradata:0 misc17.png > 1.txt
或zsteg -e 2b,b,lsb,xy misc17.png > 1.txt
(看软件给的词)
2.GIF动图
解释:通过该工具能够一帧一帧的对Gif里面的内容进行查看
-
选择此功能对Gif图片进行一帧一帧查看
-
在线网站:https://tu.sioe.cn/gj/fenjie/(对动图,特别是多张图片需要拼接在一起的很有效)
-
时间轴(每一帧间的时间间隔):
apt install imagemagick
identify -format "%T " n.gif
-
APNG转GIF
解释:APNG是一种跟GIF类似的图片也是有多帧的存在,其内容特征存在fdAT
fcTL
通过tweakpng查看,在遇到APNG时有时也需要时间轴处理,此时需要使用到APNG Disassembler(使用脚本将apng
格式转gif
格式再用identify
命令会出现问题)使用:APNG Disassembler是客户端软件,选择文件后就能对其进行操作,之后它会生成APNG对应帧的每一个图片,并有一个txt文件包含其时间轴,此时我们可以用python进行提取
base_url = r'apngframe' l = [] for i in range(1, 69): if i < 10: path = base_url + '0' + str(i) + '.txt' else: path = base_url + str(i) + '.txt' with open(path, 'r') as f: t = f.readline()[6:] x = t.split('/')[0] print(x, end=' ') l.append(x) print() # 转ascii for i in l: print(chr(int(i)), end='')
-
每张GIF图片的偏移
identify -format "%X %Y\n" misc46.gif>1.txt
用python脚本根据X Y坐标绘图
from PIL import Image import matplotlib.pyplot as plt a = open('1.txt','r').read() a = a.split('\n') a.pop(-1) a = [i.replace('+','') for i in a] a = [(int((i.split()[0])),int((i.split()[1]))) for i in a] print(a) # 对于pp中的每个像素位置i,会创建一个新的大小为1x1像素的黑色图片,并将其粘贴到原始图片img的对应位置上 img = Image.new('RGB',(400,70),(255,255,255)) for i in a: new = Image.new('RGB',(1,1),(0,0,0)) img.paste(new,i) plt.imshow(img) plt.show()
-
APNG每张XY偏移
import struct from PIL import Image from matplotlib import pyplot as plt def hex_to_b(hex_str): # 将十六进制字符串转换为字节字符串 hex_str = hex_str.encode().hex() byte_str = bytes.fromhex(hex_str) return byte_str def find_char_positions(data, pattern): positions = [] for i in range(len(data) - len(pattern) + 1): if data[i:i + len(pattern)] == pattern: positions.append(i) return positions baseurl = r'7.png' a = open(baseurl, 'rb').read() list_index = find_char_positions(a, hex_to_b('fcTL')) print(list_index) list = [] for i in list_index: t = [] d = a[i+16:i+16+4] result = struct.unpack('>I', d)[0] t.append(result) d = a[i+16+4:i+16+8] result = struct.unpack('>I', d)[0] t.append(result) list.append(t) print(list) # 对于pp中的每个像素位置i,会创建一个新的大小为1x1像素的黑色图片,并将其粘贴到原始图片img的对应位置上 img = Image.new('RGB',(400,70),(255,255,255)) for i in list: new = Image.new('RGB',(1,1),(0,0,0)) img.paste(new,i) plt.imshow(img) plt.show()
3.图片隐写
3.1 基础隐写
解释:指的是一个文件里面包含其它格式文件与图片,此时可以通过binwalk
将它们分开,不能完全相信binwalk,这个工具有时候分辨不出来里面混杂的音频文件,建议使用010editor查看,或配合foremost,或者用010editor
进行查看
3.1.1 binwalk
解释:下面这三款工具要轮流使用直到完成任务,目前看来,binwalk
一般能分析对隐写的文件,但是拆分不出来,反而foremost能够拆分出来
binwalk xxxx -e
自动分离图片里面的内容
3.1.2 foremost
foremost xxx
3.1.3 dd
解释:dd需要结合binwalk显示的块数,例如下图
dd if=hong.mp3 of=hong1.jpeg skip=138170 bs=1
if=:输入文件,即源图片。
of=:输出文件,即提取出来的数据。
skip=N:跳过输入文件的前N个字节。
bs=1:设置块大小为1字节,配合skip可以准确定位到具体的字节。
N是你想要跳过的字节数,根据隐藏信息可能出现的位置来决定。
count=3142 选择要获取的块数
3.1.4 strings
解释:对于隐写而言,strings命令往往能看到图片里面写着的字符串,对于CTF而言,很可能就是出题人给的暗示
strings 1.png
3.1.5 exiftool
解释:能查看图片的相关信息,比如尺寸大小,时间等等,当有图片隐写时其有时也能够进行分离(例如jpg图片)
下载:apt-get install exiftool
使用:exiftool 1.jpg
3.2 工具隐写
3.2.1 四个盲水印
-
盲水印(单图)
解释:我们遇到一些题可能暗藏着盲水印,需要我们来解决,
使用文章开头提到的WaterMark软件 -
盲水印(多图)
地址:https://github.com/chishaxie/BlindWaterMark
解释:适合于多张图片最后合成一个图片的使用BlindWaterMark脚本安装依赖:
pip install opencv-python pip install matplotlib
合成
python bwm.py encode hui.png wm.png hui_with_wm.png python bwm.py encode A图 B图 生成的图片路径 python2版本使用bwm.py文件 python3版本把bwm.py改为bwmforpy3.py
提取
python bwm.py decode hui.png hui_with_wm.png wm_from_hui.png python bwm.py decode A图 B图 生成的图片路径 python2版本使用bwm.py文件 python3版本把bwm.py改为bwmforpy3.py
-
盲水印(多图)
地址: https://github.com/linyacool/blind-watermark
命令:python decode.py --original ori.png --image res.png --result extract.png
-
java盲水印
地址:https://github.com/ww23/BlindWatermark
命令:java -jar BlindWatermark.jar decode -c img-ec.jpg img-dc.jpg
3.2.2 隐写信息提取
-
blind_watermark
解释:可能提取出来文本,文件,压缩包,图片等等
地址:https://github.com/guofei9987/blind_watermarkimport threading from blind_watermark import WaterMark # 提取文字 wm_shape = int(input('请输入该题的wm_shape,在此为数字(不知道的话只能爆破解很慢):')) bwm1 = WaterMark(password_img=1, password_wm=1) try: wm_extract = bwm1.extract(r'./img/embedded.png', wm_shape=wm_shape, mode='str') print(wm_extract) except: print('尝试求解失败') pass
from blind_watermark import WaterMark # 提取图片 bwm1 = WaterMark(password_wm=1, password_img=1) # notice that wm_shape is necessary print('请注意需要知道wm_shape的值,否则出来的图片没有任何用') bwm1.extract(filename='33.png', wm_shape=(350, 350), out_wm_name='extracted.png')
-
HideInfo
地址:https://github.com/guofei9987/HideInfo
支持格式:图片、音频、文本
原理:其基于特定算法,能够将图片直接转为RGB存储,也能LSB隐写(非传统),也能往音频和文本里面嵌入内容,且内容无法加密
3.2.3 隐写信息加密提取
- cloacked-pixel
原理:基于LSB的,且必须要加密的一种技术(加密的有效载荷将具有高熵并且将类似于随机数据。这就是为什么LSB位置中0和1的频率应该相同-0.5的原因)
支持格式:图片类型
解出内容格式:一般为文件
地址:https://github.com/livz/cloacked-pixel
命令:(python2) lsb.py extract <stego_file> <out_file> <password>
- stegpy
原理:基于LSB的一种将编码信息隐写在图像和音频文件中的程序。
解出内容格式:一般为字符
支持格式:WAV、WebP、GIF、BMP、PNG
安装:pip install stegpy
命令:stegpy 0.png -p # -p不要带密码,因为后面会提示你输入密码,如果没有加密可以不输入-p
- OurSecret.exe 使用该软件
3.2.4 LSB隐写
-
基本的LSB隐写:https://github.com/librauee/Steganalysis
原理:基于最低有效位,循环把原图的RGB三色道的二进制最后一位进行替换要隐藏的内容二进制第n项,该隐写可以直接通过Stegsolve去通过查看RGB 0通道数据提取出来 -
变种LSB隐写:https://github.com/guofei9987/HideInfo
原理:基于最低有有效位,但其使用位运算来隐写内容,Stegsolve去通过查看RGB 0通道数据不能提取出来
隐写:hide_in_img.file_encode(filename='output_block.jpg', img_filename='279cebca7eb64cb5d7662c98061fdaeea1d4a8e5.png', img_filename_new='藏物于图.png')
提取:text_encode = hide_in_img.file_decode('藏物于图-解出的文件.jpg', img_filename='藏物于图.png')
4.图片高宽隐藏
4.1 高度篡改
解释:因为图片给的高度或宽度很小,当更改图片高时,隐藏的内容就能看到了
辨别PNG图片高度是否变化:通过Stegsolve的File Format对比最开头的CRC,如果CRC与计算出来的有出入可能就是高度修改了或者使用pngcheck工具查看CRCapt install pngcheck
改变GIF高度注意:GIF每一帧都有一个单独的高度,所以需要Ctrl-F
改变图片高度:使用010editor工具,先打开Variables,如下图1;调整高度如下图2
4.2 宽度篡改
解释:当高度修改时,可能只是有信息展示不全,但是当宽度篡改时,图片可能如下那样,完全无法还原原图内容,想恢复宽度,可以使用爆破来产生所有宽度的图片,下面给出一个求PNG的爆破脚本(PNG可以使用恢复高度的方法,但是要注意,可能CRC也被改过,导致破解不出来)
def go(filepath, start, end):
bp = open(filepath, "rb").read()
os.makedirs('output')
print('正在创建目录:output')
print("正在爆破")
for i in range(start, end):
png_name = 'output/' + str(i) + '.png' # 我是建立一个文件夹,可以不写前面的文件夹路径。
png = open(png_name, "wb")
data = bp[:16] + struct.pack('>i', i) + bp[20:]
png.write(data)
png.close()
print("爆破完成,全部输出在output文件夹内")
5.图片模糊
解释:给的图片特别模糊看不清里面的内容,可以通过SmartDeblur进行调整对比度来看尝试让图片清晰一点
6.识别码
解释:有二维码、条形码等等,可能普通的扫码器扫描不出来,可以在在线网址上进行扫码https://online-barcode-reader.inliteresearch.com/
7.双图结合
解释:当题目中给出双图时,可能要用到Stegsolve的Image Combiner功能,先打开一张图再点击Image Combiner然后切换打开新图的色道有时候会出现二维码
8.文件修复
解释:文件能发现明显的头标志,但是不完整,需要修复,或者填充
9.1 PNG
9.1.1 PNG结构分析
解释:正常的PNG文件由多个Chunk组成,其中一定存在的是开头ChunkIHDR
与结尾ChunkIEND
,其它还是辅助块(pHYs、tEXt等等)(可能存在可能不存在)和数据块(IDAT)(可能存在多个)构成,其中每个Chunk组成结构如下示例表格
字段 | 大小 | 描述 |
---|---|---|
Length | 4 | 当前Chunk块的长度 |
Chunk Type | 4 | 数据块类型,例如IHDR、pHYs、IDAT、IEND等等 |
Chunk Data | Length | 数据块的数据 |
CRC | 4 | 当前Chunk块的 循环冗余码 |
分析图片:
# png开头
89 50 4E 47 0D 0A 1A 0A
# 第一块(IHDR)
00 00 00 0D(Lenth) 49 48 44 52(ASCII:IHDR)
00 00 00 32 00 00 00 32 08 02 00 00 00(Chunk Data)91 5D 1F E6(CRC)
# 第二块(IDAT)
00 00 00 4D(Lenth) 49 44 41 54 78(ASCII:IDAT)
9C ED CE 31 01 C0 20 10 00 B1 07 FF 9E 5B 03 2C
99 60 B8 28 C8 9A F9 E6 3D FB 76 E0 AC 96 A8 25
6A 89 5A A2 96 A8 25 6A 89 5A A2 96 A8 25 6A 89
5A A2 96 A8 25 6A 89 5A A2 96 A8 25 6A 89 5A A2
96 A8 25 6A 89 5A E2 07 3F AC 01 63(Chunk Data)
F7 09 5E 5B(CRC)
# 第三块(IEND)
00 00 00 00(Lenth) 49 45 4E 44(ASCII:IEND) AE 42 60 82(CRC)
9.1.2 PNG注意事项
IDAT的长度:PNG图片里面的数据库IDAT的长度有一定的规律,若有多个IDAT,其前几个长度一般是相同且大于最后一块的长度(如果前几个不相同很有可能是人为修改过,人为修改不一定会改变图片内容),最后一个IDAT长度往往是小于前面的IDAT,在这样可能会遇到插入错误IDAT的CTF题目,删除错误IDAT即可,建议使用tweakpng进行删除
IDAT的数据内容:在有多个IDAT的情况下,往往可以看到第一个IDAT后面紧跟着78 9C(49 44 41 54 78 9C),这代表着IDAT的内容将会被Zlib压缩(78 9C就是Zlib的头),剩下的IDAT后面跟着的也是被Zlib压缩的数据,但是注意一个PNG文件里面只有一个Zlib头,除第一个外的IDAT虽然内容也是被Zlib压缩的,但是其并不是一个新的Zlib压缩包,而是第一个Zlib压缩包余下的内容,这意味在题目中binwalk出两个Zlib往往有一个是有问题的
9.1.3 PNG开头块详解
1、开头的0~7八个字节为png的文件头:89 50 4E 47 0D 0A 1A 0A (固定格式)
2、8~11四个字节:00 00 00 0D 表示头部数据块的长度为13(固定格式)
3、12~15四个字节:49 48 44 52 表示文件头数据块的标示(固定格式)
4、16~19四个字节:00 00 03 84表示图片的宽(不固定)
5、20~23四个字节:00 00 00 96表示图片的高(不固定)
6、24~28五个字节:08 02 00 00 00表示Bit depth(图像深度)、ColorType(颜色类型)、 Compression method(压缩方法)、 Filter method(滤波器方法)、Interlace method(隔行扫描方法)这五个字节不固定,均为可变数据
7、29~32四个字节:76 EC 1E 40为图片的crc校验值由从第12个字节到第28个字节的十七位字节进行crc计算得到
9.2 JPG/JPEG
FF D8 FF + E0/E1 + Data size(2 bytes) + Data(n bytes)
E0表示:JFIF(老设备使用较多)
E1表示:Exif(存储信息更多比如设备时间等等)
10.图片内容异或加减等等
解释:当前10种方案,都不能解决问题时,图片里面只有一群陌生的十六进制,往往有可能是数据被进行了运算,我们可以通过010editor来轻松的解决这些问题
举例:
具体操作指南:
- 点击tools随便打开一个功能
- 然后虽然都是英文但是下面都有数学公式如下图,特别注明的是下面的X[i]表示的是当前文件遍历每一个16进制的值
- 注意取反,Negate是取反的函数,意思就是比如0x77+0x89=0x100,此时如果源文件为0x77取反后结果为0x89
二、具体类别
补充
1.文件头尾
文件 | 文件头hex | 文件头ascii | 文件尾 | 备注 |
---|---|---|---|---|
PNG | 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 | ‰PNGIHDR | 49 45 4E 44(IEND)此结尾并非最后几个(可能是倒数几个) | |
GIF | 47 49 46 38 | G I F 8 | 00 3B | |
WEBP | 52 49 46 46 XX XX XX XX 57 45 42 50 | RIFFWEBP | 00 00 | |
BMP(16位深度) | 42 4D 28 07 06 00 00 00 00 00 36 00 00 00 | BM( | 无固定结尾 | |
BMP(24位深度) | 42 4D A0 0A 09 00 00 00 00 00 36 00 00 00 | BM | 无固定结尾 | |
BMP(32位深度) | 42 4D 18 0E 0C 00 00 00 00 00 36 00 00 00 | BM | 无固定结尾 | |
JPG/JPEG(JFIF) | FF D8 FF E0 | ÿ Ø ÿ à J F I F | FF D9 | |
JPG/JPEG(Exif) | FF D8 FF E1 | ÿ Ø ÿ à J F I F | FF D9 | |
TIF/TIFF | 49 20 49 | 无固定结尾 | ||
TIF/TIFF(Intel) | 49 49 2A | II* | 无固定结尾 | |
TIF/TIFF(big endian) | 4D 4D 00 2A | MM | 无固定结尾 | |
TIF/TIFF(Motorola) | 4D 4D 2A | MM | 无固定结尾 | |
TIF/TIFF(BigTIFF files) | 4D 4D 00 2B | MM | 无固定结尾 | |
Python 3.0(pyc) | 3B 0C 0D 0A | 暂无 | 无固定结尾 | |
Python 3.1(pyc) | 4F 0C 0D 0A | 暂无 | 无固定结尾 | |
Python 3.2(pyc) | 6C 0C 0D 0A | 暂无 | 无固定结尾 | |
Python 3.3(pyc) | 9E 0C 0D 0A | 暂无 | 无固定结尾 | |
Python 3.4(pyc) | EE 0C 0D 0A | 暂无 | 无固定结尾 | |
Python 3.5(pyc) | 17 0D 0D 0A | 暂无 | 无固定结尾 | |
Python 3.6(pyc) | 33 0D 0D 0A | 暂无 | 无固定结尾 | |
Python 3.7(pyc) | 42 0D 0D 0A | 暂无 | 无固定结尾 | |
Python 3.8(pyc) | 55 0D 0D 0A | 暂无 | 无固定结尾 | |
Python 3.9(pyc) | 61 0D 0D 0A | 暂无 | 无固定结尾 | |
Python 3.10(pyc) | 6F 0D 0D 0A | 暂无 | 无固定结尾 | |
ZIP | 50 4B 03 04 | P K | 50 4B | |
RAR4 | 52 61 72 21 1A 07 00 | R a r ! | C4 3D HEAD_TYPE | |
RAR5 | 52 61 72 21 1A 07 01 00 | R a r ! | 1D 77 56 51 HEAD_SIZE HEAD_TYPE | |
7Z | 37 7A BC AF 27 1C | 7 z ¼ ¯ ’ | ||
Wav | 52 49 46 46 XX XX XX XX 57 41 56 45 66 6D 74 20 | RIFF ™WAVE | 无固定结尾 | |
img(软盘) | EB 4E 90 | ëN | 无固定结尾 | 编辑虚拟机添加软盘即可打开 |
2.压缩文件特征
解释:用010editor打开压缩文件,如果看到里面出现文件名或其它文本数据,很有可能其就是压缩文件,一般可以尝试zip和rar两种
工具配置
1.Stegsolve
工具地址:http://www.caesum.com/handbook/Stegsolve.jar
解释:该工具需要安装jar包后才能配合使用,下面同时会给出快速打开工具的代码,需要两个文件,启动的时候启动vbs文件
start.bat
java -jar Stegsolve.jar
start.vbs
set ws=createobject("wscript.shell")
ws.Run """start.bat""",0
2.binwalk
-
下载:https://github.com/ReFirmLabs/binwalk/archive/refs/tags/v2.3.2.zip
-
setup:
python setup.py install
-
便捷化配置:自己写一个快速启动binwalk的py然后打包成exe然后放置于python下面的Scripts目录就能直接在cmd
binwalk xxxx
执行命令import subprocess import sys # 获取命令行参数(除了脚本名称之外的部分) args = sys.argv[1:] # 指定 Python 解释器和 binwalk 脚本的路径 python_path = "python" binwalk_script = "D:\\python\\Scripts\\binwalk" # 使用 args 中包含的参数构造命令 command = [python_path, binwalk_script] + args # 调用 subprocess.call 来执行命令 subprocess.call(command)
pyinstaller --onefile binwalk.py
3. 010editor
下载:https://www.sweetscape.com/010editor/
复制16进制到左边:Ctrl+Shift+V
复制文本到右边边:Ctrl+V
插入内容:光标放至要插入内容处,然后在软件最上方一栏选择Edit然后选择插入选择Hex与大小即可
对代码进行异或操作等等:
4.WaterMark
解释:一款普通破解盲水印的工具(适合单图片),下载非官网,请注意
https://pan.quark.cn/s/ab1aaa012cd0#/list/share
5.BlindWaterMark
解释:一款普通破解盲水印的工具(适合多图片)
https://github.com/chishaxie/BlindWaterMark
6.tweakpng
https://entropymine.com/jason/tweakpng/