MISC Picture

Fc04dB Lv4

# MISC—Picture

一键梭哈网站:https://aperisolve.fr/

# 盲水印隐写

一张图片的情况

可以使用 隐形水印工具 V1.2 或者 WaterMark 来提取水印

两张图片的情况

1
2
3
先把要处理的图片拉入BlindWaterMark-master文件夹,然后使用如下命令
py bwmforpy3.py decode day1.png day2.png flag.png --oldseed
Tips:这里还会出现FFT(傅里叶盲水印):直接运行CTFD中的FFT.py

# 图片的分离和拼接

(1) 可以用 kali 的 convert 分离和 montage 拼接命令

1
2
3
4
5
6
分解GIF的命令:convert glance.gif flag.png
水平镜像翻转图片:convert -flop reverse.jpg reversed.jpg
垂直镜像翻转图片:convert -flip reverse.jpg reversed.jpg
合成图片的命令:montage flag*.png -tile x1 -geometry +0+0 flag.png
-tile是拼接时每行和每列的图片数,这里用x1,就是只一行
-geometry是首选每个图和边框尺寸,我们边框为0,图照原始尺寸即可

(2) 使用在线网站分解:https://tu.sioe.cn/gj/fenjie/,stegsolve 也可以

(3) 用 py 脚本跑

1
2
3
4
5
6
7
8
9
10
import os
from PIL import Image
im = Image.new('RGB', (2*201, 600)) # new(mode,size) size is long and width
PATH = 'E:/ctf/glance.gif'
FILE_NAME = [i for i in os.listdir(PATH)]
width = 0
for i in FILE_NAME:
im.paste(Image.open(PATH+i), (width, 0, width+2, 600)) # box is 左,上,右,下
width += 2
im.show()

# 像素点合成

注:Linux wc 命令用于计算字数。

-l 或–lines 显示行数。

-w 或–words 只显示字数。

-c 或–bytes 或–chars 只显示 Bytes 数。

可以改个标题后用在线网站将 txt 转换为 ppm 文件 https://convertfree.com/cn/txt-to-ppm#google_vignette

# OurSecret 隐写

拉入 OurSecret,输入密码解密,得到隐藏文件

# 拼图题

碎图片合成一张图片

1
2
3
#在Windows中使用imagemagick处理
magick.exe montage *.png -tile 18x10 -geometry 125x125+0+0 flag.jpg
magick montage *.png -tile 40x22 -geometry +0+0 flag-0.png
1
2
3
4
5
6
7
#在kali中处理
拉入kali里处理,如果是碎的图片,
先使用 montage *.PNG -tile 12x12 -geometry +0+0 out.png合成一张图片
*.png表示匹配所有图片
-tile表示图片的张数
-geometry +0+0表示每张图片的间距为0
合成后要先查看图片的宽高(宽高要相等,不相等要用PS调整)

也可以使用 gaps 智能拼图

1
2
3
4
5
6
7
8
gaps --image=out.png --generation=30 --population=144 --size=30 --save 

--image 指向拼图的路径
--size 拼图块的像素尺寸
--generations 遗传算法的代的数量
--population 个体数量
--verbose 每一代训练结束后展示最佳结果
--save 将拼图还原为图像
1
2
3
4
5
6
gaps --image=flag.jpg --generations=50 --population=180 --size=125 --verbose

-generations 你要迭代多少次
-population 你有多少个小拼图
--size 每张小图,也就是拼图小块的大小
--verbose 实时显示

# 近邻法缩放图片

在 PS 中打开图片,然后在更改图像大小中,将宽高调成指定像素并将重新采样选项选为邻近(硬边缘)

# pixeljihad(有密码)

直接使用在线网站解密即可:PixelJihad (sekao.net)

# 隐写文本可能藏在原图片和隐写文件的中间

直接在 010 中搜索 IEND,然后查看后面有没有额外内容即可

# 提取图片中等距的像素点得到隐写的图片

在 windows 的终端 wt 中运行 CTFD 中的 Get_Pixels.py

# silenteye 隐写

特征:放大图像后会有行列不对齐的小灰块

直接用 silenteye 打开输入密钥 decode 即可,默认密钥是 silenteye

# 图片报错改宽高后图片无变化

可以再 foremost 一下

# DeEgger Embedder 隐写

可以直接使用 DeEgger Embedder 工具 extract files

# flag 可能藏在 exif 中

直接在 linux 中输入以下命令查看即可,如果偷懒也可以直接使用 破空 flag 查找工具 进行查找

1
exiftool 3.jpg

# 给了两张图片,flag 藏在每行不同像素的个数中

例题 1-2023 羊城杯初赛 - 两支老虎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from PIL import Image, ImageChops

