这两天抽空把whaleCTF的MISC部分的题目整理了下完整的Writeup,时间比较急写得简陋了些,如有错误欢迎各位师傅指正,持续更新~~
直接上stegsolve神器,会发现一张反色的二维码,保存下来再反色即可,得到flag
winhex下可以看到图片包含了一个zip,分离至新文件保存为zip即可得到flag
和上一题同一个套路,jpg包含两张图片,分离即可
各种姿势检测下也没检测出什么,直接上strings大法,strings whale.jpg | grep flag
分析下文件类型是一张gif,改后缀丢到gif分离器(stegsolve神器也可以),在第五帧得到flag(妹砸好评~)
在winhex下搜索FFD9,发现没有结尾,拉到结尾发现有字符串,unicode——>ascii, getflag!
firefox加载不出来,用py把图片下载下来(win10自带的edge可以加载出来)
根据题目提示,跟图片提示IHDR块有关。py下载脚本如下:
import requests
response = requests.get('http://whalectf.xin/files/850e2779d4afe36c2094333c56c0cf84/HARD.png')
cat_img = response.content
with open('HARD.png','wb') as f:
f.write(cat_img)
f.close()
对一张正常的图片,通过修改其宽度或者高度隐藏信息,使计算出的CRC校验码与原图的CRC校验码不一致;windows的图片查看器会忽略错误的CRC校验码,因此会显示图片,但此时的图片已经是修改过的,所以会有显示不全或扭曲等情况,而Linux下的图片查看器不会忽略错误的CRC校验码,因此用Linux打开修改过宽或高的png图片时,会出现打不开的情况,也基本可以确定图片宽和高被动了手脚,话不多说,上脚本爆破。
参考链接如下:PNG(文件头IHEDR)图片隐写
# -*- coding: utf-8 -*-
# __Author__:Pad0y
import binascii
import struct
crc32key = 0xCAF304E6 # CRC校验
for i in range(0, 65535):
height = struct.pack('>i', i)
data = '\x49\x48\x44\x52\x00\x00\x0C\xF0' + height + '\x08\x06\x00\x00\x00'
crc32result = binascii.crc32(data) & 0xffffffff
if crc32result == crc32key:
print ''.join(map(lambda c: "%02X" % ord(c), height))
丢到winhex,发现是bmp,比较少遇到bmp图片的隐写,骚分析一波,什么也得不到(- -)目测是LSB隐写了,这里用wbs43open解密
这里密码为空
得到的文件用notepad++打开就能看到flag,这里有点邪门,怎么提交都显示错误(气哭.jpg)可能因为前面下划线还有东西,换个姿势继续日
由于图片是bmp类型,像素在图片中是倒叙存储,即以b,g,r的方式存储,而且当高度为正时图片中最后一行像素是存储在第一行的位置(对于正常二位坐标系,当以左下角为原点高度才会为正),用PIL的getdata读出的像素只是像素点在图片的相对位置,并非在文本文件的相对位置,所以以文本文件的方式读取图片
bmpfi=open("nvshen.bmp","rb")
bmpstr=bmpfi.read()
bmpfi.close()
bfOffBits=int(bmpstr[13:9:-1].encode("hex"),16)
str1=""
for j in xrange(bfOffBits,len(bmpstr)):
str1+=bin(ord(bmpstr[j]))[-1]
i=0
lst=""
while i<len(str1):
str2=str1[i:i+8]
lst+=chr(int(str2,2))
i=i+8
fi=open("flag",'wb')
fi.write(lst)
fi.close()
得到的flag再用notepad打开(会心一笑.jpg)
python大法好!
打开链接图片又是无法显示的问题,把链接丢到能下载东西的软件就行了,得到一张损坏的GIF,缺少头部所致,补齐后分离每一帧图片,把灰色部分连起来base64解码即可Getflag。
题目也提示了用ps去处理,仔细看人像背后有些字,这里需要锐化图片才能让图片清晰,电脑上没ps,于是用下在线版的ps,没法满足需求(蛋疼),上万能的py勉强能看出来
>>> from PIL import Image
>>> img = Image.open('1.png')
>>> x = img.size[0]
>>> y = img.size[1]
>>> x,y
(399,378)
>>> for i in range(x-2):
for j in range(y-2):
a = img.getpixel((i,j))[0] + img.getpixel((i,j))[1]+img.getpixel((i,j))[2]
b = img.getpixel((i,j+1))[0] + img.getpixel((i,j+1))[1]+img.getpixel((i,j+1))[2]
c = img.getpixel((i,j+2))[0] + img.getpixel((i,j+2))[1]+img.getpixel((i,j+2))[2]
if(a>b and c>b) or (a<b and c <b):
pass
else:
img.putpixel((i,j),(255,255,255))
>>> img.show()
似曾相识的题目,binwalk里可以看到0x15AFFB后面是zlib的压缩数据
pngcheck下图片,可以看到正常的块的length是在65524的时候就满了,而倒数第二个IDAT块长度是45027,最后一个长度是138,很明显最后一个IDAT块是有问题的,因为他本来应该并入到倒数第二个未满的块里。
winhex可以看到最后四个字节是CRC校验码,所以有效数据是789C-7667范围内的字节,写个脚本提取出来
# -*- coding: utf-8 -*-
# __Author__:Pad0y
from PIL import Image
import zlib
img = open('sctf.png', 'rb').read()[0x15AFFB:0x15B085] # 获取zlib压缩数据
data = zlib.decompress(img)
binstr = str(data,encoding='utf8')
# print(len(binstr)) 长度是625,刚好可以绘制成 25*25的图片
pic = Image.new('RGB', (25, 25)) # 绘制一张25*25的图
i = 0
for y in range(0, 25):
for x in range(0, 25):
if binstr[i] == '1':
pic.putpixel([x, y], (0, 0, 0))
else:
pic.putpixel([x, y], (255, 255, 255))
i = i+1
pic.save('flag.png')
得到一张二维码,getflag!
看题目是最低位,由此联想到LSB,先想办法获取最低位,再把获取结果表示出来。但是二进制最低位只可能是0或1,由0,1表示出来的像素点自然想到是二维码,LSB的套路。如何证明?丢到神器大概可以发现有个二维码的图片。
这题目骚姿势比较多,大佬们用matlab三行代码就解决了,也可以把这张图片转化为32bit的png图片(用画图工具另存为png就可以)在丢到stegsolve就能看到完整的二维码,扫一下得到flag
flag{i love u}
py实现如下
from PIL import Image
lena = Image.open('01.bmp')
im=Image.new('1',lena.size)
pixsels = lena.load()
width=lena.size[0]
height=lena.size[1]
for x in range(0,width):
for y in range(0,height):
g=pixsels[x,y]
im.putpixel((x,y),int(bin(g)[-1]))
im.save("2.bmp")
看了下到此为止只有7个人做出来,应该算是MISC部分最难的一题,上神器,太大无法加载出来,上脚本PIL库无法加载这么大的图片也报错,心态爆炸。一开始想着如何恢复看似二维码的图片(然而并不是),还参考了如何用PS修复一张模糊的二维码?
然并卵,想在一天内AK隐写题果然很蛋疼(还要算上写博客的时间)
直接丢到notepad,发现一些有趣的东西
此前一直没有注意题目的描述部分"教了李磊四个简单的动作:上、下、左、右",对应如下:
^[[A:上
^[[B:下
^[[C:右
^[[D:左
(觉得熟悉不,终端想拿到上一条命令按↑有时候就会出现^[[A)
重新回到图片,中间有个绿色的点,很容易联想到以此为起点,按着所给的路径走,因为整张图片是灰色,这张图其实是一张灰度图,RGB值大概在一百多左右,可以对应ASCII值(笑容逐渐荡漾),这样根据每个路径就能得到,剩下的一个问题就是移动时间和方向,图像中有个comment元数据,这里可以借助exiftools查看内容
# -*- coding: utf-8 -*-
# __Author__: Pad0y
from PIL import Image
import sys
comment = """
^[[A^[[D^[[C^[[A^[[C^[[C^[[B^[[D^[[A^[[A^[[A^[[A^[[D^[[D^[[D^[[C^[[B^[[A^[[B^[[B^[[A^[[C^[[C^[[D^[[C^[[B^[[C^[[D^[[A^[[B^[[A^[[D^[[B^[[C^[[C^[[A^[[B^[[C^[[A^[[D^[[B^[[C^[[C^[[C^[[D^[[D^[[C^[[C^[[B^[[A^[[A^[[C^[[C^[[C^[[D^[[B^[[B^[[C^[[A^[[B^[[A^[[C^[[C^[[C^[[A^[[D^[[D^[[D^[[B^[[A^[[A^[[A^[[D^[[C^[[C^[[C^[[C^[[D^[[A^[[D^[[A^[[D^[[D^[[B^[[C^[[C^[[D^[[B^[[B^[[A^[[A^[[B^[[A^[[A^[[C^[[B^[[A^[[D^[[A^[[D^[[B^[[D^[[D^[[C^[[D^[[D^[[B^[[A^[[D^[[D^[[C^[[A^[[D^[[A^[[C^[[A^[[A^[[B^[[B^[[C^[[A^[[B^[[A^[[D^[[A^[[B^[[C^[[A^[[B^[[D^[[D^[[B^[[C^[[D^[[B^[[A^[[C^[[B^[[B^[[B^[[B^[[D^[[C^[[C^[[C^[[C^[[D^[[B^[[C^[[B^[[B^[[D^[[B^[[A^[[D^[[C^[[A^[[B^[[D^[[D^[[A^[[B^[[D^[[C^[[B^[[B^[[A^[[D^[[A^[[D^[[B^[[C^[[B^[[A^[[D^[[D^[[D^[[B^[[D^[[B^[[C^[[D^[[C^[[D^[[B^[[A^[[A^[[A^[[D^[[C^[[A^[[A^[[C^[[B^[[C^[[C^[[B^[[C^[[C^[[B^[[B^[[A^[[D^[[B^[[C^[[B^[[D^[[D^[[A^[[C^[[D^[[B^[[B^[[B^[[B^[[D^[[D^[[B^[[C^[[B^[[C^[[C^[[B^[[C^[[C^[[C^[[A^[[C^[[C^[[A^[[C^[[C^[[B^[[D^[[C^[[D^[[D^[[D^[[D^[[D^[[B^[[D^[[C^[[C^[[B^[[C^[[D^[[B^[[D^[[C^[[C^[[C^[[C^[[B^[[D^[[C^[[D^[[B^[[D^[[A^[[C^[[C^[[B^[[C^[[D^[[C^[[A^[[A^[[B^[[D^[[B^[[C^[[D^[[A^[[D^[[B^[[D^[[A^[[B^[[C^[[D^[[B^[[C^[[B^[[A^[[B^[[A^[[B^[[B^[[C^[[B^[[D^[[C^[[A^[[C^[[B^[[C^[[B^[[D^[[D^[[B^[[C^[[D^[[D^[[A^[[B^[[A^[[D^[[D^[[D^[[D^[[C^[[A^[[A^[[B^[[B^[[D^[[A^[[C^[[A^[[A^[[B^[[B^[[C^[[A^[[D^[[A^[[B^[[C^[[D^[[D^[[A^[[D^[[D^[[C^[[A^[[A^[[D^[[C^[[C^[[A^[[C^[[C^[[B^[[D^[[A^[[A^[[C^[[B^[[B^[[C^[[A^[[A^[[C^[[A^[[B^[[C^[[B^[[C^[[C^[[A^[[C^[[C^[[C^[[B^[[C^[[C^[[C^[[C^[[B^[[C^[[A^[[D^[[C^[[A^[[A^[[B^[[C^[[D^[[C^[[C^[[C^[[D^[[A^[[B^[[D^[[B^[[C^[[C^[[B^[[B^[[A^[[D^[[A^[[D^[[B^[[A^[[C^[[A^[[A^[[A^[[A^[[B^[[C^[[A^[[C^[[C^[[D^[[B^[[D^[[D^[[C^[[C^[[B^[[C^[[B^[[C^[[B^[[C^[[B^[[B^[[D^[[A^[[B^[[A^[[C^[[A^[[D^[[D^[[B^[[C^[[C^[[C^[[D^[[B^[[A^[[B^[[A^[[A^[[C^[[A^[[D^[[B^[[A^[[C^[[A^[[D^[[A^[[D^[[D^[[D^[[C^[[D^[[A^[[A^[[C^[[B^[[B^[[D^[[C^[[C^[[A^[[B^[[B^[[C^[[D^[[C^[[B^[[D^[[B^[[D^[[C^[[C^[[D^[[D^[[B^[[D^[[B^[[C^[[A^[[A^[[C^[[B^[[B^[[A^[[A^[[A^[[B^[[C^[[D^[[A^[[C^[[A^[[C^[[D^[[D^[[C^[[D^[[A^[[D^[[D^[[D^[[A^[[C^[[D^[[D^[[A^[[B^[[D^[[D^[[B^[[A^[[B^[[C^[[D^[[C^[[C^[[A^[[B^[[C^[[B^[[B^[[B^[[B^[[C^[[A^[[D^[[A^[[D^[[B^[[B^[[D^[[D^[[D^[[B^[[D^[[A^[[C^[[D^[[C^[[C^[[D^[[C^[[A^[[C^[[B^[[D^[[B^[[B^[[C^[[A^[[B^[[A^[[C^[[D^[[D^[[D^[[C^[[C^[[D^[[D^[[A^[[B^[[D^[[D^[[D^[[C^[[C^[[B^[[D^[[D^[[B^[[B^[[A^[[B^[[B^[[C^[[A^[[A^[[A^[[C^[[D^[[D^[[A^[[D^[[A^[[B^[[C^[[C^[[C^[[B^[[D^[[D^[[D^[[D^[[C^[[D^[[D^[[B^[[C^[[D^[[B^[[B^[[C^[[D^[[B^[[C^[[C^[[D^[[B^[[D^[[C^[[A^[[C^[[C^[[D^[[B^[[D^[[B^[[D^[[A^[[B^[[B^[[B^[[A^[[D^[[C^[[C^[[C^[[C^[[C^[[A^[[D^[[B^[[C^[[A^[[B^[[D^[[B^[[D^[[B^[[B^[[B^[[D^[[C^[[B^[[B^[[B^[[C^[[B^[[A^[[D^[[C^[[C^[[A^[[D^[[A^[[B^[[A^[[D^[[D^[[B^[[A^[[D^[[B^[[C^[[B^[[A^[[D^[[B^[[C^[[D^[[C^[[A^[[B^[[D^[[D^[[D^[[C^[[B^[[B^[[A^[[D^[[D^[[B^[[D^[[D^[[C^[[C^[[D^[[D^[[A^[[B^[[C^[[D^[[D^[[C^[[C^[[D^[[A^[[C^[[A^[[C^[[A^[[D^[[B^[[C^[[A^[[C^[[B^[[C^[[B^[[A^[[D^[[B^[[D^[[A^[[D^[[C^[[A^[[B^[[B^[[D^[[C^[[A^[[C^[[A^[[D^[[D^[[B^[[C^[[D^[[C^[[B^[[C^[[C^[[C^[[B^[[A^[[D^[[B^[[A^[[A^[[D^[[A^[[D^[[D^[[C^[[B^[[D^[[D^[[C^[[D^[[B^[[B^[[A^[[A^[[C^[[A^[[A^[[A^[[A^[[D^[[C^[[D^[[A^[[B^[[C^[[A^[[A^[[C^[[D^[[C^[[D^[[C^[[D^[[A^[[C^[[B^[[C^[[D^[[C^[[B^[[A^[[D^[[B^[[B^[[B^[[B^[[B^[[C^[[A^[[A^[[A^[[A^[[D^[[C^[[C^[[A^[[C^[[B^[[D^[[C^[[D^[[A^[[A^[[B^[[D^[[C^[[A^[[A^[[C^[[B^[[D^[[B^[[C^[[D^[[D^[[C^[[B^[[C^[[A^[[D^[[A^[[D^[[A^[[B^[[D^[[D^[[B^[[A^[[D^[[D^[[A^[[D^[[D^[[D^[[A^[[C^[[A^[[B^[[D^[[D^[[B^[[B^[[B^[[D^[[D^[[A^[[A^[[C^[[B^[[B^[[B^[[C^[[C^[[A^[[C^[[B^[[B^[[D^[[B^[[B^[[C^[[B^[[D^[[D^[[A^[[A^[[B^[[D^[[C^[[C^[[C^[[C^[[B^[[A^[[C^[[C^[[C^[[D^[[D^[[D^[[B^[[C^[[D^[[B^[[D^[[D^[[B^[[B^[[D^[[C^[[C^[[D^[[D^[[A^[[A^[[D^[[C^[[C^[[C^[[B^[[C^[[A^[[D^[[D^[[C^[[A^[[A^[[D^[[C^[[C^[[D^[[D^[[A^[[B^[[B^[[D^[[D^[[A^[[B^[[B^[[D^[[C^[[B^[[D^[[A^[[B^[[C^[[C^[[B^[[D^[[C^[[C^[[C^[[C^[[C^[[C^[[D^[[A^[[B^[[D^[[A^[[A^[[B^[[D^[[C^[[B^[[D^[[D^[[A^[[C^[[C^[[B^[[A^[[A^[[C^[[C^[[B^[[D^[[A^[[A^[[B^[[A^[[A^[[D^[[C^[[C^[[C^[[B^[[A^[[B^[[D^[[D^[[C^[[D^[[B^[[A^[[B^[[B^[[A^[[A^[[A^[[B^[[D^[[D^[[D^[[B^[[C^[[D^[[C^[[A^[[D^[[B^[[A^[[A^[[D^[[A^[[D
"""
comment = comment.replace('^','').replace('[[','')
# A is up
# B is down
# C is right
# D is left
x, y = 4846 + 61, 6900 + 61
size = 122
image = Image.open('Question.jpg')
data = image.load()
string = []
for new_place in comment:
# new_place = 'A'
if ( new_place == 'A' ): y -= size
if ( new_place == 'B' ): y += size
if ( new_place == 'C' ): x += size
if ( new_place == 'D' ): x -= size
try:
string.append(chr(data[x, y][0]))
# print data[x,y]
except:
pass
# print len(string)
sys.stdout.write( "".join(string) )
image.close()
根据题目提示把得到的字符串用MD5加密就能得到flag了(记得转换成小写)
2019-01-13补充:对于小伙伴们的issues如下
使用binwalk发现文件是由两张png组合而成的,分开,放在winhex中观察发现两个文件中字节数不同
分别是1d55db和1d5648于是将使用stegsolve对图片做减法,发现得到的图片最下角有一条红色的线,猜想相对大一些的图片可能是在结尾加入了密码字符而导致两个文件字节数不相同,将做完减法的图片放入stegsolve观察发现在Red plane 0处线短的很厉害,基本可以确定在图像结尾处添加了对r使用了低位加密的像素点,将大一点(1d5648)的图像放入stegsolve,用stegsolve的data extract选择red0得到结果观察字符串尾部,即可发现key