图片隐写
JPG图片
JPG图片十六进制开头FF D8,结尾 FF D9
JPG无法使用lsb隐写
1、插入文件尾
结尾后有隐藏base64编码,解码得到flag
2、文件中间
可ctrl+f 搜索flag格式文本,如果是base64编码则搜索ZmxhZ3,不同题目,不同应对
3、插入压缩包
在中间发现flag.txt文件
binwalk 文件名 :可查看图片信息
隐藏了个txt文件
第一种方法:binwalk -e 文件名 :提取文件(如果报错,试一下这个binwalk -e [文件] --run-as=root)root权限使用
第二种方法:
foremost 文件名(如果有output就用foremost -T文件名)
右键以root身份打开(不知道为什么改权限不能):破案了,chmod 777 -R ...才是修改他和他的子文件
搭配:binwalk查看信息,foremost提取内容
4、双图隐写
stegsolve:
1.File Format:显示图片的具体信息
2.Data Extract:数据提取
3.Steregram Solve:立体试图 可以左右控制偏移
4.Frame Browser:帧浏览器 主要是对GIF之类的动图进行分解,把动图一帧帧的放
5.Image Combiner:拼图,图片拼接
用frame browser,发现flag
PNG图片
- IHDR:文件头数据块,包含图像的基本信息和属性。
- IDAT:图像数据块,包含图像像素数据。
IHDR
(固定)八个字节 89 50 4E 47 0D 0A 1A 0A为png的文件头
(固定)四个字节00 00 00 0D 代表IHDR头的长度为13
(固定)四个字节 49 48 44 52(当这四个数放在ascll码中就是IHDR)
(可变)13位数据块(IHDR头)
00 00 02 7F代表该图片的宽
00 00 00 AF代表该图片的高
后五个字节依次为:
Bit depth(图像深度)、ColorType(颜色类型)、Compression method(压缩方法)、Filter method(滤波器方法)、Interlace method(传输方法)
剩余四字节为该png的CRC检验码,由十七个字节(IHDR+IHDR头)进行crc计算得到。
IDAT
IDAT 到 IEND之间是IDAT块的数据
文件尾:AE 42 60 82
5、lsb1 隐写二维码
关于lsb:网络安全 - Lsb图片隐写 - 个人文章 - SegmentFault 思否
随便按按,再随便扫扫,拿下
6、lsb2 隐写ascii码
第一种方法:
Data Extract,RGB全0(一般是这样,原理不知),ascll码以PK开头(PK开头一般是zip文件),save bin保存为zip
file 文件:查看该文件是什么格式
ELF是linux中的可执行文件
直接执行,如果不行,属性把勾勾上
第二种:
zsteg
zsteg可以检测PNG和BMP图片里的隐写数据。
zsteg 文件(zsteg -a 文件就是查看所有通道的信息)
发现有个zip压缩包,提取一波
zsteg -e (通道) 文件 -> 保存的文件
zsteg -e b1, 1.png-> out.zip
7、lsb3 双图比较
给了两张图,combiner结合一下,左点点,右点点
保存,并打开该二维码,在R、G、B分别为0时,得到三个二维码,分别扫一扫,得到三串字符串
DES
6XaMMbM7
U2FsdGVkX18IBEATgMBe8NqjIqp65CxRjjMxXIIUxIjBnAODJQRkSLQ/+lHBsjpv1BwwEawMo1c=
很明显DES加密,直接网站走起,得到flag:ctf{67a166801342415a6da8f0dbac591974}
7、lsb3 终极双图
binwalk发现有东西
foremost提取出两张图片
stegslove 结合一下(如果全黑代表着两张图片一模一样,每一个字节都一样(因为异或运算,00为黑)
xor发现全黑,难道这两个图片一模一样,别人的wp告诉我No
放大5000倍的景象
然后就自闭了,给个类似的wp,不弄了0x005图片隐写之双图对比+RGB通道隐写-CSDN博客
lsb解密(cloacked-pixel)
带密码:
python2 lsb.py hide <img_file> <payload_file> <password>
python2 lsb.py extract <stego_file> <out_file> <password>
不带密码:
与原图异或
与国际象棋棋盘原图异或
与windowsxp界面异或:https://www.anquanke.com/post/id/228129#h2-5
10、PNG IHDR1
一顿binwalk、foremost操作后,什么没发现,题目是IHDR,试试是不是修改宽高,直接化身脚本小子
# 判断宽高是否有问题
import zlib
image_data = open('图片路径', 'rb')
bin_data = image_data.read()
crc32key = zlib.crc32(bin_data[12:29]) # 使用函数计算
if crc32key == int(bin_data[29:33].hex(), 16): # 对比算出的CRC和原本的CRC
print('宽高没有问题')
else:
print('宽高被改了')
# 修改
import binascii
import struct
crcbp = open("图片路径", "rb").read() # 打开图片
crc32frombp = int(crcbp[29:33].hex(), 16) # 读取图片中的CRC校验值
print(crc32frombp)
for i in range(4000): # 宽度1-4000进行枚举
for j in range(4000): # 高度1-4000进行枚举
data = crcbp[12:16] + \
struct.pack('>i', i) + struct.pack('>i', j) + crcbp[24:29]
crc32 = binascii.crc32(data) & 0xffffffff
# print(crc32)
if (crc32 == crc32frombp): # 计算当图片大小为i:j时的CRC校验值,与图片中的CRC比较,当相同,则图片大小已经确定
print(i, j)
print('hex:', hex(i), hex(j))
exit(0)
执行第一个脚本时,显示宽高被改了,第二个:
用winhex修改宽高值后拿下
小tips:丢进linux里面发现图片打不开,应该也可以说明crc错误
10、PNG IHDR2
一来就裂开,直接丢进winhex修改png头
改完后打开,一眼图片没显示完
再次化身脚本小子
11、PNG IDAT
pngcheck
pngcheck -v 文件 #查看数块数据
png图片里的IDAT块数据会以IDAT开头,后面跟着它的数据
IDAT 块只有当上一个块充满时(一个IDAT块满长度为65524),才会继续一个新的块。
pngcheck 一下,发现倒数第二块还没满,说明最后一块有问题
脚本小子又来了,最后一段打开winhex发现是zlib(78 9C文件头是zlib标志)
# 解压zlib
import zlib
import binascii
id = ''
result = binascii.unhexlify(id)
print(result)
print()
result = zlib.decompress(result)
print(result)
尝试了二进制转换ascll失败,wp启动
发现这个01串长度是625(c的strlen函数算)
脚本小子启动
# 01串生成二维码
from PIL import Image
# MAX x MAX 矩阵
MAX = 25
pic = Image.new("RGB",(MAX, MAX))
str = ""
i=0
for y in range (0,MAX):
for x in range (0,MAX):
if(str[i] == '1'):
pic.putpixel([x,y],(0, 0, 0))
else:
pic.putpixel([x,y],(255,255,255))
i = i+1
pic.show()
pic.save("flag.png")
14 image photography隐写
Image Steganography
通道篇
lsb隐写脚本
# lsb隐写
from PIL import Image
img = Image.open('./pic/low.bmp')
img_tmp = img.copy()
pix = img_tmp.load()
width, height = img_tmp.size
for w in range(width):
for h in range(height):
if pix[w, h] & 1 == 0:
pix[w,h] = 0
else:
pix[w, h] = 255
img_tmp.show()
提取像素点脚本
提取像素点之后,转换成伪二进制,然后再转为ascll码
#提取像素点
## 尝试提取二进制
from PIL import Image
im = Image.open(路径)
width,height = im.size
bin = ''
for x in range(width):
for y in range(height):
r,g,b = im.getpixel((x,y))
if r > 127:
bin += '1'
else:
bin += '0'
with open('out.txt','w')as f:
f.write(bin)
YCbCr
YCbCr与RGB的相互转换
Y=0.299R+0.587G+0.114B
Cb=0.564(B-Y)
Cr=0.713(R-Y)
R=Y+1.402Cr
G=Y-0.344Cb-0.714Cr
B=Y+1.772Cb
RGB转YCbCr
import cv2
import cv2 as cv
img=cv2.imread(路径)
src_value=cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
a, b, c = cv.split(src_value) #使用cv.split分离通道
cv.imwrite('a.png', (a % 2) * 255) #对三个通道中的数据分别根据奇偶做二值化处理,并分别保存为图片
cv.imwrite('b.png', (b % 2) * 255)
cv.imwrite('c.png', (c % 2) * 255)
RGB隐写
屏幕截图cve
CVE-2023-28303
大概原理:截图裁剪只会覆盖原数据,在截的那段填上IEND尾标志符,后面的数据不会删除
适用:GIF和PNG
前提:修复图片宽高
python3 .\gui.py
其他
提取文件名
import os
path = 路径
filenames = os.listdir(path)
str = ''
for file in filenames:
str += file[:-4]
print(str)
去除多余字符
# 去除多余字符
with open(读取文件路径,'r') as file1:
with open(写入文件路径,'w') as file2:
str = file1.read()
for c in str:
if c not in (字符):
file2.write(c)
RGB转图片
1、puzzlesolver
2、脚本
from PIL import Image
x = 338 #宽
y = 1582 #高
img = Image.new("RGB",(x,y))
with open(路径) as file:
for width in range(0,x):
for height in range(0,y):
line = file.readline()
rgb = line.split(' ')
img.putpixel((width,height),(int(rgb[0]),int(rgb[1]),int(rgb[2])))
img.show()
因式分解网站因式分解计算器 - Calculator.iO
二进制转图片或二维码
1、puzzlesolver
2、转图片脚本
from PIL import Image
# 二维码
MAX = 二维码宽高
pic = Image.new("RGB",(MAX1, MAX2))
str = "二进制数据"
# # 图片
# MAX1 = 351
# MAX2 = 64
# pic = Image.new("RGB",(MAX, MAX))
# str = "二进制数据"
i=0
for y in range (0,MAX):
for x in range (0,MAX):
if(str[i] == '1'):
pic.putpixel([x,y],(0, 0, 0))
else:
pic.putpixel([x,y],(255,255,255))
i = i+1
pic.show()
pic.save("flag.png")
09 20
1、摩斯密码
ctfshow-misc详解(持续更新中) - 清纯少女小琪 - 博客园 (cnblogs.com)
转为ascii码,分别为
09→ tab符
20→空格
0A→\r
0D→\n
tab 替换 -
空格 替换 .
tab 替换/
\n 替换/
2、snow空白隐写
3、wbStego4open
会把插入数据中的每一个ASCII码转换为二进制形式,然后把每一个二进制数字再替换为十六进制的20或者09,20代表0,09代表1。
EXIF隐写
exif:记录数码照片的属性信息和拍摄数据
1、直接查看属性有没有备注
2、
exiftool
exiftool 文件,即可显示照片的信息
8、exif隐写1
方法一:直接看属性
方法二:
exiftool
9、exif隐写2
直接看属性有信息,但不全,用exiftool
base64解密拿下flag
GIF
文件头:47 49 46 38 或 GIF89A(47 49 46 38 39 61)
文件尾:00 3B
gif文件格式:GIF文件格式 - 简书 (jianshu.com)
12、GIF1 空间域
空间域:一帧帧的图片构成,每一帧的图片,都成了隐藏信息的一种载体
发现是GIF图,直接Frame Browser
可将每张图片分离出来,然后改一下rgb更好看
Y2F0Y2hfdGhlX2R5bmFtaWNfZmxhZ19pc19xdWl0ZV9zaW1wbGU=
!注意I不要写成1
13、GIF2 时间域
时间域:每一帧间的时间间隔作为信息隐藏的载体
解压后发现文件裂开了,直接开始修复,这个gif用GIF89A修复
winhex增加数据 右键-->编辑-->剪贴板数据-->粘贴,然后保存
我试着丢进Frame Browser 发现全部一样的,按照题目提示,时间域
ImageMagick
magick identify 文件:显示每一帧的情况
magick identify -format "%s %T\n" 文件 以序号和%T,可提取出有用的东西(我也不知道为什么有用),只留%T的话就剩下后面的10/20
这里的20和10 代表0、1或者1、0(两种都试一下)
丢进word里,搞出一堆0、1
最后二进制转ascll码得到flag
PuzzleSolver
TIFF
文件头:49 49 2A 00
16、Fireworks图层隐写
给了张大白和一个txt,图片binwalk后有jpg和tiff,foremost和binwalk -e
都提取不出来,那就手动提取
提取后tiff图片,有26个图层,后面纯脑洞,丢个wp
ARW(RAW)
提取lsb
import rawpy
with rawpy.imread(图片路径) as raw:
with open(存储路径, 'wb') as f:
raw_data = raw.raw_image
bits = ''
for x in range(raw_data.shape[0]): # 4024
for y in range(raw_data.shape[1]): # 6048
bits += '1' if (int(raw_data[x][y]) & 1) else '0'
if len(bits) == 8:
f.write(bytes([int(bits, 2)]))
bits = ''
工具篇
17、steghide隐写01
适用:JPEG(jpg),BMP,WAV,AU
本身以为题目的意思是藏在图层下面,stegslove一顿操作后,发现啥都没有,搜了一下原来steghide是工具,由于刚开始我没下,就直接binwalk了一下
提取出好多东西,一个jpg照片,一个加密pdf文档,以及原本的png照片
steghide
steghide工作流程:一个需要隐藏的文件用steghide的 参数加命令 隐藏进图片中,然后设置密码
steghide extract -sf 1.jpg -p 123456 #隐藏信息从载体中分离出来
#-sf 参数
#test.jpg 图片名称
#-p 密码参数,后面空格跟密码,无密码可不加参数
试了一下,发现有密码。
stegseek
用于爆破steghide信息隐藏时设置的密码
有密码:stegseek 图片 密码字典
检测和无密码提取元数据:stegseek --seed 图片
。。真尴尬,密码就是空,没事,拿到flag就行
18、steghide隐写01
有了前车之见,直接丢进kali里binwalk发现没东西,就steghide然后试一下空密码,发现不行,就用stegseek
拿下(本身还在想这是什么密码要解密的,后面改了一下ISO。。。那串东西发现KEY后面的两个特殊符号会变,就知道不是用的,给后面的字符串套上flag头提交)
19、outguess隐写
outguess
适用:PPM、PNM 和 JPEG(jpg)
工作过程:
outguess -k “my secret key” -d hidden.txt 1.jpg 2.jpg
加密之后,1.jpg会覆盖2.jpg,hidden.txt的内容是要隐藏的东西。
outguess -r 图片 -t 文件
图片就是那张隐写图,文件是将隐藏的内容输出到该文件中
20、F5隐写
适用:jpg
原理:【隐写术】F5隐写_Em0s_Er1t的博客-CSDN博客
在F5工具目录下使用命令
java Extract refresh.jpg -p refresh
密码一般是图片名,也可能是其他
21、stegdetect隐写
Stegdetect程序主要用于分析JPEG文件。 用Stegdetect可以检测到通过JSteg、JPHide、OutGuess、Invisible Secrets、F5、appendX和Camouflage等隐写工具隐藏的信息。
stegdetect.exe -tjopi -s 10.0 文件 #tjopi每个都有自己作用 -s是设置敏感度,默认为1,这条命名是判断隐写工具
发现用jphide隐写
但是jphide解密时需要密码。
图片是个二维码,草料解码一下
然后就懵逼了,看了wp后才知道这是培根密码:CTF-隐写术(二)_pcat.jpg-CSDN博客
拿到flag:CTF{123pcat321_Jphide}
22、Jphide隐写
丢进winhex里
里面藏着个hint.txt,binwalk+提取
拿到密码,jphide解密拿到flag
23、Jstego隐写
适用:JPEG(jpg)
原理与代码:用到matlab:JSteg信息隐藏算法_Dennis F的博客-CSDN博客
使用工具:
24、jsteg
适用JPEG、jpg
.\jsteg-windows-amd64.exe reveal ?.jpg > ?.txt
25、PixelJihad
适用:jpg,bmp,png
A JavaScript steganography tool
26、盲水印
适用:全(maybe)
java盲水印
java -jar .\BlindWatermark-v0.0.3-windows-x86_64.jar decode -c+需要解盲水印的图片名称+解完后的图片名称
在软件目录下打开终端使用
python盲水印
给一张图时:
两张图时:
python2 和 python3 都试一下
2、可选大小
guofei9987/blind_watermark: Blind&Invisible Watermark ,图片盲水印,提取水印无须原图! (github.com)
pip3 install blind-watermark
bwm1 = WaterMark(password_wm=1, password_img=1)
# notice that wm_shape is necessary
bwm1.extract(filename='output/embedded.png', wm_shape=(128, 128), out_wm_name='output/extracted.png', )
27、snow空白隐写
SNOW.EXE -C -p "..." 文件
html文件:Snow web-page encryption/decryption (misty.com)
28、python剑龙解密
适用于.pyc .pyo .py文件
python3.6 .\stegosaurus.py -x "D:\比赛ing\pyc\flag.pyc"
29、图片拼接(ImageMagick+gaps)
ImageMagick
montage 输入文件路径 -tile 长宽数量 -geometry 拼图间隙 输出路径
magick montage ?.? -tile nxm -geometry +0+0 ?.png
magick montage *.png -tile 36x36 -geometry +0+0 flag.png
gaps
gaps run 上面拼好的图片 保存图片路径 --generations=? --population=? --size=?
gaps run ./flag.png nealg.png --generations=30 --population=200 --size=40
--size 拼图块的像素尺寸(每一小块的大小,p成正方形)
--image 指向拼图的路径(可以缺省,让他自己拼)
如果结果不理想,可以尝试改变--generations
和--population
,把--population
调大
其他属性
--generations 遗传算法的代的数量
--population 个体数量
--verbose 每一代训练结束后展示最佳结果
--save 将拼图还原为图像
二维码拼接
QRazyBox - QR Code Analysis and Recovery Toolkit (h3110w0r1d.com)
30、silenteye
31、bftools
理论上适用于png文件,但jpg也可用,可能适用全部
bftools.exe decode braincopter 图片 > 文件
bftools.exe decode braincopter flag.png > 1.txt
bftools.exe decode brainloller 图片 > 文件
bftools.exe decode brainloller flag.png > 1.txt
32、s-tools
适用:wav、bmp、gif
右键reveal,加密方式一个个试
33、马赛克修复
tool:BishopFox/unredacter: Never ever ever use pixelation as a redaction technique (github.com)
首先把马赛克区域裁剪下来
常见的像素块大小:8x8、4x4、16x16和32x32,这里的每一小块是8x8(一般是8x8),裁剪工具裁剪一下,高约是32,就截取至32,长截取也是找8的倍数,差不多就是336
preload.ts,设定猜测的字符集和每小块大小
cnpm install
cnpm start
34、wbStego
适用:BMP,TXT,HTM,PDF
zsteg后有wbStego,但png不能用wbStego,此时可以尝试lsb解密(cloacked-pixel)
35、Free File Camouflage
36、翻转+图像增强(反卷积)
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2
# local module
def blur_edge(img, d=31):
h, w = img.shape[:2]
img_pad = cv2.copyMakeBorder(img, d, d, d, d, cv2.BORDER_WRAP)
img_blur = cv2.GaussianBlur(img_pad, (2 * d + 1, 2 * d + 1), -1)[d:-d, d:-d]
y, x = np.indices((h, w))
dist = np.dstack([x, w - x - 1, y, h - y - 1]).min(-1)
w = np.minimum(np.float32(dist) / d, 1.0)
return img * w + img_blur * (1 - w)
def motion_kernel(angle, d, sz=65):
kern = np.ones((1, d), np.float32)
c, s = np.cos(angle), np.sin(angle)
A = np.float32([[c, -s, 0], [s, c, 0]])
sz2 = sz // 2
A[:, 2] = (sz2, sz2) - np.dot(A[:, :2], ((d - 1) * 0.5, 0))
kern = cv2.warpAffine(kern, A, (sz, sz), flags=cv2.INTER_CUBIC)
return kern
def defocus_kernel(d, sz=65):
kern = np.zeros((sz, sz), np.uint8)
cv2.circle(kern, (sz, sz), d, 255, -1, cv2.LINE_AA, shift=1)
kern = np.float32(kern) / 255.0
return kern
if __name__ == '__main__':
print(__doc__)
import sys, getopt
opts, args = getopt.getopt(sys.argv[1:], '', ['circle', 'angle=', 'd=', 'snr='])
opts = dict(opts)
try:
fn = args[0]
except:
fn = 路径
win = 'deconvolution'
img = cv2.imread(fn, 0)
# if img is None:
# print('Failed to load fn1:', fn1)
# sys.exit(1)
img = np.float32(img) / 255.0
cv2.imshow('input', img)
img = blur_edge(img)
IMG = cv2.dft(img, flags=cv2.DFT_COMPLEX_OUTPUT)
defocus = '--circle' in opts
def update(_):
ang = np.deg2rad(cv2.getTrackbarPos('angle', win))
d = cv2.getTrackbarPos('d', win)
noise = 10 ** (-0.1 * cv2.getTrackbarPos('SNR (db)', win))
if defocus:
psf = defocus_kernel(d)
else:
psf = motion_kernel(ang, d)
cv2.imshow('psf', psf)
psf /= psf.sum()
psf_pad = np.zeros_like(img)
kh, kw = psf.shape
psf_pad[:kh, :kw] = psf
PSF = cv2.dft(psf_pad, flags=cv2.DFT_COMPLEX_OUTPUT, nonzeroRows=kh)
PSF2 = (PSF ** 2).sum(-1)
iPSF = PSF / (PSF2 + noise)[..., np.newaxis]
RES = cv2.mulSpectrums(IMG, iPSF, 0)
res = cv2.idft(RES, flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT)
res = np.roll(res, -kh // 2, 0)
res = np.roll(res, -kw // 2, 1)
cv2.imshow(win, res)
cv2.namedWindow(win)
cv2.namedWindow('psf', 0)
cv2.createTrackbar('angle', win, int(opts.get('--angle', 135)), 180, update)
cv2.createTrackbar('d', win, int(opts.get('--d', 22)), 50, update)
cv2.createTrackbar('SNR (db)', win, int(opts.get('--snr', 25)), 50, update)
update(None)
while True:
ch = cv2.waitKey()
if ch == 27:
break
if ch == ord(' '):
defocus = not defocus
update(None)
35、光栅工具
-x XCOORDINATE 自动读取图片并尝试爆破横向光栅图
-y YCOORDINATE 自动读取图片并尝试爆破纵向光栅图
-i IMAGEOUT 自定义爆破光栅图
python3 Raster-Terminator.py -x demo.png
36、无网络环境下利用二维码传输文件
Release v0.6.1 · sz3/cfc (github.com)
37、Peano(皮亚诺)曲线
脚本摘自:https://almostgph.github.io/2024/01/08/IrisCTF2024/
from PIL import Image
from tqdm import tqdm
def peano(n):
if n == 0:
return [[0,0]]
else:
in_lst = peano(n - 1)
lst = in_lst.copy()
px,py = lst[-1]
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + 1 + i[0], py - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px - i[0], py - 1 - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py - 1 - i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + 1 + i[0], py + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px,py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
return lst
order = peano(6)
img = Image.open(r"C:\Users\ASUSROG\Desktop\chal.png")
width, height = img.size
block_width = width # // 3
block_height = height # // 3
new_image = Image.new("RGB", (width, height))
for i, (x, y) in tqdm(enumerate(order)):
# 根据列表顺序获取新的坐标
new_x, new_y = i % width, i // width
# 获取原图像素
pixel = img.getpixel((x, height - 1 - y))
# 在新图像中放置像素
new_image.putpixel((new_x, new_y), pixel)
new_image.save("rearranged_image.jpg")