CTFshow刷题日记-MISC-图片篇(下 24-51)文件结构与颜色通道

文件结构篇

misc24-bmp改高度

提示:flag在图片上面

bmp格式文件

真flag在图片上面,改一下高度就可以看到了

image-20210910085845890

image-20210910085605456

misc25-png改高度

提示:flag在图片下面

改高度即可

image-20210910085906929

misc26-crc32

提示:flag还是在图片下面,但到底有多下面?

用tweakpng打开文件

image-20210910090048499

通过python2脚本来获取图片高度

# -*- coding: utf-8 -*-
import binascii
import struct

#\x49\x48\x44\x52\x00\x00\x01\xF4\x00\x00\x01\xA4\x08\x06\x00\x00\x00

crc32key = 0xCBD6DF8A
for i in range(0, 65535):
  height = struct.pack('>i', i)
  #CRC: CBD6DF8A
  data = '\x49\x48\x44\x52\x00\x00\x01\xF4' + height + '\x08\x06\x00\x00\x00'

  crc32result = binascii.crc32(data) & 0xffffffff

  if crc32result == crc32key:
    print ''.join(map(lambda c: "%02X" % ord(c), height))

第二个脚本-推荐使用piccrc32.py

import binascii
import struct

crcbp = open("misc26.png", "rb").read()
for i in range(2000):
    for j in range(2000):
        data = crcbp[12:16] + struct.pack('>i', i)+struct.pack('>i', j)+crcbp[24:29]
        crc32 = binascii.crc32(data) & 0xffffffff
        if(crc32 == 0xEC9CCBC6):
            print(i, j)
            print('hex:', hex(i), hex(j))

image-20210910203410546

得出正确的高度

将0x25e转换为十进制,修改高度保存

image-20210910203748669

所以说flag就是25e拼接上图片中给出的内容

misc27-jpg改高度

提示:flag在图片下面

修改高度

image-20210910205755643

保存为图片

misc27

misc28-gif改高度

提示:flag在图片下面

也是010改图片高度

image-20210910210156938

注意图片是gif格式,有不同的帧,所以在不同的块也有宽高的数值需要多试试,改这个有效的

misc29-多帧gif改高度

多帧gif动图

image-20210910210815819

有很多的帧,需要都改一下高度,发现在第八帧有flag

image-20210910211001243

misc30-bmp改宽度

提示:正确的宽度是950

image-20210910211445142

发现图片是这样的,按照提示去修改宽度即可

misc31-bmp计算宽度

提示:高度是正确的,但正确的宽度是多少呢

bmp的文件格式

这张图片由900*150个像素,文件头占用53字节,文件尾在0x76F50位置,换算成10进制为487248

因为每个像素点由3个字节(十六进制的6位)表示,每个字节负责控制一种颜色,分别为蓝,绿,红,所以文件真是像素个数为

(487248-53)/3=162398

题目提示图片高度是正确的,所以说要计算宽度

宽度即为像素数除以高度:192398/150=1082(如果有余数要舍去)

将图片宽度改为1082保存图片打开即为flag

同样之前的misc24题已知宽度求高度也是同样的计算方法

misc32-png计算crc改宽度

知道高度爆破宽度,跟26题一样,可以用之前的脚本

新脚本-效果一样,这两个脚本都需要改crc的值

import zlib
import struct

# 同时爆破宽度和高度
filename = "misc32.png"
with open(filename, 'rb') as f:
    all_b = f.read()
    data = bytearray(all_b[12:29])
    n = 4095
    for w in range(n):
        width = bytearray(struct.pack('>i', w))
        for h in range(n):
            height = bytearray(struct.pack('>i', h))
            for x in range(4):
                data[x+4] = width[x]
                data[x+8] = height[x]
            crc32result = zlib.crc32(data)
            #替换成图片的crc
            if crc32result == 0xE14A4C0B:
                print("宽为:", end = '')
                print(width, end = ' ')
                print(int.from_bytes(width, byteorder='big'))
                print("高为:", end = '')
                print(height, end = ' ')
                print(int.from_bytes(height, byteorder='big'))

python3运行即可

image-20210911093032161

宽度为1044,修改宽度保存

misc33-pngcrc计算宽高

高度和宽度都不对,使用32题的脚本计算下

在010中找到crc的值

image-20210911094353193

更改脚本运行

image-20210911094339505

misc34-png爆破宽度

提示:出题人狗急跳墙,把IHDR块的CRC也改了,但我们知道正确宽度肯定大于900

import zlib
import struct
filename = "misc34.png"
with open(filename, 'rb') as f:
    all_b = f.read()
    #w = all_b[16:20]
    #h = all_b[20:24]
    for i in range(901,1200):
        name = str(i) + ".png"
        f1 = open(name,"wb")
        im = all_b[:16]+struct.pack('>i',i)+all_b[20:]
        f1.write(im)
        f1.close()

