记moectf2020新生赛 及 部分MISC题WP

这篇博客详细记录了解决两道信息安全竞赛题目——MISC-两只企鹅和CRYPTO-EasyRSA的过程。在MISC题目中,作者通过分析图片尾部的隐藏压缩包,使用Stegosaurus工具从pyc文件中解密出DES密码,最终获得flag。而在Crypto题目中,通过理解PNG文件格式、CRC32爆破及RSA加密原理,成功还原了加密key并解密出PNG数据。

大二老腊肉第一次参加新生赛,虽然被hidden了,但还是玩的挺开心🙃

由于是新生赛,题目普遍难度不大,挑了几道比较有趣的题目写了WP
(其他题目在最后给出各位大佬的WP供参考

MISC-两只企鹅

下载题目得到一张🐧图片,打开winhex拖到尾部发现压缩包。
用winhex搜索“PK”找到压缩包头部,正好也找到了压缩包密码。
在这里插入图片描述
分离压缩包并解压,得到flag.pyc,第一时间想到用uncompyle6到pyc反编译

uncompyle6 -o flag.py flag.pyc

然后运行flag.py画了另一只企鹅 (单身🐕HP-1

在这里插入图片描述

flag.py最后有DES加密过的flag,只有找到密钥才能解密。
密钥找了半天啥也找不到,就开了hint…说要注意冗余代码的作用,又卡了好久
直到群里师傅说到 Stegosaurus…

Stegosaurus是一种用于在Python字节码中嵌入Payload的隐写工具
具体可以看 https://www.freebuf.com/sectool/129357.html

直接用 Stegosaurus 解出pyc文件中隐藏的DES密码在这里插入图片描述
运行flag.py输入密码得flag
在这里插入图片描述

MISC-Show off

(又是一道吃🐕粮的题

题目给了一个图片和一个加密脚本。通过阅读脚本,发现脚本把另外一张jpg的数据通过 ”或运算“ 或者 ”与非运算“隐藏在了所给图片的RGB值中,其中每个色块隐藏了3位二进制值。

这道题的关键就是读懂下面的关键代码

#关键代码
if ((data[curr//8] >> (curr%8)) & 1) == 1:#隐藏位为1
	pixel[k] &= ~(1 << (table.index(key[curr%KeyLen])%8) )
else:#隐藏位为0
	pixel[k] |= (1 << (table.index(key[curr%KeyLen])%8) )

我们现有的是运算之后的色块RGB值和key的值。由于我太菜,不知道怎么倒回去求:是发生了或运算还是与非运算,那就爆破吧!2333

def solve(c,key):#爆破
    for i in range(256):
        if i &~ key==c:
            return '1'
    return '0'

这样我们就得到了隐写进去的jpg文件的所有的01序列,也就得到了这张jpg图片
解密脚本:

#!/usr/bin/python3

from PIL import Image, ImageDraw

table = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?!."
Key = "Wouldyouliketoseemygirlfriend?"


def solve(c,key):
    for i in range(256):
        if i &~ key==c:
            return '1'
    return '0'


class Cover:
    def __init__(self,raw_path):
        self.img = Image.open(raw_path)
        self.img_heigh, self.img_width = self.img.size
        self.draw = ImageDraw.Draw(self.img)

    def extract(self,key:str):
        s=self.img_heigh*(self.img_width)
        KeyLen = len(key)
        res=[]
        temp=''
        curr=0
        for i in range(self.img_heigh):
            for j in range(self.img_width):
                pixel = list(self.img.getpixel((i,j)))
                for k in range(3):
                    #print(pixel[k],(1 << (table.index(key[curr%KeyLen])%8)) )
                    temp+=solve(pixel[k],(1 << (table.index(key[curr%KeyLen])%8)))
                    curr+=1
                    if curr%100000==0:#输出解密进度
                        print(curr,curr/s/3)
                    if len(temp)==8:#每8位二进制转换为16进制
                        t=list(temp)
                        t.reverse()	#这样解的二进制序列是反着的,需要倒回来
                        res.append(int(''.join(t),2))
                        temp=''

        f=open('solve.jpg','wb')
        f.write(bytes(res))
        
        return bytes(res)
        

def show_off():
    '''
    with open("./test.txt", "rb") as f:
        data = f.read()
    '''
    new_cover = Cover("merged.png")
    new_cover.extract(Key)

if __name__ == "__main__":
    show_off()

打开图片,啊这?!(单身🐕HP-100
在这里插入图片描述
右击图片查看属性,在备注栏找到flag
(🐧,yyds!
在这里插入图片描述

CRYPTO-Easy RSA (非预期

这题不小心用MISC的方法把⏳带佬的RSA给非预期了…
下面说一下思路:

题目给了加密后的PNG数据和加密脚本。阅读脚本,发现是用随机生成的key逐位异或PNG文件数据,最后用RSA加密了key。

由题目中key的生成看出key共有38位,key的各位的范围 0-255

key = b''.join([urandom(1) for _ in range(38)])

为了形象地表达,我们画一个表格
在这里插入图片描述
我们知道PNG文件是有固定格式的(具体可以百度

①先看PNG文件头。随便找一张PNG,用winhex打开。我标蓝了文件头的前38比特,其中划红线的地方大多数的PNG都是一样的。
在这里插入图片描述
那么通过异或密文和明文,可以还原key的相应位。
在这里插入图片描述
②然后看文件尾,PNG文件尾12比特固定为 00 00 00 00 49 45 4E 44 AE 42 60 82
在这里插入图片描述
那么文件尾对应key中的哪几位呢?

我们用全文长度对38(key的长度)取余,发现结果是36。key又是循环使用的,说明文件尾12比特对应了key的24-35位。
在这里插入图片描述
③CRC32爆破PNG宽高求key中间部分

CRC32爆破PNG宽高是MISC中的常用手段,下图划红线的部分就是划蓝线部分的CRC32值
在这里插入图片描述
既然我们已知key的相应位,我们可以通过异或轻易求出flag.png的CRC32,通过对flag.png高宽的爆破,得到明文的16-23比特,再异或得到key的16-23比特。

下面给出一个python的爆破脚本

import zlib
import struct
crc32key = 0xCE084A0A #补上0x,winhex下copy hex value。
data = bytearray(b'\x49\x48\x44\x52\x00\x00\x05\x4A\x00\x00\x07\x02\x08\x06\x00\x00\x00')   #winhex下copy grep hex。
n = 4095 #理论上0xffffffff,但考虑到屏幕实际/cpu,0x0fff就差不多了
for w in range(n):#高和宽一起爆破
    width = bytearray(struct.pack('>i', w))#q为8字节,i为4字节,h为2字节
    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)
        if crc32result == crc32key:
            print(width,height)
            print(int.from_bytes(width,"big"),int.from_bytes(height,"big"))
            print(hex(int.from_bytes(width,"big")),hex(int.from_bytes(height,"big")))

        

得到0x104,0x104,则flag.png对应位为: 00 00 01 04 00 00 01 04
在这里插入图片描述
④最后两比特通过爆破RSA解出
只需遍历最后两比特,用RSA加密key,与题中给出的cipher比较即可。
(遍历次数只有256*256次,完全可以爆破。)
在这里插入图片描述
通过以上四步我们就还原了key可以开心地解密了(因为是异或加密,解密就用加密脚本再加密一次就行了)

下面给出解密脚本

from random import *
from gmpy2 import *
from Crypto.Util.number import *
from os import urandom
import zlib
import struct
XOR = lambda s1,s2 : bytes([x1^x2 for x1,x2 in zip(s1,s2)])

def enc(plain , key):
    block = len(plain) // len(key)
    res = b''
    if len(plain) % len(key) != 0:
        block += 1
    for i in range(block):
        res += XOR(plain[i*len(key):(i+1) *len(key)] , key)
    return res

def brute_force(key):
    cipher = 6295099350956528607396037601959877791934298828699677426193295804984422774839568282303538751735922668555038227591570508075836074695625068231718282637434619892411169336112708789051866841386960010983915517645375007603334301928436122113314644281698850356618954553636354588295318626415800893401794520007438195584651069726083411367546831544328390372733644828779287264837918630675648320615405654401121853964018368418748858627359640564171296914178768702389891031507220222973953333450633882912483139393113713737597059909038607300118809911390415352026833692334281800776950518743683416169290877451319659303510788506268753501781
    n = 18960722153219768424825297430288596402599615217676565923231065891419305439903969133336842165182164434305093888822528498525074593142143485234947709395152694407269932336781777523140964102701768500976985551580573991501756530871678077669095976326733950375907332862285201282966826531285878400261799511805034490220073023034504007192915282979526255340588329327377354539737224907994999568858311693959486822039283333461158422904468721851447600849924776566655800183080470425226397793377929935260296448192941725176241434514639433822949600965755910239847434831269722996200119639816772695986801602222805123304519611949635935587881
    e = 7
    for i in range(256):
        for j in range(256):
            key[36]=i
            key[37]=j
            k=bytes_to_long(bytes(key))
            if pow(k,e,n)==cipher:
                return

f=open('enc_flag.png','rb')
c=f.read()
f.close()
f=open('sample.png','rb')#sample:一张正常的PNG
s=f.read()
f.close()

s_start=s[:16]
s_end=s[-12:]
c_start=c[:16]
c_end=c[-12:]


k_start=XOR(s_start,c_start)#key 0-15
k_end=XOR(s_end,c_end)  #key 24-35


#爆破出来是0x104*0x104,异或解出key 16-23
k_middle=[107, 56, 164, 91, 186, 40, 226, 217]#key 16-23
#ps 因为我懒得加 CRC32 加密脚本了,直接把爆破好的key放过来了.....

key=[]
for i in list(k_start):
    key.append(i)
for i in list(k_middle):
    key.append(i)
for i in list(k_end):
    key.append(i)
key.append(0)
key.append(0)
            
brute_force(key)

#print(key)

f = open('flag.png' , 'wb')
m = enc(c , key)
f.write(m)
f.close()

解密后扫码得flag

若有错误,欢迎指出~

其他选手的WP

BlackBird - Misc Classic-Crypto Web Algorithm Reverse Pwn
blackbird-bb.github.io

吉吉 - Reverse Crypto Misc Web Algorithm Classic-Crypto Android
https://zhuanlan.zhihu.com/p/262761814

framist - Reverse Pwn Algorithm Crypto Classic-Crypto Misc Web
https://framist.github.io/2020/10/09/%E3%80%90moeCTF%E9%A2%98%E8%A7%A3-0x00%E3%80%91%E5%BA%8F/

Noahtie - Reverse Pwn Algorithm Crypto Classic-Crypto Misc Web Android
https://noahtie.github.io/2020/10/12/MoeCTF2020/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值