持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情
最近太累了,我们还是做一些密码题来放松一下吧。
[NCTF2019]Sore
下载附件得到txt文件和一个py文件,我们先分析一下txt文件:
nsfAIHFrMuLynuCApeEstxJOzniQuyBVfAChDEznppfAiEIDcyNFBsCj #部分
这是类似维吉尼亚密码,只不过这里的密钥字符在ascii_letters里面选,手动去做非常复杂,于是我们找到一个网站解密vigenere-solver:
得到关键数据:
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
下载附件后得到文件内容如下:
感觉这种类型的数据很奇怪,我们有出题人的代码:
#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方向的题目,有兴趣的小伙伴可以去找一找了解一下。