把生成的所有图片都保存下来了(建议在空文件夹里),然后用眼看哪个是正常的。

最后得到正确的宽度是1123

这里的w和h需要打开图片去看下

image-20210911100530502

struct --- 将字节串解读为打包的二进制数据
struct.pack(format, v1, v2, ...)
返回一个 bytes 对象,其中包含根据格式字符串 format 打包的值 v1, v2, ... 参数个数必须与格式字符串所要求的值完全匹配

运行结果

image-20210911101814029

misc35-jpg爆破宽度

提示:出题人负隅顽抗,但我们知道正确宽度肯定大于900

#w = all_b[157:159]
#h = all_b[159:161]
import zlib
import struct
filename = "misc35.jpg"
with open(filename, 'rb') as f:
    all_b = f.read()
    #w = all_b[159:161]
    #h = all_b[157:159]
    for i in range(901,1200):
        name = str(i) + ".jpg"
        f1 = open(name,"wb")
        im = all_b[:159]+struct.pack('>h',i)+all_b[161:]
        f1.write(im)
        f1.close()

需要把图片的基础高度调高一点

高度调到了600,宽度在993-1000这个范围内都可以得到flag

misc36-gif爆破宽度

gif图片,提示正确宽度在920-950之间

image-20210911111204041

改下脚本

import zlib
import struct
filename = "misc36.gif"
with open(filename, 'rb') as f:
    all_b = f.read()
    #w = all_b[6:8]
    for i in range(920,950):
        name = str(i) + ".gif"
        f1 = open(name,"wb")
        im = all_b[:38]+struct.pack('>h',i)[::-1]+all_b[40:]
        f1.write(im)
        f1.close()

注意高度存储时是倒着的,因为

image-20210911113524027

运行之后发现没有flag,需要提高图片高度再试

image-20210911112423282

misc37-gif逐帧

提示flag再图片里

这是个gif动图,在某些帧里边发现了不一样的字符

用stegslove打开

image-20210911131610690

image-20210911131623328

flag藏在不同的帧里边

misc38-apng图片分离

提示:flag在图片里

是一个png图片,在win10自带的图片浏览器中并没有发现什么,使用chrome或者蜂蜜图片浏览器打开发现这是张可以动的png

使用apngdis.exe(APNG Disassembler)工具分离文件

image-20210911133117286

image-20210911133120744

发现flag片段

misc39-gif时间差flag

提示:flag就像水,忽快忽慢地流

是一个gif,不过这里是利用不同帧之间的间隔时间来隐写的。

这里利用linux下的工具identify

安装命令:sudo apt-get install imagemagick
基本的命令格式:
  identify [options] input-fileidentify:命令名称
  options:参数
  input-file:文件名。
提取命令:identify -format "%T " misc39.gif > 1.txt

得到的一串36和37,考虑把37换成1、36换成0,就得到长度为287的二进制字符串,考虑每7位转一个字符(正常是8位一组),得到flag

image-20210911135018034

s="11000111110100110011011100111101000110111111101111111011011010101100100111000011000101100101100110110011001110010111001011010111001101100010011011111000101100101011001001101100111000110010001110010110110011001111000010111001110010111000101100011110000101100000110100011010101110011111101"
flag=""
for i in range(41): #287//7
    flag += chr(int(s[7i:7(i+1)],2))
print(flag)

misc-40apng时间差flag

apng动图,使用APNG Disassembler工具,分离文件,同时会生成txt文件,记录详细信息

image-20210911140127814

例如这个需要把54提取出来,转换成对应的ascii码

flag=""
for i in range(1,69):
    if i < 10:
        i = "0" + str(i)
    f = open("apngframe" + str(i) + ".txt")
    s = f.read()
    # delay=116/1000
    flag += chr(int(s.split("/")[0][6:]))
print(flag)

实际实在28张图片时出现flag,可以将range范围改为28,69

image-20210911140327472

misc42-IDAT数据块长度

提示:flag有多长?2cm……不好意思打错了,41位

用tweakpng打开图片,发现IDAT数据块的长度转换成ASCII码就是flag

image-20210911142901317

misc43-收集报错

提示:错误中隐藏着通往正确答案的道路

tweakpng打开图片发现很多报错提示,用pngdebugger看一下

image-20210911143704559

发现很多报错代码,结合提示将其拼合转换成字符串

python整理下格式

string ="E59387E50x93A62E630x746673680x6F777B360x656232350x383966660x666635650x333930660x653662380x373530340x646263300x3839327D"
string=string.replace("0x","")
for i in range(0,len(string),2):
    print((string[i:i+2]),end=" ")

因为前两个字符是中文python字符集里没有,所以偷懒直接用工具

image-20210911145519290

misc44-转换报错提示

提示:错误中还隐藏着坑

tweakpng 依旧是一堆报错,而且这次IDAT的数据块非常的多

image-20210911145719612

使用pngDebugger将输出导出为文档