img1 = Image.open("1.png")
width1,heigth1 = img1.size # 1134,720
img2 = Image.open("2.png")
width2,heigth2 = img2.size # 1144,720
img2 = img2.crop((0,0,1134,720))
width2,heigth2 = img2.size
# img2.save("3.png")

diff_dit = {}
# 返回差异图像,表示 img1 和 img2 之间的像素差异。
diff = ImageChops.difference(img1,img2)
width3,heigth3 = diff.size
for x in range(width3):
for y in range(heigth3):
pixel3 = str(diff.getpixel((x,y)))
# 统计一下差异像素
if pixel3 not in diff_dit:
diff_dit[pixel3] = 0
else:
diff_dit[pixel3] += 1
print(diff_dit)
# {'(0, 0, 0)': 813891, '(1, 1, 1)': 2533, '(1, 1, 0)': 53}

for y in range(heigth1):
cnt = 0
for x in range(width1):
pixel1 = img1.getpixel((x,y))
pixel2 = img2.getpixel((x,y))
if pixel1 != pixel2:
cnt += 1
if cnt != 0:
print(chr(cnt),end='')
# DASCTF{tWo_t1gers_rUn_f@st}

# PNG

# CRC 错误 (不能乱改),改宽高

脚本爆破

# LSB (最低有效位) 隐写:

没有密钥的情况

1
2
3
4
# 用zsteg快速查看
zsteg -a (文件名) #查看各个通道的lsb
-b的位数是从1开始的 zsteg zlib.bmp -b 1 -o xy -v
提取文件并导出 zsteg -e b1,r,lsb,xy 3.png > 123.jpg

stegsolve 保险一点

有密钥的情况(cloacked-pixel)

lsb 隐写的可能是加密后的数据,i 春秋最喜欢的 cloacked-pixel

拉到 kali/WSL 里用 cloacked-pixel 命令解密出数据

1
python2 cloacked-pixel-master/lsb.py extract 0.png out.data f78dcd383f1b574b

0.png 是隐写后的图片;out.data 是隐写内容保存的位置;f78dcd383f1b574b 是密钥

# IDAT 块隐写

(1) 解压 zlib 获得原始数据

然后用 010 提取数据扔进 zlib 脚本解压获得原始数据

将异常的 IDAT 数据块斩头去尾之后使用脚本解压,在 python2 代码如下:

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

(2) 加上文件头爆破宽高得到新的图片

一般出问题的 IDAT Chunk 大小都是比正常的小的,很可能在图片末尾

如果不确定是哪一个有问题,可以尝试都提取出来,一个一个分析

可以使用 tweakpng 辅助分析,但是一般用 010 的模板提取分析就够了

我们可用 kali 中的 ** pngcheck -v 0.png ** 检查 IDAT

# png 数据末尾藏 zip

补上压缩包的文件头,然后提取出来,解压 (可用 stegpy 得到解压密码)。

或者直接 foremost 提取

# apngdis_gui

一张 png 图片还可能是 apng,直接用 apngdis_gui 跑一下,可以分出两张相似的 png

# CVE-2023-28303 截图工具漏洞

可以使用 Github 上大佬写好的工具一把梭,前提是需要知道原图的分辨

# JPG

# 用 stegdectet 看看是什么加密:

.\stegdetect.exe -t jopi -s 10.0 .\0.jpg

# steghide 隐写

1
2
#如果密码已经知道了
steghide extract -sf filename -p passwd
1
2
3
4
5
6
7
8
9
10
11
12
#如果密码未知
可以用下面这个脚本爆破
#bruteStegHide.sh
#!/bin/bash

for line in `cat $2`;do
steghide extract -sf $1 -p $line > /dev/null 2>&1
if [[ $? -eq 0 ]];then
echo 'password is: '$line
exit
fi
done
1
2
#或者在WSL或者kali里用Stegseek跑(字典在wordlist里)
stegseek filename rockyou.txt

# outguess 隐写

1
2
3
outguess -k "abc" -r mmm.jpg flag.txt
#-k 后面跟的是解密的密钥
#flag.txt是解密后数据保存的位置

# F5-steganography-master

1
2
3
4
5
#有密码的情况
java Extract beautiful.jpg -p passwd
#无密码的情况
java Extract beautiful.jpg
#解密出来的数据会放到F5文件夹下的output.txt中

# JPG 宽高隐写

010 打开 JPG 图片,找到 struct SOF 块数据,手动调整宽高即可

# BMP

# bmp 宽高爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import os
import time
import math
import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-f", type=str, default=None, required=True,
help="输入同级目录下图片的名称")
args = parser.parse_args()

SAVE_DIR = os.getcwd()


def save_img(data, width=None, height=None, sqrt_num=None):
with open(os.path.join(SAVE_DIR, "fix_width.bmp"), "wb") as f:
f.write(data[:0x12] + width.to_bytes(4,
byteorder="little", signed=False) + data[0x16:])

