2023年磐石行动第十六周

1、冰冰给我flag可以吗

题目内容:

png文件解密,flag用DASCTF包裹

题目下载之后发现pypy.exe和一个无法显示的图片,根据题目的描述,应该是图片被这个程序加密了,需要解密才能得到正确的图片:

通过pyinsxtractor.py将exe程序解包,发现使用python3.7(满足<3.9的条件),直接使用

uncompyle6 pypy.PYC > pypy.py

得到加密程序的源码

# uncompyle6 version 3.9.0
# Python bytecode version base 3.7.0 (3394)
# Decompiled from: Python 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: pypy.py
import base58

def enc(stream, file):
    text = base58.b58encode(stream) #base58编码
    temp = list(bytes.decode(text))
    return temp


if __name__ == '__main__':
    fp = open('冰冰给我flag可以吗.png', 'rb')
    context = fp.read()
    key = context[0] #文件第一个字节 0x89
    fp.close()
    fp = open('冰冰给我flag可以吗.png', 'wb')
    tmp = enc(context, fp) #enc编码
    for i in range(len(tmp)):
        tmp[i] = chr(ord(tmp[i]) ^ key) #异或key

    fp.write(bytes((''.join(tmp)), encoding='utf-8'))
    fp.close()
# okay decompiling .\pypy.pyc

加密过程就是base58编码+异或0x89,编写exp如下:

import base58
import time

def main():
    print('begin')
    begin_time = time.time()
    #读入图片
    with open('0.png','rb') as f:
        datas = f.read()

    datas = list(bytes.decode(datas)) #字节转字符list
    key = 0x89

    #存储png_data
    png_data = ''
    for data in datas:
        png_data += chr(ord(data)^key) #获得异或之后的png数据
    #base58解码
    png_data = base58.b58decode(png_data)
    
    #写入图片
    with open('flag.png','wb') as f:
        f.write(png_data)
    print('end')
    print('used time is %.2fs'%(time.time()-begin_time))

if __name__ == '__main__':
    main()
    
#begin
#end
#used time is 9.11s

得到flag图片如下:

2023年磐石行动第十六周_第1张图片

图片中flag如下:

DASCTF{S@Y_nO_13u7_tH3_6odY_!s_H0Ne~T}

但是这个题目flag错了,需要提交,将6odY修改为6ody

DASCTF{S@Y_nO_13u7_tH3_6ody_!s_H0Ne~T}

2、Yusa的密码学课堂——密码分析

题目描述

这节课老师给Yusa讲述了一个黄道十二宫杀手的故事。。。

题目下载之后得到task.py

import random
from secret import flag,table

one = []
zero = []
for each in table:
	if round(random.random()):
		one.append(each)
	else:
		zero.append(each)
output=""
for each in flag:
	each = bin(ord(each))[2:].rjust(8,'0')
	for i in each:
		if i == '1':
			output += random.choice(one)
		if i == '0':
			output += random.choice(zero)
print(output)

'''
♥•∧∨∨◦∧Δ∨♠∧⇑⇐⇑♥∞∨ℵ⇐•∧6◦♣⇑⊗⊕∧⇐∧ℵ⇒Δ•⊕ℵ6♠⊕∧∨ΩΔΔ⊕ℵ⊗♥∨Ω◦♠⊗♥⊗ℵ8♣•∧⊕8∧⊗⊕⇑⊗◦∧⊕◦∞Δ∨ℵ⊗∧∧∧Δ∧⇓⇒⇑⊕♦♣8⊕∞Θ⊕⊕⇐∞∧⊕♦∇∞ℵ6⊕♦♦Δ♠♣∨⊕∞∧∨⊕◦ΩΩΔ♦⇑6⇐ℵ∞∨8⇐♠6⇐◦∞⇐⊕Δ∇8•◦∧∧♥⇓Θ♥♠∞ΔΔ◦⇑8∨⊕⊗◦♥♦∧⇒♦♦♣♠8∞ℵ8♦∨••∨∧♣⇐⇑6Θ⇓∧Θ⇐♦8∨⊗∞⇑∧⊗•♦∨∇⇒∧⊗⇐⊕8⇓♠⇑∨••8♥⊕Ω•⇐⇒◦♠⇐♦♠∞⇓⇑⇐⇐6∨◦Θ6♠∇ℵ♦♥◦•∇⇑⇐♣⊕8ℵΘ⇑⇒•♥∧◦∞∨∨♦⊗⇒8♥Θ♣⇑∞∨♦⇑◦♠⇑♥♥♠Δ8Δ⇒⇒Δ6♣⇒♦∇ΘΔ8♣♦⊕⇑♠Θ♥⇐8Δ•∧ℵ•68∧ΩΘ∨∞♣♦∨⇓6•Δ⊗♠♣⇓ℵ♥∞
'''

