1

简介

隐写术是ctf(Capture tThe Flag)里常见的一种类型,其目的是隐藏信息。常见的可以利用jpg,png,gif,MP4等等格式来进行信息的隐藏。

常见文件头

  1. JPEG (jpg):FFD8FFE1

  2. PNG (png):89504E47

  3. GIF (gif):47494638

  4. HTML (html):68746D6C3E

  5. ZIP Archive (zip):504B0304

  6. RAR Archive (rar):52617221

实践

图种

图种=图片+zip

  • 使用:直接将图片另存为zip,再解压即可,使用winhex查看二进制文件信息

  • 原理:jpg一般是以FF D9结尾图片查看器会忽视jpg结束符之后的内容,所以我们附加的zip,自然也就不会影响到图像的正常显示。

  • 制作:

    copy /b 1.jpg+password.zip out.jpg
  • 工具:

    1. winhex查看二进制信息,手动复制 tu.jpg

    2. linux file命令查看

    3. binwalk工具

      binwalk out.jpg 
      binwalk 3.png
      binwalk -e out.jpg
      binwalk -e 3.png
  • 缺点:这种类型的隐写也是比较容易被发现的,如果发现是jpg图片的话,观察文件结束符之后的内容,查看是否附加的内容,正常图片都会是FF D9结尾的;使用像binwalk之类的工具可以很快的分析出来

lsb

前面的做法没有改变原有文件,但是增加了新文件,增加了体积,把数据直接增加在了jpg后面。还有一种数据就是不增加体积,直接修改原数据,利用了修改数据的方式来隐藏自己传递的信息。

一种常见的方式是利用LSB来进行隐写,LSB也就是最低有效位 (Least Significant Bit)。原理就是图片中的像数一般是由三种颜色组成,即三原色,由这三种原色可以组成其他各种颜色,例如在PNG图片的储存中,每个颜色会有8bit,LSB隐写就是修改了像数中的最低的1bit,在人眼看来是看不出来区别的,也把信息隐藏起来了。譬如我们想把’A’隐藏进来的话,如下图,就可以把A转成16进制的0x61再转成二进制的01100001,再修改为红色通道的最低位为这些二进制串。

clipboard.png

clipboard.png

工具:Stegsolve lsb.png

注意:在这个过程中,我们要注意到,隐写的载体是PNG的格式,如果是像之前的jpg图片的话就是不行的,原因是jpg图片对像数进行了有损的压缩,你修改的信息可能会被压缩的过程破坏。而PNG图片虽然也有压缩,但却是无损的压缩,这样子可以保持你修改的信息得到正确的表达,不至于丢失。BMP的图片也是一样的,是没有经过压缩的,可以发现BMP图片是特别的大的,因为BMP把所有的像数都按原样储存,没有压缩的过程。

gif

gif和别的图片最大的区别就是gif是动态图,它是可以由多帧组成的可以顺序播放的

PASSWORD is Y2F0Y2hfdGhlX2R5bmFtaWNfZmxhZ19pc19xdW10ZV9zaW1wbGU=

这个结果可以直接使用base64解压

exif and tail

数据存放在exif头或者直接加在tail结尾
exif.jpg tail.jpg

终极大boss

将信息隐藏和加解密结合
sctf-misc400
这个题需要我们对于png图片的格式有一些了解,先用stegsolve查看一下,其他的LSB之类的并没有发现什么问题,然后看了一下结构发现,有一些异常的IDAT块。图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。

clipboard.png

可以用pngcheck来辅助我们观察,可以看得更加清晰。pngcheck.exe -v sctf.png

clipboard.png

clipboard.png

我们用winhex把这一部分异常的IDAT块给扣出来。然后就是要研究研究这个块是什么情况,发现了载体之后就是要想办法找出他的规律。观察那一部分的数据,可以看到是16进制的78 9C开头的,百度一下分析是zlib压缩的标志。在png的百度百科里也可以查到PNG的IDAT是使用从LZ77派生的无损数据压缩算法,可以用zlib解压。那么就尝试用zlib来解一下这段数据

得到16进制的以方便python处理,前面的4字节是长度 然后是标志位IDAT 然后开始是数据,直到 D9 CF A5 A8是crc32校验位。 所以实际的数据是:

789C5D91011280400802BF04FFFF5C75294B5537738A21A27D1E49CFD17DB3937A92E7E603880A6D485100901FB0410153350DE83112EA2D51C54CE2E585B15A2FC78E8872F51C6FC1881882F93D372DEF78E665B0C36C529622A0A45588138833A170A2071DDCD18219DB8C0D465D8B6989719645ED9C11C36AE3ABDAEFCFC0ACF023E77C17C7897667

然后用python来写zlib解压

#! /usr/bin/env python
import zlib
import binascii
IDAT = "789C5D91011280400802BF04FFFF5C75294B5537738A21A27D1E49CFD17DB3937A92E7E603880A6D485100901FB0410153350DE83112EA2D51C54CE2E585B15A2FC78E8872F51C6FC1881882F93D372DEF78E665B0C36C529622A0A45588138833A170A2071DDCD18219DB8C0D465D8B6989719645ED9C11C36AE3ABDAEFCFC0ACF023E77C17C7897667".decode('hex')
#print IDAT
result = binascii.hexlify(zlib.decompress(IDAT))
print result
 
#print result.decode('hex')
1111111000100001101111111100000101110010110100000110111010100000000010111011011101001000000001011101101110101110110100101110110000010101011011010000011111111010101010101111111000000001011101110000000011010011000001010011101101111010101001000011100000000000101000000001001001101000100111001111011100111100001110111110001100101000110011100001010100011010001111010110000010100010110000011011101100100001110011100100001011111110100000000110101001000111101111111011100001101011011100000100001100110001111010111010001101001111100001011101011000111010011100101110100100111011011000110000010110001101000110001111111011010110111011011

得到的01 串的长度是625,除以8 除以7 都无法整除,也就是说没法直接转换成ascii码。

625=25*25
刚好是个正方形的形状,那么尝试一下 把这些01 组成一个正方形 看看是什么

#!/usr/bin/env python
import Image
MAX = 25
pic = Image.new("RGB",(MAX, MAX))
str = "1111111000100001101111111100000101110010110100000110111010100000000010111011011101001000000001011101101110101110110100101110110000010101011011010000011111111010101010101111111000000001011101110000000011010011000001010011101101111010101001000011100000000000101000000001001001101000100111001111011100111100001110111110001100101000110011100001010100011010001111010110000010100010110000011011101100100001110011100100001011111110100000000110101001000111101111111011100001101011011100000100001100110001111010111010001101001111100001011101011000111010011100101110100100111011011000110000010110001101000110001111111011010110111011011"
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")

发现是一个二维码 可以编码来画出 0代表了是白色 而1代表了黑色,然后可能会需要旋转来调整一下,才能扫描出来。处理一下得到了一个二维码。然后扫描得到了flag。

SCTF{(121.518549,25.040854)}


LiangJ
78 声望3 粉丝

引用和评论

0 条评论