CTF隐写

0 阅读15分钟

隐写

[UTCTF2020]docx

查看保存下来的word文档,发现只是关于"迪克"的介绍image-20260225162318741.png

通过"ctrl+A"、"ctrl+F"搜索,没有发现什么有用的信息

了解到dock文件本质是一种压缩包

image-20260225160208329.png

将后缀名改为zip解压,最终在media目录下找到一张图片image-20260225161002347.png

得到flag:utflag{unz1p_3v3ryth1ng},但我是在BUU提交,flag为:flag{unz1p_3v3ryth1ng}

解压1

打开word文档,依旧没什么用的信息image-20260225170921852.png

修改后缀名解压,先去找图片,没找到的话flag很有可能在xml文件里

在解压目录下执行powershell命令cat *.xml | findstr flag > 1.txt,查看1.txt,直接搜索

image-20260225172334171.png

hear_with_your_eyes

使用工具Audacity,右键左边部分查看频谱图,即可查看到flagimage-20260225181926635.pngimage-20260225182724485.png

或者使用在线工具音频转频谱图工具|在线频谱可视化|声学分析图生成 - 音频大师image-20260225182654293.png

可以得到flag: flag{e5353bb7b57578bd4da1c898a8e2d767}

MP3隐写

使用工具MP3stego找到被隐藏的文件

发现需要密码,从MP3文件里寻找image-20260225195828802.png

Decode.exe -X -P 123456 atta.mp3

image-20260225194050232.png

atta.mp3.txt中寻找flag:flag{airrudder s@y you find this MP3 flag}image-20260225200155995.png

提交竟然是错误的。。。

Word文档隐写]字体颜色

进去查看发现就一两个空格和一张贴图image-20260225203933706.png

对那张贴图入手,将它拉长拉大,操作过程中发现贴有字符,有个很明显的标记image-20260225204229360.png

选中它,更改颜色,flag就显现了image-20260225204348738.png

至此已经确定flag在此处,直接复制粘贴提交flag{f88fc63705a085c2109597a469798eb5}

波形图

将下载好的音频拖到Audacity,发现波形长这样image-20260225221858427.png

频谱图更是没眼看,就不贴了,经过我一顿摸索,点击到了缩放开关,但一开始没这么顺利,由于之前的"ctrl+滚轮"以及基准线混乱导致胡乱地放大缩小,拿到的数据根本不全,所以点击波形图任意地方,再点击"home"键将基准线放在波形图最左边的时候,再点击缩放开关,就得到下面这波形 image-20260225232601502.png

发现这个波形确实很有规律,每条线平行,一上一下不对齐,代表的不就是仅有0和1的二进制吗,不动基准线慢慢点击放大(或"ctrl+1"),知道好数为止,以上1下0为标准,记下波形对应的数字,用snipaste截图工具标记完成后全选标记的数字,并将空格去除后拿到原始二进制数据image-20260226075721634.png

1 1 00110 11 01100 110000 111 00 1111111  0111 010 111 0110000 101 0111 01 0101 011 00 110111 010 1110 111 011 01110 1111 00 111111 0 1
#去除空格后的数据
110011011011001100001110011111110111010111011000010101110101010110011011101011101110110111011110011111101

拿去给在线网站转ASCII,发现结果不是一团浆糊,要么就是非法二进制,想起之前使用过一款工具CaptfEncoder,死马当活马医了image-20260226082330833.png

原本是直接贴上去,提示数字过大,删掉到只剩8位的时候解出了乱码,直到剩下7位的时候正常了,经了解这是7位一组用于早期的基本ASCII字符集的,现在基本是8位一组,怪不得解不出

既然确定7位一组,写一个简单的脚本

def b_ascii(b_str):
    return ''.join(chr(int(b_str[i:i+7],2)) for i in range(0,len(b_str),7))
b_input = "110011011011001100001110011111110111010111011000010101110101010110011011101011101110110111011110011111101"
print(b_ascii(b_input))

image-20260226085422562.png

差点就忽略了第一条线,刚好跟左边黑线重合,不过有惊无险,拿到了二进制数据,flag:flag{W0W*funny}

莫尔斯电码

下载的时候听了这个音频,像是电报,而电报使用的是摩尔斯电码image-20260226100555031.png

将音频拖到Audacityimage-20260226100744376.png

摩尔斯电码主要由"."和"-"构成,以最细的波为".",较粗的为"-",分析写出电码符号image-20260226101922045.png

..-. .-..	   .-	  --.		   ..	  ... 		.- 	..	.-.  .-.     ..-    -..     -..     .   .-.		     --	---   .-.     ...    . 	   -.-.	---    -..     .

"notepad++"替换掉多余的Tab和空格

..-. .-.. .- --. .. ... .- .. .-. .-. ..- -.. -.. . .-. -- --- .-. ... . -.-. --- -.. .

