bugku-misc-粗心的佳佳

这题有点意思,“作业.png”堪称一件完美的艺术品了,作者大大真的是yyds,膜拜了!!!

只是跌跌撞撞好容易翻过了高山,又出现了妖,菜鸡是无论如何也擒不住了,还反被打死了!哎哎哎,崎岖坎坷咋的它就那么多!

解析:下载附件:

需要事先了解一下图像的模式:

别的没啥用,只需要知道:

  1. 对于L模式的图片,getpixel((x,y))是一个int值(0~255)
  2. 对于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}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值