图片隐写

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

CTF中的隐写术总结 - FreeBuf网络安全行业门户

会把插入数据中的每一个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

21.pdf


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

PixelJihad (sekao.net)


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、翻转+图像增强(反卷积)

代码来源:https://github.com/spipm/BraekerCTF_2024_public/blob/main/challenges/programming-misc/eye-doctor/solve/deconvolution.py

# 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

posted @ 2023-10-09 23:32  ^cyi^  阅读(918)  评论(2编辑  收藏  举报