使用在线工具在线摩斯密码翻译器-在线工具大全解出

image-20260226110449249.png

拿到flag:FLAGISAIRRUDDERMORSECODE,即flag{flagisairruddermorsecode},又是这个"空气舵",遇见他我的flag就提交不成功

隐藏文字

打开题目,发现竟一个字都没有

image-20260226111813631.png

众所周知,word有隐藏文字的选项,直接"ctrl+A"并打开字体设置将隐藏取消image-20260226112904527.pngimage-20260226113029746.png

flag显现image-20260226113103554.png

flag:flag{hahaha,you find me!!!}

[NISACTF 2022]huaji?

题目给了一个格式不明的文件,通过powershell查看是否有flag字段image-20260226115036707.png

盲猜是一个压缩包。。。

好吧猜错了,使用010查看image-20260226115802013.png

翻了一下,觉得应该从文件签名入手,毕竟一开始给的文件连是什么格式都不确定,于是去找了一些常见图片格式签名

格式文件签名(十六进制)对应的ASCII字符(部分)
JPEGFF D8 FFÿØÿ
PNG89 50 4E 47 0D 0A 1A 0A.PNG....
GIF47 49 46 38(GIF87aGIF89a)GIF8
BMP42 4DBM
WebP52 49 46 46 .... 57 45 42 50RIFF....WEBP

可以发现,这是JPEG图片,JPEG有结束符"FF D9",先打开看看

打开就是这样,耍我呢

image-20260226120355967.png

再用010打开,搜索结束符"FF D9",或者点击"unknownPadding"大概率就找到了image-20260226122404205.png

那结束符之后的部分就是隐藏的内容了,而且开头为"50 4B 03 04",或者那个PK..,标准的ZIP,就是一个ZIP压缩包,看来是需要分离工具,使用在线工具foremost文件提取工具image-20260226125238662.png

解压后发现有两个文件夹:"jpg"和"zip","zip"里还有一个ZIP压缩包,"jpg"里存的就是之前耍我的图片

直接解压发现出了问题,试试7z能不能解压image-20260226130142620.png

提示需要输入密码image-20260226130313246.png

对于jpg图片还有一种手段--EXIF,可以通过属性查看

那就查看属性,发现这个"照相机型号"很可疑image-20260226120836348.png

6374665f4e4953415f32303232,这看起来很像十六进制,看的时候没觉得,贴在这的时候就感觉有点像了,不说了,拿去解密试试

解出来的结果为ctf_NISA_2022,那这就是解压密码了

生成了flag.txtimage-20260226131135446.png

拿到flag:flag{Nls@_FumYEnnOjy}

图片宽高

使用powershell的curl下载

curl -O "a1.png" https://files.buuoj.cn/files/a3a87d92898dab2f5e96fd972411e3d1/atta.png

打开是这样的,图片选的不错image-20260226132924268.png

发现图片尺寸不太对劲image-20260226173937876.png

校验CRC

import struct, zlib

with open('a1.png','rb') as f: m=f.read()
crc_s=struct.unpack('>I',m[29:33])[0]
w,h=struct.unpack('>II',m[16:24])
ihdr_d=m[12:16]+m[16:24]+m[24:29]
crc_c=zlib.crc32(ihdr_d)&0xffffffff
print(f"计算CRC: 0x{crc_c:08x} 存储CRC: 0x{crc_s:08x}")

image-20260226185334735.png

明显不对等,求出正确的宽高并修改

import struct, binascii

with open('a1.png', 'rb') as f:
    m = f.read()
crc = int(m[29:33].hex(),16)  #图片中的CRC值
found = False
for i in range(1000):
    for j in range(1000):
        c = m[12:16] + struct.pack('>i', i) + struct.pack('>i', j) + m[24:29]
        crc1 = binascii.crc32(c) & 0xffffffff        
        if crc1 == crc:
            print(f"found: {hex(i)} x {hex(j)} ({i} x {j})")
            found = True
            break  #内层循环退出    
    if found:
        break  #外层循环退出
if not found:
    print("not found")

正确的尺寸image-20260226185804812.png

用010打开a1.png,image-20260226190314898.png

16-19字节: 00 00 02 58表示图片的宽,20-23字节: 00 00 02 26表示图片的高,修改回正确尺寸image-20260226190701111.png

浏览图片(部分截图)image-20260226184621638.png

成功拿到flag:flag{8648ad48a4c8777e1089b5e58e829901}

其他方法:

import struct, zlib, sys

if len(sys.argv) != 2:
    print("python fix.py photo.png")
    sys.exit()
png = sys.argv[1]
with open(png, 'rb') as f:
    data = f.read()
#查找IHDR块位置
ihdr_s = data.find(b'IHDR') - 4
if ihdr_s < 0:
    print("未找到IHDR块")
    sys.exit()