然后写脚本,把CRC OK的替换成1,CRC FAILED替换成0

f=open("1.txt","r")
s=f.read()
f.close()
flag=""
for i in s.split():
    if "OK!" == i:
        flag += "1"
    elif "FAILED" ==i:
        flag += "0"
print(flag)
#11111111111111110110001101110100011001100111001101101000011011110111011101111011011000110110001100110001011000010110011000110011001100100110001001100110001110010011011000110011001100000011100001100110011000110011000100110010001101100011001100110010001100110011000101100010011001010011011100111000001100110110011000110110001110010110010101111101
print(len(flag)) #344
for i in range(43):
    print(chr(int(flag[8*i:8*(i+1)],2)),end="")

misc45-转换格式

提示:有时候也需要换一换思维格式

其实图片中是是藏有了其他文件但是因为png和bmp格式图片像素点的读取方式不一样,在png格式下分离不出文件,需要先把图片从png格式转换成bmp格式,在进行binwalk提取得到flag.png

在线转换网站:https://cn.onlineconvert.com/pdf-to-bmp

image-20210911152943031

misc46-49卷废了

misc46

是一个gif

提取出它的详细信息:identify misc46.gif > /1.txt (这里直接在根目录生成1.txt,好找)

内容大概长这样,其中0+0、174+49、196+47这些是偏移量,这题就利用这个来作图。

image-20210911153740881

写一个很简单的脚本把坐标提取出来

f = open("1.txt","r")
x = f.readlines()
f.close()

f = open("out.txt","w")
for i in x:
    f.write(i.split("+")[1])
    f.write(" ")
    f.write(i.split("+")2)
    f.write("\n")
f.close()

再利用gnuplot作图,画出来的结果有点模糊,自行调整

image-20210911153730014

再翻转一下得到flag

misc47

给了一个png,打开发现没内容,用浏览器打开,确认是apng

思路和上题一致,不过稍微复杂一点,先通过这篇文章了解一下apng文件结构,简单来说就是每一个IDAT块前面都会有一个fcTL块,它其中就包含水平垂直偏移量

如下,对应坐标点就是(182,52)

image-20210911153714457

之前的脚本有的师傅使用的时候有点小问题,所以改了一下

from PIL import Image

im = Image.new('RGB', (400, 80), 255)
f = open('1.txt','r') #把图片的十六进制导出,保存为1.txt
c = f.read()
f.close()

lt = c.split("6663544C")
for i in range(2,len(lt)):
    x = int(lti,16)
    y = int(lti,16)
    im.putpixel((x,y),0)  
im.show()

misc48

用010editor打开,发现有提示

1、统计FF的数量,再减去1
2、ctfshow{}中包含32个字符

image-20210911153815196

提示了,但没有完全提示,因为第一条提示,其实指的是统计每两个有意义块之间的FF的数量再减一

图中紫色的就是,开头的那个FF也算,因为只有一个,减去1后就是0;接下来是12、11、0…

image-20210911153826521

因为flag长度是32位,所以只统计前32个,即:

0 12 11 0 7 10 13 13 9 0 9 13 0 13 6 0 10 9 2 1 0 1 10 8 11 5 12 7 2 2 3 10

分别转十六进制后,再连接在一起,得到:ctfshow{0cb07add909d0d60a92101a8b5c7223a}

misc49
提示:它们一来就是十六种。本题略脑洞,可跳过

八神的脑洞题,靠我自己想是不行的,果断参考wp

用winhex打开,能看到很多字符串,这其实是八神给的提示,虽然我没get到

image-20210911153839826

重点是这些字符串前面,都出现过FFE? 这种格式的数据,搜索一下发现有挺多的

image-20210911153857849

把所有十六进制数保存在2.txt中,用一个小脚本处理一下

f=open("2.txt","r")
txt=f.read().replace("\n","")
f.close()

l=txt.split("FFE")
flag=""
for i in range(1,len(l)):
    flag += li
print(flag.lower()[:32]) #得到的结果套上ctfshow{}

其实就是把FFE后面的那个字符提取出来,再连接在一起,一共32位(),这就是flag。

这里写脚本的时候有个小失误,把这种也统计进去了,所以只有前32位是符合格式的正确的。

————————————————
版权声明:本文为CSDN博主「z.volcano」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45696568/article/details/115261347

颜色通道

misc50-stegslove一把梭

既然提示了颜色通道

image-20210911154129552

剩余的在后边几个通道

总结

  1. 以上misc图片题基本包含ctf中的题型,但是没有涉及到隐写的内容,f5,outguess,盲水印等都没有出现
  2. misc图片题有固定的套路需要多细心观察,有耐心,做题的过程中也发现了很多之前没有注意的盲点
  3. 我们一起成长,一起进步

参考链接

https://blog.csdn.net/weixin_45696568/article/details/115261347

感谢z.volcano师傅写的文章给我很大的启发

  • 11
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OceanSec

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值