aes256密钥生成工具 aes 密钥长度_判断密码长度

大家好,这里是求道轩,我是求道仙人。 给我5分钟,带你看世界。

基本看完古典密码部分,虽然有些新的脑洞,但是暂时不值得写,比如JS混淆,曼彻斯特编码,ook,敲击码,base64隐写等,栅栏,凯撒,猪圈,夏多,培根,一次异或等,但是都是直接大概接触过的。

下面是两天的成果

aes256密钥生成工具 aes 密钥长度_aes256密钥生成工具_02


古典密码-编码

aes256密钥生成工具 aes 密钥长度_aes256密钥生成工具_03


古典密码-加密

今天的主角是之前没怎么接触的AES,跟着答案做了几题,收获颇多。

[来源]
集团信安新平台-Crypto训练专题

[工具]
python

[知识点]

AES

高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法,对称加密算法也就是加密和解密用相同的密钥。
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。
在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。
密钥的长度可以使用128位、192位或256位。
密钥的长度不同,推荐加密轮数也不同。

AES有多种加密模式,今天先了解最简单的-电码本模式(Electronic Codebook Book (ECB)

将整个明文分成若干段相同的小段,然后对每一小段进行加密。

每128位(16字节)独立 独立 相互独立

aes256密钥生成工具 aes 密钥长度_bc_04


ECB模式

具体加密函数就不管了,反正每128位通过给定密钥key加密得到密文。
下来看两道题。

[解析]

AES04 ECB

下载后两个文件ecbecb.py,分别是可执行程序以获取flag,以及源代码供分析用。
关注下ecb.py核心代码如下:

if __name__ == '__main__':
    print('flag', aes_ecb_encrypt(pad16(flag)))
    while True:
        print("Input a string to encrypt (input 'q' to quit):")
        user_input = raw_input()
        if user_input == 'q':
            break
        output = aes_ecb_encrypt(pad16(user_input+flag))
        print("Here is your encrypted string, have a nice day :)")
        print(output)

首先打印了flag的AES-ECB模式加密结果,然后永真循环,接受用户输入user_input,然后对user_input+flag字符串AES加密输出。由于ECB模式每16字节相互独立,那么显然这里可以爆破,原理如下:

  • 输入15个A,记录其作为前缀加flag的加密结果
  • 爆破flag的第一个字符x,15*A+x加密结果和前面加密结果的前16字节相同。
  • 其他字符同理,比如爆破第17个字符,那么继续输入15个A
  • 首先需要知道flag的长度,通过逐渐增加输入长度进行测试。当密文变长时,说明此时的输入+flag长度正好被16整除,且比密文少16个字符,以此求出flag长度。

那么最终脚本就出来了

# -*- coding: utf-8 -*-
from pwn import *

r = process('./ecb')
def sendmessage(text):
    s = r.recvline()
    r.sendline(text)
    s = r.recvline()
    s = r.recvline()
    return s.strip()

s = r.recvline()
encflag = s.split(' ')[1][1:-2]
tmp = 'A'
flag_length = 0
l = len(sendmessage(tmp))
while(True):
    tmp += 'A'
    ll = len(sendmessage(tmp))
    if(ll>l):
        flag_length = (ll - 32)/2  - len(tmp)
        break
print flag_length
# flag_length = 21
# 开始爆破每位
dic = 'abcdefghijklmnopqrstuvwxyz0123456789_{}'
known = ''
for i in range(flag_length):
    print 'ROUND %d' %(i+1)
    target = 'A'*(15+(i//16)*16-len(known))
    print 'TARGET text: %s'%target
    enctext = sendmessage(target)
    print 'TARGET: %s'%enctext[(i//16)*32:(i//16)*32+32]
    for j in dic: #爆破合法字符集
        tmptext = 'A'*(15+(i//16)*16-len(known))+known+j
        print 'TRY %s' % tmptext
        enctmp = sendmessage(tmptext)
        print 'TRY enctmp %s' % enctmp[(i//16)*32:(i//16)*32+32]
        if(enctmp[(i//16)*32:(i//16)*32+32]==enctext[(i//16)*32:(i//16)*32+32]):
            known += j
            break
    print known

运行结果如下

aes256密钥生成工具 aes 密钥长度_加密结果_05


ECB模式爆破每位

找到flag{g3t_17_by_d1g17}

AES05 ECB+

和上面类似,加强版
看看核心代码

if __name__ == '__main__':
    print('flag', aes_ecb_encrypt(pad16(flag)))
    while True:
        print("Input a string to encrypt (input 'q' to quit):")
        user_input = raw_input()
        if user_input == 'q':
            break
        b = getrandbits(1)
        output = aes_ecb_encrypt(pad16(('A'*b)+user_input+flag))
        print("Here is your encrypted string, have a nice day :)")
        print(output)

通过分析补充16字节函数,如果text正好是16的倍数,也会追加16个字符。

def pad16(text):
    return text + chr(16-(len(text)%16))*(16-(len(text)%16))

在用户输入前面随机加入0或1位A,和上面类似,但是需要变种。

判断flag长度

AES在加密前需要padding,可以通过大量相同内容测试,判断出flag的长度,比如不停的输入1,

aes256密钥生成工具 aes 密钥长度_判断密码长度_06


ECB模式长度测试

发现密文长度可能是32或48,说明随机加的A导致变化,那么原始flag长度就是30。
更进一步爆破长度脚本如下:

# Step 1: find length of flag
count = 1
flaglength = 0
s = r.recvline()
while(True):
    tmpstr = 'A' * count 
    legal = len(sendmessage(tmpstr))
    for i in range(10): #每种前缀尝试10次,看看是否有变化
        tmpl = len(sendmessage(tmpstr))
        if(tmpl != legal):
            ll = legal
            if(tmpl>ll):
                ll = tmpl
            flaglength = ll/2 - count - 17
    if(flaglength>0):
        break
    count += 1
print '*** flag length is %s ***' % str(flaglength)

进一步分析服务器前缀加A影响,如果输入一个A,返回长度可能会不一样,那么输入17个A也不会不一样。较长的结果是18A+flag,较短的是17A+flag结果。
然后我们输入16个A,如果返回结果和上面17A+flag相同,说明服务器帮忙加A了,丢弃,如果和上面不同,说就是16A加flag的结果,依次类推,我们知道了A+flag的正确结果。

flaglength = 30
# Step2: get result of different padding A
paddingA = []
for i in range(20):
    paddingA.append('')
while(True):
    s = sendmessage('A'*17)
    if(len(s)==128): # 64个密文字符  18A+30flag+16个pading
        paddingA[18] = s
        break
for i in range(17,0,-1):
    print "padding {}*A".format(i)
    for j in range(10):
        tmpstr = sendmessage('A'*i)
        if not tmpstr == paddingA[i+1]:
            paddingA[i] = tmpstr
            break
    print paddingA

aes256密钥生成工具 aes 密钥长度_aes256密钥生成工具_07


不同长度前缀A+flag的加密结果

接下来就是要爆破字符了,如何爆破第1个字符呢,通过构造15个A+任意可见字符的前缀,尝试让服务器替我们加密,看哪个结果和刚才的答案paddingA[15]一样。
如果服务器替我们增加了一个A也无所谓,因为16个A的加密结果我知道,扔掉就可以了。以此可以推出flag的第一位,一直到15位类似
完整脚本如下

# Step 3: begin to burp 1~15 digit
known = ''
dic = 'abcdefghijklmnopqrstuvwxyz0123456789_{}'
for i in range(15):
    print '****** ROUND %d *****' % (i+1)
    last32 = paddingA[16-i][0:32]
    new32 = paddingA[15-i][0:32]
    print 'LAST: %s' % last32
    print 'NEW: %s' % new32
    for j in dic:
        tmpstr = 'A'*(15-i)+known+j
        tmps = ''
        while(True):  #重复发送,如果发现服务器没给我们添加A 跳出循环
            print 'SEND: %s'%tmpstr
            tmps = sendmessage(tmpstr)
            if not (tmps[0:32]==last32):
                break
        if(tmps[0:32]==new32):
            known += j
            print known
            break

aes256密钥生成工具 aes 密钥长度_bc_08


开始爆破flag 可以发现上面f服务器就没加A,一次就直接判断结果了。

由于ECB模式下各段互不干扰,因此接下来类似,最终结果如下

aes256密钥生成工具 aes 密钥长度_服务器_09


最终flag

找到flag{d0_n0t_4fr4id_jus7_7r1ck}[答案]
flag{g3t_17_by_d1g17}
flag{d0_n0t_4fr4id_jus7_7r1ck}