#解析IHDR数据
width, height = struct.unpack('>II', data[ihdr_s+8:ihdr_s+16])
print(f"original: {width} x {height}")
#计算当前CRC
ihdr_d = data[ihdr_s+8:ihdr_s+21]
crc = struct.unpack('>I', data[ihdr_s+21:ihdr_s+25])[0]
#搜索正确尺寸
for w in range(1000): #尺寸在600左右,所以先小范围搜索,如果没找到再修改
    for h in range(1000):
        new_d = struct.pack('>IIBBBBB', w, h, *struct.unpack('>BBBBB', ihdr_d[8:13]))
        if zlib.crc32(b'IHDR' + new_d) & 0xffffffff == crc:
            print(f"found: {w} x {h}")
            #修复文件
            fixed_d = data[:ihdr_s+8] + struct.pack('>II', w, h) + data[ihdr_s+16:]
            output = png.replace('.png', '_f.png')
            with open(output, 'wb') as f:
                f.write(fixed_d)
            print(f"saved: {output}")
            sys.exit()
print("not found")

pdf隐写

使用curl下载后先浏览内容image-20260226192442107.png

鉴于这是pdf隐写,其常用工具为wbStego4open,隐写需要密码,解密自然也需要,先cat一波看看有没有密码

cat a.pdf | findstr pass
#%%EOF pass is air123

下好wbs43open-w32.zip解压即食,选择pdf后输入找到的密码密码image-20260226202519232.png

自定义输出文件名称后继续image-20260226203017013.png

拿到flag:flag{This_IS_pdf_stego}

附加字符串

"另存为"后首先看看图片数据有没有藏东西

cat atta.png | findstr flag

什么也没有。。。

使用010打开,一搜flag,有了image-20260226213338038.png

成功拿到flag: flag{3c3cce46bfa0b813682ecd32331a1640}

LSB

对于LSB,使用zsteg

Linux下zsteg安装:gem install zsteg

使用curl下载图片后,先试试基本命令zsteg atta.pngimage-20260226224055094.png

没有发现有用的信息,尝试所有组合zsteg atta.png -a | grep flagimage-20260226224222954.png

还真有结果,这不就拿到了flag: flag{7d39d64fb69346d7e61a115317013da2}

[2019RoarCTF]黄金 6 年

打开看了10秒的视频,发现书籍跳出了二维码又迅速消失,二维码应该有东西,使用在线网站Video to JPG Converter: Convert Various Video to JPG (Free)(比较糊,但够用)或者工具DVDVideoSoft进行转图image-20260227104905757.png

选择帧率转换即可,尽量选择帧率大一点的,比如"每个视频帧"、"总计-500-视频帧"等image-20260227110230846.png

我选的是"每个视频帧",发现在出现书的最后一帧或者第一帧存在二维码(有三本书是最后一帧,一本是第一帧),如果使用的其他帧率,可以使用光影魔术手批处理图片,使二维码易识别

先随便选择一张图片,滤镜-铅笔素描,使用炭笔并将炭笔拉到10image-20260227140218547.png

确定之后点击保存动作,并输入模板名称即可,至此就可以进行批处理

image-20260227104432042.png

点击插入模板后选择你刚创建的模板image-20260227142317860.png

设置输出路径后直接开始,完成后一个个查看图片,二维码相较之前还是挺明显的,找到含二维码的图片如图:erweima.jpg

扫描得出结果分别为为:key1:ikey2:wantkey3:playkey4:ctf

结合起来就是 iwantplayctf,这总不能是flag吧,继续分析MP4文件

使用010打开,发现"Unknown box type"image-20260227145351157.png

盲猜base64,先拿去解密再说image-20260227145705250.png

我去,怎么是乱码,眨眼一看,开头Rar,flag.txt夹在乱码中,。。。RAR压缩包!

我们将解码后的数据作为rar压缩包,先从010复制这段base64放入新建的1.txt中,再将解码后的数据作为压缩包

certutil -decode .\1.txt 1.rar

用7z解压,发现需要密码image-20260227153447181.png

那么之前找到的key就是了image-20260227153624140.pngimage-20260227153717544.png

拿到flag:roarctf{CTF-from-RuMen-to-RuYuan},在BUU正确的flag:flag{CTF-from-RuMen-to-RuYuan}

[附加字符串]图种

这图也是老朋友了image-20260227163357211.png也是没有发现什么,010上线,发现chunk[22]的值有异常image-20260227160341542.png

第一眼看到了flag.txt,第二眼看到"PK.."确定是zip压缩包,如法炮制,选中从"PK"一直到末尾的文本后,点击左上角的"Edit","copy as base64"后复制到新建的txt文件,通过powershell命令得到zip文件,解压没有密码,直接拿到flag:flag{b7505ab7295fc4fbe5d835e92fc4933b}

