这题有点意思,“作业.png”堪称一件完美的艺术品了,作者大大真的是yyds,膜拜了!!!
只是跌跌撞撞好容易翻过了高山,又出现了妖,菜鸡是无论如何也擒不住了,还反被打死了!哎哎哎,崎岖坎坷咋的它就那么多!
解析:下载附件:
需要事先了解一下图像的模式:
别的没啥用,只需要知道:
- 对于L模式的图片,getpixel((x,y))是一个int值(0~255)
- 对于RGB模式的图片,getpixel((x,y))是一个三元组(r,g,b),即(0~255,0~255,0~255)
言归正传,开始撕题,先看“作业.txt”:
010打开password.png:
不出意外,在png后面发现zip数据,提取出来另存为1.zip,解压需要密码。
看来是需要通过“作业.png”解出压缩包密码了,打开“作业.png”,先把能补的都补上:
应该就是1和2处存疑:
- 先分析1:
这个fd绝对是点睛之笔了,根据fd直接可以推断出这个“作业.png”其实是个加密过程,并非我们天真以为的解密过程(也是,作者大大有那么好心么?想解密呀,自己搞去)。“作业.png”中的save就是password.png,而path其实是解密前的原图,而且是一张L模式的图片。
为什么呢?假设“作业.png”是解密过程,那path就是password.png,而题目给的password.png是一张“RGB”模式的图片,1处就一定得出现fd[0]或fd[1]或fd[2],根据图中的墨水痕迹看是不可能的,所以path只能是未知的L模式的图片,也就不是password.png,这样fd才能单独使用,因此断定“作业.png”是加密过程。fd后面的**暂时无法判断先不管。
- 现在来看2:
确定了“作业.png”是加密过程其实很容易了,(*,*,re)不就是password.png的像素值么?图片已给随便看:
getpixel((0,0))=(51,51,51),getpixel((1,1))=(53,53,53),...,getpixel((114,41))=(2,2,2),...
可以发现任何坐标点处的r、g、b值都是一样的,(*,*,re)当然就是(re,re,re)
- 回头看1处fd**:
猜测**不会太复杂,又“作业.txt”中说“加密方式只和坐标有关”,根据后面的“+y”合理猜测**应该是“+x”或“-x”。现在已经知道fd是一个int,“作业.txt”又说加密前的图片非黑即白,也就是fd只能是0或255。在password.png中,随便找个点getpixel((1,1))=(53,53,53),就有:(0+1+1)%68=53或(255+1+1)%68=53或(0-1+1)%68=53或(255-1+1)%68=53四种可能,但显然只有(255+1+1)%68=53是成立的,因此确定fd**就是fd+x。
确定了加密过程,接下来就是解密啦,相信大家都懂!Exp:1.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from PIL import Image
def enc():
im = Image.open('dec.png')
new = Image.new('RGB',(255,255),(255,255,255))#新建RGB全白图
for y in range(255):
for x in range(255):
fd = im.getpixel((x,y))
re = (fd+x+y) % 68 #加密
xin = Image.new('RGB',(1,1),(re,re,re))
new.paste(xin,(x,y))
new.save('enc.png')
def dec():
im = Image.open('password.png')
new = Image.new('L',(255,255),255) #新建L全白图
for y in range(255):
for x in range(255):
re = im.getpixel((x,y))[2] #取r,g,b哪个都行,反正都一样
fd = ((x+y)-re)%68 #解密
if (fd == 0): #全白图只涂黑的就行
new.putpixel((x,y),0)
new.save('dec.png')
dec()
enc()
得到:
可以发现enc.png和password.png是一样的,说明思路应该是对的。
扫描dec.png:
得到:IXE1VDYmMjk=,base64解码:
得到:!q5T6&29,输入解压1.zip,得到flag.docx:
哦My god!还要解密,我要哭了!这回倒是基本不影响阅读了,但却是直接玩完了,菜鸡表示看得见看不懂,呜呜
大佬说这里用到了背包加密和CBC加密,附上大佬解密脚本,exp:2.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from gmpy2 import invert
K = 1074
S = 43
inv = invert(S, K) # 求模逆元
# 背包密码解密
# 这个背包是最重的物件对应每字节8bit里的第1个bit,依此类推
def unpack(num):
A = [175, 87, 44, 21, 11, 5, 3, 1]
res = ''
for i in range(8):
if num >= A[i]:
# res = '1' + res
res = res + '1'
num -= A[i]
else:
# res = '0' + res
res = res + '0'
return int(res, 2)
C = [1817, 3100, 2240, 868, 172, 1816, 2025, 50, 172, 2289, 1642, 2067, 1337, 1681, 655, 2588, 691, 2591, 1595, 1552, 2498, 1513, 609, 1075, 602, 1420, 2720, 1042, 947, 2160, 731]
# 对密文直接进行背包解密
tmp = []
for i in C:
tmp += [unpack(i * inv % K)]
# 由于使用了CBC模式,解密结果要异或其上一位密文或初始向量
# 明文第1位不是flag,所以从第2位开始直接异或上一位密文后8位即可
for i in range(1, len(tmp)):
print(chr(tmp[i] ^ C[i-1] % 256), end = '')
(注:脚本出自https://blog.csdn.net/am_03/article/details/120106283
感谢大佬:彬彬有礼am_03 )
得到:flag{th1s_1s_S0_Ez_encr9pti0n}