这个题目初始看着特别唬人,加密原理

1、将table的字符通过round(计算小数四舍五入情况)添加到one和zero中
2、接着将flag中的字符转为二进制,如果不足8位,在高位补0
3、根据字符二进制0和1情况将one和zero中的字符随机输出

从上述分析发现存在两次随机,感觉特别有难度,但是仔细思考就会发现,其实第二个随机其实没啥用,只要知道one和zero,判断输出的字符是在one中,那么那一位就是1,如果字符在zero中,那一位就是0,具体输出那个字符不需要关心,接着就发现table无法知道,随机数也没有办法预测啊,接着分析就会发现一个隐藏的条件,flag应该是可见字符,那么ascii范围<127,那么flag的最高位一定是0,我们通过这个条件知道一部分zero或者全部zero,接着判断output是不是在zero判断当前位是1还是0,接着编写exp如下:

#encoding=utf-8

output = '♥•∧∨∨◦∧Δ∨♠∧⇑⇐⇑♥∞∨ℵ⇐•∧6◦♣⇑⊗⊕∧⇐∧ℵ⇒Δ•⊕ℵ6♠⊕∧∨ΩΔΔ⊕ℵ⊗♥∨Ω◦♠⊗♥⊗ℵ8♣•∧⊕8∧⊗⊕⇑⊗◦∧⊕◦∞Δ∨ℵ⊗∧∧∧Δ∧⇓⇒⇑⊕♦♣8⊕∞Θ⊕⊕⇐∞∧⊕♦∇∞ℵ6⊕♦♦Δ♠♣∨⊕∞∧∨⊕◦ΩΩΔ♦⇑6⇐ℵ∞∨8⇐♠6⇐◦∞⇐⊕Δ∇8•◦∧∧♥⇓Θ♥♠∞ΔΔ◦⇑8∨⊕⊗◦♥♦∧⇒♦♦♣♠8∞ℵ8♦∨••∨∧♣⇐⇑6Θ⇓∧Θ⇐♦8∨⊗∞⇑∧⊗•♦∨∇⇒∧⊗⇐⊕8⇓♠⇑∨••8♥⊕Ω•⇐⇒◦♠⇐♦♠∞⇓⇑⇐⇐6∨◦Θ6♠∇ℵ♦♥◦•∇⇑⇐♣⊕8ℵΘ⇑⇒•♥∧◦∞∨∨♦⊗⇒8♥Θ♣⇑∞∨♦⇑◦♠⇑♥♥♠Δ8Δ⇒⇒Δ6♣⇒♦∇ΘΔ8♣♦⊕⇑♠Θ♥⇐8Δ•∧ℵ•68∧ΩΘ∨∞♣♦∨⇓6•Δ⊗♠♣⇓ℵ♥∞'


#构造zero字典
zero = []
for i in range(0,len(output),8):
    if output[i] not in zero:
        zero.append(output[i])

#通过判断当前位置0和1构造二进制flag字符串
flag = ''
for i in range(len(output)):
    if output[i] in zero:
        flag += '0'
    else:
        flag += '1'

#二进制转字符串
for i in range(0,len(flag),8):
    print(chr(int(flag[i:i+8],2)),end='')
#DASCTF{a30bb82811cd162434f78796c4b3dace}

得到flag如下:

DASCTF{a30bb82811cd162434f78796c4b3dace}

你可能感兴趣的:(一周一练,python,开发语言)