with open(os.path.join(SAVE_DIR, "fix_height.bmp"), "wb") as f:
f.write(data[:0x16] + height.to_bytes(4,
byteorder="little", signed=False) + data[0x1a:])

with open(os.path.join(SAVE_DIR, "fix_sqrt.bmp"), "wb") as f:
f.write(data[:0x12] + sqrt_num.to_bytes(4,
byteorder="little", signed=False) * 2 + data[0x1a:])


def get_pixels_size(data):
bfSize = int.from_bytes(data[0x2:0x2+4], byteorder="little", signed=False)
bfOffBits = int.from_bytes(
data[0xa:0xa+4], byteorder="little", signed=False)
biBitCount = int.from_bytes(
data[0x1c:0x1c+2], byteorder="little", signed=False)
channel = biBitCount // 8
# 由于宽高都会被修改,所以我计算出来的Padding_size也不是正确的,没有意义
# padding_size = (4 - col * channel % 4) * row if col * channel % 4 != 0 else 0
# pixels_size = (bfSize - bfOffBits - padding_size) // channel
return (bfSize - bfOffBits) // channel


if __name__ == '__main__':
file_path = os.path.abspath(args.f)
if os.path.splitext(args.f)[-1] != ".bmp":
print("您的文件后缀名不为BMP!")
time.sleep(1)
exit(-1)

with open(file_path, "rb") as f:
data = f.read()
col = abs(int.from_bytes(data[0x12:0x12+4],
byteorder="little", signed=True))
row = abs(int.from_bytes(data[0x16:0x16+4],
byteorder="little", signed=True))
pixels_size = get_pixels_size(data)

width, height = pixels_size // row, pixels_size // col
sqrt_num = int(math.sqrt((pixels_size)))
save_img(data, width=width, height=height, sqrt_num=sqrt_num)

print("温馨提示:由于填充字节的问题,所以可能会偏差几个像素!")
print(f"1.修复宽度: {width}")
print(f"2.修复高度: {height}")
print(f"3.修复宽度高度为: {sqrt_num}")
time.sleep(1)

# wbStego4open 隐写

用 wbStego4open 直接 decode

# silenteye 隐写

直接拉入 silenteye 解密即可

# GIF

# GIF 图片可能要分帧提取

stegsolve 可以

# Webp

webp 文件用电脑自带的图片看可能会有点问题,建议用浏览器打开这种文件

webp 可能是动图,可以用下面这个脚本分离 webp 中的每帧图片

1
2
3
4
5
6
7
from PIL import Image

img = Image.open('killer.webp')
n_frame = img.n_frames
for i in range(n_frame):
img.seek(i)
img.save(f'img/{i}.png')

# RAW、ARW

# RAW 的 LSB 隐写

ARW 文件是 Sony 相机的原始数据格式

可以使用 rawpy 模块读取图片的像素数据,查看是否存在 LSB 隐写【例:2024 L3HCTF RAWatermark】

示例脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import rawpy
import numpy as np
import libnum

with rawpy.imread('image.ARW') as raw:
# 从 raw 对象中获取可见的 Bayer 格式图像数据
bayer_visible = raw.raw_image_visible
# print(bayer_visible)
# 用 bitwise_and() 函数将 bayer_visible 中的每个像素值与 1 进行按位与操作,以提取每个像素的最低有效位(LSB)
lsb_array = np.bitwise_and(bayer_visible, 1)
# print(lsb_array)
# 使用 NumPy 数组的 flatten() 方法将 lsb_array 数组展平成一维数组
lsb_array_flat = lsb_array.flatten()
# print(lsb_array_flat)
hidden_message = ''.join(map(str, lsb_array_flat))
# 将隐写的数据转为十六进制,便于查看文件头
hex_data = hex(int(hidden_message, 2))
# print(hex_data[:10]) # 0x504b0304
# 将二进制数据转换为byte类型数据
data = libnum.b2s(hidden_message)

with open('flag.zip', 'wb') as f:
f.write(data)

# 直接改后缀为.data,然后拖入 Gimp

# 二维码

# bmp 转二维码

# Aztec code、DataMatrix、GridMatrix、汉信码、PDF417code

# 二维码的纠错等级

1 位置的颜色 2 位置的颜色 纠错等级 容错率
L(Low) 7%
M(Medium) 15%
Q(Quartil) 25%
H(High) 30%

# 二维码修复

  • Title: MISC Picture
  • Author: Fc04dB
  • Created at : 2024-11-08 11:01:30
  • Updated at : 2024-11-08 11:02:21
  • Link: https://redefine.ohevan.com/2024/11/08/MISC-Picture/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments