隐写术Steganography
(一)NTFS数据流隐写
(二)base64隐写
(三)图像隐写
(四)零宽字符隐写
(五)word隐写
(六)PYC隐写
(七)音频隐写
(八)文件合成与分离
(九)BMP/PDF隐写
【附】检测工具
(一)NTFS数据流隐写
NTFS是微软Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式。NTFS比FAT文件系统更稳定,更安全,功能也更为强大。
这个NTFS数据流文件,也叫Alternate data streams,简称ADS,是NTFS文件系统的一个特性之一,允许单独的数据流文件存在,同时也允许一个文件附着多个数据流,即除了主文件流之外还允许许多非主文件流寄生在主文件流之中,它使用资源派生的方式来维持与文件相关信息,并且这些寄生的数据流文件我们使用资源管理器是看不到的。
(二)base64隐写
可以看出一串base64的编码最多也只有4bit的隐写空间,所以实现隐写往往需要大量编码串。,隐写时把明文的每个 字符用8位二进制数表示,由此将整个明文串转为bit串,按顺序填入base64编码串的可隐写位中即可实现隐写。)
题目:
[GXYCTF2019]SXMgdGhpcyBiYXNlPw==
Base64码表:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /=
Base64是一种用64个可打印字符来表示二进制数据的方法。
base64编码
1个字节对应8个比特,一个可打印字符对应6个比特,即一个单元,将目标字串变成二进制数据流,然后6个一单元划分对应成码表的索引,用base64码表中的字符替换。
码文c和明文m的关系为len(c)×6==len(m)×8len(c)×6==len(m)×8,即len(c)×3==len(m)×4len(c)×3==len(m)×4,那么必须码文字符串的长度必须为4的倍数,明文字符串的长度必须为3的倍数。
对于明文字符串长度不足3的倍数的情况用每一个二进制位用0 bit0 bit补足直到满足明文字符串长度为3的倍数。
不难看出,一个base64码文最多可以有2个‘=’,最少可以没有等号(此时明文长度刚好是3的倍数)。
base64解码
把码文末端的‘=’去除
在其二进制数据的末尾丢弃最小数目的二进制位使二进制位数为8的倍数,然后8位一组进行ASCII编码。
base64隐写原理
在base64解码中,去除等号之后将末尾一些二进制位丢弃使二进制位数为8的倍数,所以一些隐藏数据可以写在可以被丢弃的部分,这部分可以随意写成任意值而不用担心影响解码的结果,同时也说明了不同的base64码文可能生成相同的明文。
下面是提取脚本
importre
path ='flag.txt'#your path
b64char ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /'
withopen(path,'r')asf:
cipher = [i.strip()foriinf.readlines()]
plaintext =''
foriincipher:
ifi[-2] =='=':# There are 4-bit hidden info while end with two '='
bin_message =bin(b64char.index(i[-3]))[2:].zfill(4)
plaintext = bin_message[-4:]
elifi[-1] =='=':# There are 2-bit hidden info while end with one '='
bin_message =bin(b64char.index(i[-2]))[2:].zfill(2)
plaintext = bin_message[-2:]
plaintext = re.findall('.{8}', plaintext)# 8bits/group
plaintext =''.join([chr(int(i,2))foriinplaintext])
print(plaintext)
下面是隐写脚本
#!/usr/bin/env python
importbase64
withopen('./gitanjali.txt','r')asf:
data = [i.strip()foriinf.readlines()]
base64Data = [base64.b64encode(i)foriindata]
b64char ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /'
msg ='Gitanjali'# 隐写内容,注意隐写内容不应超过最大隐写bit数
msg_bit =''.join([bin(ord(i))[2:].zfill(8)foriinmsg])
offset =0
new_data = []
foriinbase64Data:
ifi[-2]=='=':# There are 4-bit hidden info while end with two '='
offset =int(msg_bit[:4],2)
i = i.replace(i[-3], b64char[b64char.index(i[-3]) offset])
msg_bit = msg_bit[4:]
elifi[-1]=='=':# There are 2-bit hidden info while end with one '='
offset =int(msg_bit[:2],2)
i = i.replace(i[-2], b64char[b64char.index(i[-2]) offset])
msg_bit = msg_bit[2:]
new_data.append(i "n")
withopen('./encodeFile.txt','w')asf:
f.writelines(new_data)
(三)图像隐写
有时候会出现许多文件的情况,此时想找出某一个关键的信息字段可以用下面的指令
exiftool *|grep flag#搜索当前文件夹下所有文件的exif信息中的flag字符
还有直接在图片属性的备注里给出flag,这种情况可以用010editor或者winhex等工具打开图片搜索关键字如flag、ctf等查看。
宽高修改
IHDR隐写(.png)
对.png格式的图片进行宽高的修改进而隐藏图片关键信息
原理:png图片的宽和高信息在png的IHDR数据块内,通过修改图片的宽和高数据使图片仅显示一部分,另一部分不显示
特征:010 Editor中打开后会出现CRC校验值错误的报错提示
破解:可以通过CRC值来暴破获取正确的宽值或者高值
importzlib
importstruct
filename =#图片路径
crc_str=#图片的CRC值
# 同时爆破宽度和高度
withopen(filename,'rb')asf:
all_b = f.read()
data =bytearray(all_b[12:29])
n =4095
forwinrange(n):
width =bytearray(struct.pack('>i', w))
forhinrange(n):
height =bytearray(struct.pack('>i', h))
forxinrange(4):
data[x 4] = width[x]
data[x 8] = height[x]
crc32result = zlib.crc32(data)
#替换成图片的crc
ifcrc32result == crc_str:
print("宽为:", end ='')
print(width, end =' ')
print(int.from_bytes(width, byteorder='big'))
print("高为:", end ='')
print(height, end =' ')
print(int.from_bytes(height, byteorder='big'))
.jpg宽高隐写
SOF0段的X_image和Y_image分别记录图片的宽和高,直接修改,不用担心校验错误。
.bmp宽高隐写
IDAT隐写(.png)
图像数据块 IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。
储存图像像数数据
在数据流中可包含多个连续顺序的图像数据块
采用 LZ77 算法的派生算法进行压缩
可以用 zlib 解压缩
值得注意的是,IDAT 块只有当上一个块充满时,才会继续一个新的块。一旦出现不符合这个规律的情况(有一块IDAT还没填满但紧跟其后的是一个新的块),那么就是人为添加了数据块。
破解:010 editor直接提取出数据,然后扔进zlib解压脚本(如下)里解压获得原始数据。
#! /usr/bin/env python
importzlib
importbinascii
IDAT =".........".decode('hex')#填入dump出的IDAT数据
result = binascii.hexlify(zlib.decompress(IDAT))
print(result.decode('hex'))
print(len(result.decode('hex')))
LSB隐写
LSB-Steganography工具实现的不加密隐写可以用Stegsolve查看,这种算法的特点就是LSB位前面很多0
[cloacked-pixel](https://github.com/livz/cloacked-pixel#:~:text=cloacked-pixel. Platform independent Python tool to implement LSB,Hide files within least significant bits of images.)工具实现的加密隐写(带密钥)可以用Stegsolve查看,这种算法的特点是开头有个数字,根据观察,这个数字应该是有用的数据的长度,然后才是很多个0,接着是相应长度的数据。
cloacked-pixel使用方法:
lsb.py hide #隐写
lsb.py extract #提取
lsb.py analyse #分析检测
提取工具:
对于不加密的LSB隐写:Stegsolve中extract
对于带密码的LSB隐写:[cloacked-pixel](https://github.com/livz/cloacked-pixel#:~:text=cloacked-pixel. Platform independent Python tool to implement LSB,Hide files within least significant bits of images.)
outguess隐写(.jpeg)
题目:[WUSTCTF2020]alison_likes_jojo
使用outguess实现隐写加密与解密
加密:
outguess -k"my secret key"-d hidden.txt in.jpg out.jpg
加密之后,in.jpg会覆盖out.jpg,hidden.txt中的内容是要隐藏的东西
解密:
outguess -k"my secret key"-r out.jpg hidden.txt
解密之后,解密内容放在hidden.txt中
检测:使用stegdetect工具可以检测
F5隐写
检测:使用stegdetect工具可以检测
提取:F5-steganography
盲水印
盲水印是一种肉眼不可见的水印方式,可以保持图片美观的同时,保护资源版权。
提取:
ImageIN:GUI交互(建议只得到一张图的情况下使用)
BlindWaterMark:命令行python脚本(CTF题中可能会给两张一样的图,这个时候用此工具更好)
JPHide(.jpeg)
先解压压缩JPEG图像,得到DCT系数;然后对隐藏信息用户给定的密码进行Blowfish加密;再利用Blowfish算法生成伪随机序列,并据此找到需要改变的DCT系数,将其末位变为需要隐藏的信息的值,最后把DCT系数重新压回成JPEG图片。
提取:JPHS
JPHS内置JPHIDE和JPSEEK
JPHide程序主要是实现将信息文件加密隐藏到JPEG图像功能,
JPSeek程序主要实现从用JPHide程序加密隐藏得到的JPEG图像探测提取信息文件
SilentEye(.jpeg)
(四)零宽字符隐写
零宽字符是一种在浏览器中不打印的字符,大致相当于 display: none ,在许多文本应用中也不显示,比如邮箱、QQ、微信、文本编辑器等
html中有三种零宽字符 – 零宽空格、零宽连字、零宽不连字
零宽字符在浏览器中对应的转义字符
零宽空格 ---
零宽不连字 ---
零宽连字 ---
零宽字符在Unicode中的编码为
u200B u200C u200D
(五)word隐写
word中隐藏字段:在Word中选中要隐藏的字段,右击选择字体选项,在效果一栏中有隐藏选项,选中后即可隐藏。默认情况下隐藏文字是不会被打印出来的。
破解:如果想知道是否有隐藏文本,可在文件选项中单击文件→选项→显示文件→选项→显示,在始终在屏幕上显示这些格式标记标签下选择隐藏文字复选框,即可查看,打印选项标签勾选打印隐藏文字即可打印。或者在保存文件后选择文件→检查→检查文件文件→检查→检查文件,查看是否有隐藏文字。
**白色背景下的白字无法被识别出有隐藏的文字 **。
破解:
全选改字体颜色为别的颜色
搜索字符串,例如flag等
word中隐藏图片:word中插入的图片分为嵌入式和非嵌入式,区别在嵌入式会跟着文本的位置产生移动,即有回车后,图片下移。但非嵌入的不会跟着文本走,即有回车后,图片保持原位置不动。
word改后缀名为zip然后解压
(六)PYC隐写
原理是在 python 的字节码文件中,利用冗余空间,将完整的 payload 代码分散隐藏到这些零零碎碎的空间中。
例如,从 Python 3.6开始,有一个较大的改变,就是不管 opcode 有没有参数,每一条指令的长度都两个字节,opcode 占一个字节,如果这个 opcode 是有参数的,那么另外一个字节就表示参数;如果这个 opcode 没有参数,那么另外一个字节就会被忽略掉,一般都为00。
Stegosaurus 是一款隐写工具,它允许我们在 Python 字节码文件 (pyc 或 pyo) 中嵌入任意 Payload。由于编码密度较低,因此我们嵌入 Payload 的过程既不会改变源代码的运行行为,也不会改变源文件的文件大小。Payload 代码会被分散嵌入到字节码之中,所以类似 strings 这样的代码工具无法查找到实际的 Payload。Python 的 dis 模块会返回源文件的字节码,然后我们就可以使用 Stegosaurus 来嵌入 Payload 了。
提示:Stegosaurus 仅支持 Python3.6 及其以下版本
快速入门:-p 要隐藏的文本,-r 显示最大隐藏字节,-x可以解密
(七)音频隐写
DeepSound隐写
DeepSound 是一种隐写术工具和音频转换器,可将秘密数据隐藏到音频文件中。该应用程序还使您能够直接从音频文件或音频 CD 曲目中提取秘密文件。DeepSound 可用作 wave、flac、wma、ape 和音频 CD 的版权标记软件。DeepSound 还支持使用 AES-256(高级加密标准)加密机密文件以提高数据保护。该应用程序还包含一个易于使用的音频转换器模块,可以将多种音频格式(FLAC、MP3、WMA、WAV、APE)编码为其他格式(FLAC、MP3、WAV、APE)。
DeepSound 2.0 Free Download (soft112.com)
MP3stego隐写
MP3stego是命令行工具(也有图形界面的)
decode -X -P
SilentEye隐写
https://sourceforge.net/projects/silenteye/
(八)文件合成与分离
binwalk:可快速分辨文件是否由多个文件合并而成,并将文件进行分离。如果分离成功会在目标文件的目录。
分析文件:binwalk filename
分离文件:binwalk -e filename
formost命令:formost filename -o 输出的目录名
dd:当文件自动分离出错或者因为其他原因无法自动分离时使用
(九)BMP/PDF隐写
提取工具:wbStego Steganography Tool (bailer.at)
针对.bmp/.pdf隐写,得到的_is文件用notepad 打开
【附】检测工具
stegdetect
stegdetect 用来检测jpg类型的图片是否隐藏着其他文件或内容。它可以检测到通过JSteg、JPHide、OutGuess、Invisible Secrets、F5、appendX和Camouflage等这些隐写工具隐藏的信息。
安装:
推荐在linux环境下,下载stegdetect 源码包之后进入其目录,执行下面指令
linux32 ./configure
linux32 make
使用stegdetect时会有一些参数,下面简单罗列一下:
q ——仅显示可能包含隐藏内容的图像
n ——启用检查JPEG文件头功能,以降低误报率。如果启用,所有带有批注区域的文件将被视为没有被嵌入信息。如果JPEG文件的JFIF标识符中的版本号不是1.1,则禁用OutGuess检测。
s ——修改检测算法的敏感度,该值的默认值为1。检测结果的匹配度与检测算法的敏感度成正比,算法敏感度的值越大,检测出的可疑文件包含敏感信息的可能性越大。
d ——打印带行号的调试信息。
t ——设置要检测哪些隐写工具(默认检测jopi),可设置的选项如下:
j ——检测图像中的信息是否是用jsteg嵌入的。
o ——检测图像中的信息是否是用outguess嵌入的。
p ——检测图像中的信息是否是用jphide嵌入的。
i ——检测图像中的信息是否是用invisible secrets嵌入的。
常用指令
stegdetect.exe -tjopi -s 10.0 hide.jpg