或者使用binwalkforemost之类的工具直接提取压缩包

盲水印

1.jpg

给了两张图片:blind1.png、blind2.png,可以还原盲水印,先下载BlindWaterMark,并安装环境:

#使用清华源
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
python bwmforpy3.py decode blind1.png blind2.png blind3.png

blind3.png

拿到flag: flag{aa3379655baa69c7fb7a7e469c8be88e}

EXIF

EXIF可通过属性-详细信息查看image-20260227175628845.png

直接找到了,flag: flag{3b593bffdfac8c9e634c44937e431c5e}

[SCTF 2021]in_the_vaporwaves

将题目给的c.wav音频拖到Audacity进行解析image-20260227182243916.png

整体看着互为镜像,先看开头的波形,放大看看细节image-20260227192155454.png

可以看出左右声道波形呈镜像关系,左右声道"反相"了,当左右声道包含"反相"信号时,在单声道混合中会相互抵消,从而将信息显现或隐藏,即左=莫尔斯+背景;右=莫尔斯-背景,让左右声道混合,抵消背景音,从而让莫尔斯显现

通过"轨道-混音-混合立体声至单声道",发现波形成了这一条断断续续的线image-20260227211509718.png

听听看,果然是莫尔斯电码image-20260227211906864.png

将它放大到好辩认的程度,记下这莫尔斯电码,有点长,"ctrl+i"将其分割成两段

第一段的数据image-20260227212924047.png

 ...	-.-.	     -	   ..-.		-..    .    ...    .----		.-.	...--		..--.-		 -..	  .-.	   ..    ...-     .    ...	 ..--.-

第二段image-20260227214136840.png

..	-.	-  -----	 	..--.-		 ...-		.-	.--.	  ---		.-.	.--		.--.-.		...-    .   ...

得到完整莫尔斯电码

 ... -.-. - ..-. -.. . ... .---- .-. ...-- ..--.- -.. .-. .. ...- . ... ..--.- .. -. - ----- ..--.- ...- .- .--. --- .-. .-- .--.-. ...- . ...

拿去解密image-20260227220032983.png

即flag: SCTFDES1R3_DRIVES_INT0_VAPORW@VES

借鉴:

[SCTF 2021]in_the_vaporwaves - snowhy的博客

解压2

将docx文件改后缀解压后,先查看图片文件夹中是否有东西

我去,还是有些轻松的image-20260228061123003.pngimage2.png

拿到flag: flag{wow!!!you real find me}

总结

经典隐写题到这里就差不多结束了,对于图片隐写中的"附加字符串",我们只要在010中找到"unknown"部分就行了,当然你也可以找到文件尾,后面的部分差不多就是隐藏的内容了;"图种"也差不多,只不过多了分析文件类型分离步骤,如果用工具的话可能也不用分析;"EXIF(Exchangeable image file format/可交换图像文件格式)"通常在JPEG(jpg)、TIFF、RIFF等文件中,右键点击属性即可查看,给了一张JPEG图片,可能就要注意查看属性了;"图片宽高"题目通过修改原图片的宽高,将包含有效信息的部分隐藏,不过使用在线网站或者代码编辑器查看时显示异常,从而快速判断是否跟图片宽高有关,有异常则可以修复图片,拿到关键信息;"LSB"题目就是通过修改图片中的一些RGB最低位的二进制数据,使其部分组合起来为A,当然,是A的二进制,使用zsteg能够找到一些关键信息;"盲水印"题目一般给两张几乎一样的图片,可能有点颜色上的差异,这就是进行盲水印隐写时用非黑白图片作为盲水印导致的,音频、图片、数字图书等都可以使用,对于图片,可以使用BlindWaterMark提取盲水印。

音频隐写通常是隐藏莫尔斯电码信号、文件、二进制编码等,还有就是在频谱里隐藏,听起来声音有些异常。莫尔斯电码的音频非常有辨识度,听一下就听出来了,小时候的谍战片没白看,如果将莫尔斯电码隐藏起来,耳朵够灵也可以听出端倪,最好是混音一下,可能就出来了;MP3音频如果隐藏了文件,可以通过MP3stego解离出来,密码需要自己去找;波形图中常包含关键信息,所以,波形图中的波形图才是我们要去找的,像莫尔斯电码,音频开头或者末尾的微小波形,找到大概就能拿到关键信息了;意义不明的波形但有二元规律,可以考虑将其转换为二进制数据并解密。

视频隐写主要是将关键信息隐藏在视频帧中,剩下的就是一般思路了

文档隐写对于docx文件,可能是将其转回压缩包,并在其中添加图片或者在文件中写入关键信息,也可能利用word隐藏文字;PDF主要是通过工具隐写,思路跟MP3藏文件差不多。。。

靶场:BUUCTF在线评测