BUUCTF密码进阶-DAY7

353 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情

最近太累了,我们还是做一些密码题来放松一下吧。

[NCTF2019]Sore

下载附件得到txt文件和一个py文件,我们先分析一下txt文件:

nsfAIHFrMuLynuCApeEstxJOzniQuyBVfAChDEznppfAiEIDcyNFBsCj #部分

这是类似维吉尼亚密码,只不过这里的密钥字符在ascii_letters里面选,手动去做非常复杂,于是我们找到一个网站解密vigenere-solver:

www.guballa.de/vigenere-so…

得到关键数据:

vlbeunuozbpycklsjxlfpaq

再分析task.py文件:

from string import ascii_letters
from flag import flag
ctoi = lambda x: ascii_letters.index(x)
itoc = lambda x: ascii_letters[x]
key = flag.strip('NCTF{}')
len_key = len(key)
plaintext = open('plaintext.txt', 'r').read()
plain = ''.join(p for p in plaintext if p in ascii_letters)
cipher = ''.join( itoc( ( ctoi(p) + ctoi( key[i % len_key] ) ) % 52 )  for i,p in enumerate(plain) )
open('ciphertext.txt', 'w').write(cipher)

要解的此题分为三步:

Kasiski 实验

重合指数攻击

字母频率分析

通过密钥长度进行分组可以分成8组,每一组对应的密钥字母是一样的,利用字母遍历来算出最符合字母频率的情况,网上找到个解题脚本:

from string import ascii_letters
ciper='nsfAIHFrMuLynuCApeEstxJ'
plain='Shewouldntwalkrightnext'
x=zip(plain,ciper)
flag=''
for i,j in x:
        if ascii_letters.index(i)<ascii_letters.index(j):
            flag+=ascii_letters[ascii_letters.index(j)-ascii_letters.index(i)]
        elif ascii_letters.index(i)>ascii_letters.index(j):
            flag+=ascii_letters[52-ascii_letters.index(i)+ascii_letters.index(j)]
        elif ascii_letters.index(i)==ascii_letters.index(j):
            flag+=ascii_letters[0]
print(flag)

得到flag:

vlbeunuozbpycklsjXlfpaq

[SUCTF2019]MT

下载后得到附件内容如下:

from Crypto.Random import random
from Crypto.Util import number
from flag import flag
def convert(m):
    m = m ^ m >> 13
    m = m ^ m << 9 & 2029229568
    m = m ^ m << 17 & 2245263360
    m = m ^ m >> 19
    return m
def transform(message):
    assert len(message) % 4 == 0
    new_message = ''
    for i in range(len(message) / 4):
        block = message[i * 4 : i * 4 +4]
        block = number.bytes_to_long(block)
        block = convert(block)
        block = number.long_to_bytes(block, 4)
        new_message += block
    return new_message
transformed_flag = transform(flag[5:-1].decode('hex')).encode('hex')
print 'transformed_flag:', transformed_flag
# transformed_flag: 641460a9e3953b1aaa21f3a2

简单分析一下我们会发现,把信息不断的异或,以及与运算最后得到了密文;那么反之我们将密文连续加密就会得到原文。通过 convert() 函数获取随机数将 flag 加密,需要我们逆向 extract_number函数,于是可以构造脚本:

// python2
from Crypto.Util.number import *
def convert(m):
    m = m ^ m >> 13
    m = m ^ m << 9 & 2029229568
    m = m ^ m << 17 & 2245263360
    m = m ^ m >> 19
    return mdef transform(message):
    assert len(message) % 4 == 0
    new_message = ''
    for i in range(len(message) / 4):
        block = message[i * 4 : i * 4 +4]
        block = bytes_to_long(block)
        block = convert(block)
        block = long_to_bytes(block, 4)
        new_message += block
    return new_message
def decode(src):
	tmp=src
	while True:
		res=tmp
		tmp=transform(tmp.decode('hex')).encode('hex')
		if(tmp==src):
			return(res)
c1="641460a9"
c2="e3953b1a"
c3="aa21f3a2"
m=decode(c1)+decode(c2)+decode(c3)
print(m)
#84b45f89af22ce7e67275bdc

[AFCTF2018]MagicNum

下载附件后得到文件内容如下:

图片.png

感觉这种类型的数据很奇怪,我们有出题人的代码:

#include <stdio.h>
char flag[]="afctf{sec_is_everywhere}";
 
int main()
{
    for(int i=0;i<6;++i){
        printf("%20f\n",*(float*)(flag+i*4));
    }
    return 0;
}

简单分析一下可以得知将上面的数据转换为 内部存储模式 再转换为 二进制数据,再转成字节,就得到加密明文,但具体要怎样做呢。编写进制转换脚本如下:

import struct
import binascii
s=[72065910510177138000000000000000.000000,71863209670811371000000.000000,18489682625412760000000000000000.000000,72723257588050687000000.000000,4674659167469766200000000.000000,19061698837499292000000000000000000000.000000]
a=''
b=''
for i in s:
    i=float(i)
    tmp=struct.pack('f', j).hex()#大端
    b+=tmp
print (binascii.a2b_hex(a))
print (binascii.a2b_hex(b))

[GKCTF 2021]Random

附件如下:

import random
from hashlib import md5


def get_mask():
    file = open("random.txt","w")
    for i in range(104):
        file.write(str(random.getrandbits(32))+"\n")
        file.write(str(random.getrandbits(64))+"\n")
        file.write(str(random.getrandbits(96))+"\n")
    file.close()
get_mask()


flag = md5(str(random.getrandbits(32)).encode()).hexdigest()
print(flag)

注意到代码中有random函数,涉及伪随机数,random.txt文件内容如下:

2584323193
1099154419438958164
35367876945070316311325317953
481304047
12782770146993102006
24789628292419559503661788402
.....
总共是312个随机数

关键函数在于:

random.getrandbits(k)

MT算法能生成1-623个32位随机数,而我们有 624个已知随机数,具体分析如下:312个随机数,104个32位的,104个64位的,以此类推,都是由32位序列产生成,所以104个32位的需要生成104个随机数,104个64位的需要生成208个随机数,104个96位的需要生成312个随机数。加起来总共624个。那么我们就可以预判出下一个随机数。

from hashlib import md5
from randcrack import RandCrack
 
with open(r'random.txt', 'r') as f:
    l = f.readlines()
l = [int(i.strip()) for i in l]
t = []
for i in range(len(l)):
    if i % 3 == 0:
        t.append(l[i])
    elif i % 3 == 1:
        t.append(l[i] & (2 ** 32 - 1))
        t.append(l[i] >> 32)
    else:
        t.append(l[i] & (2 ** 32 - 1))
        t.append(l[i] & (2 ** 64 - 1) >> 32)
        t.append(l[i] >> 64)
rc = RandCrack()
for i in t:
    rc.submit(i)
flag = rc.predict_getrandbits(32)
print(md5(str(flag).encode()).hexdigest())

今天刷的题目还是挺有难度的,第二个题目涉及了一直异或之后想与要我们怎么复原的思想,第三个题涉及到了计算机中如何保存浮点数的大小端问题,最后这一道题目涉及了随机函数的伪随机数知识点,即当我们知道一定信息后随机数就变得可以预测了,也是一道挺有挑战的题目,而且伪随机数题目之前还专门总结了WEB方向的题目,有兴趣的小伙伴可以去找一找了解一下。