crypto-byte_me(CSAWQual 2019)

pwn和AES结合的一题,比较有趣记录一下。
源码:

import random
import string
from Crypto.Cipher import AES

def pad(text, block_size):
    pad_size = block_size - (len(text) % block_size)
    if pad_size == 0:
        pad_size = block_size
    pad = chr(pad_size) * pad_size
    return text + pad


def random_key(num):
    return "".join(
        [random.choice(string.ascii_letters + string.digits) for n in range(num)]
    ).upper()


key = random_key(16)
random_string = random_key(random.randint(1, 15))


def ecb_enc(text):
    cipher = AES.new(key, AES.MODE_ECB)
    return cipher.encrypt(text)


def encryption_oracle(text, unknown):
    text = pad(random_string + text + unknown, 16)
    return ecb_enc(text)


def string_length_detect():
    str1 = encryption_oracle("A" * 16)


def main():
    # decoded_flag = "ZmxhZ3t5MHVfa24wd19oMHdfQjEwY2tzX0FyZV9uMFRfcjMxaWFiMTMuLi59".decode(
    #     "base64"
    # )
    decoded_flag=open('flag.txt','r').readline()
    print(encryption_oracle(decoded_flag, "").encode("hex"))

    while True:
        userInput = raw_input("Tell me something: ")
        print(encryption_oracle(str(userInput), decoded_flag).encode("hex"))


if __name__ == "__main__":
    exit(main())

大概分析下加密逻辑

nc到端口后大概是这样一个交互系统,首先会给出flag的密文,然后根据用户输入内容,生成userInput pad flag后加密的密文。

85829fadf80f134d0097a221ba4f47511b0970034a91e98db1c7c2024ff8327e76b516e79d29db3110aa732ebfcd5fce9970f767ef628600d95ca4d468f201c3
Tell me something: abc
abc
1e087d1961c2f82e0380db9aab7055ef4c36c6b44dc4443bda5ca0063c4cb3fcf41ba1587ca9414add8012a62da0b05674753964c838c7434663c1deac98c62f

加密函数逻辑大概是将明文text进行一次pad(random_string+text+unknown, 16)拼接操作,预处理得到长度为16倍数的字符串。其中random_string是长度1~15的随机字符串。
然后将预处理字符串进行一次AES/ECB加密,注意到加密密钥key是16位随机字符串。

解题思路

首先要熟悉一下AES/ECB加密,参考这里
可以说ECB模式是AES中最容易理解的模式,本质上就是把明文分组逐个用密钥加密,再把每一组密文拼接作为最终密文,可以看做若干个明文分组独立加密互不影响。
接下来就是构造输入字符了

  • 如果先输入head字符串,与开头的random_string刚好组成16位字符串分组,则后续分组加密与random_string无关。
random_string+headxxxx…xxxx…
  • 再构造如下明文字符串
random_string+headx*15+fflag{…
  • 显然上面字符串与下面字符串的前两个分组密文一定相同
random_string+headx*15+flag{…

以此类推,通过构造特殊的input字符串,比对对应密文分组,达到按字节爆破flag内容的效果。实际上程序一开始给出的flag单独加密结果不需要使用。
最终exp:

from pwn import *
r=remote('node4.buuoj.cn',29999)
re=r.recvline().decode()
re=bytes.fromhex(re)
set1=[]
print('attack start.......')
for i in range(1,17):
    sen_mes='a'*i
    try:
       r.sendline(sen_mes)
    except:
        print('something ro')
    r.recvline()
    re1=r.recvline().strip().decode()
    if re1[0:16] in set1:
        head=i-1
        break
    set1.append(re1[:16])
print(head)
#a reference
#attack [0:16]
mes='a'*head
realflag=''
for i in range(1,17):
    ju=mes+'b'*(16-i)
    r.sendline(ju)
    r.recvline()
    ju_recv=r.recvline().strip().decode()
    for j in range(32,128):
        mes_add=mes+'b'*(16-i)+realflag+chr(j)
        r.sendline(mes_add)
        r.recvline()
        try_recv=r.recvline().strip().decode()
        if ju_recv[32:64]==try_recv[32:64]:
            realflag=realflag+chr(j)
            print('realflag:{}'.format(realflag))
            break
#attack[16:32]            
for i in range(1,17):
    ju=mes+'b'*(16-i)
    r.sendline(ju)
    r.recvline()
    ju_recv=r.recvline().strip().decode()
    for j in range(32,128):
        mes_add=mes+realflag[i:15+i]+chr(j)
        r.sendline(mes_add)
        r.recvline()
        try_recv=r.recvline().strip().decode()
        if ju_recv[64:96]==try_recv[32:64]:
            realflag=realflag+chr(j)
            print('realflag:{}'.format(realflag))
            break
#attack all
for i in range(1,17):
    ju=mes+'b'*(16-i)
    r.sendline(ju)
    r.recvline()
    ju_recv=r.recvline().strip().decode()
    for j in range(32,128):
        mes_add=mes+realflag[i+16:31+i]+chr(j)
        r.sendline(mes_add)
        r.recvline()
        try_recv=r.recvline().strip().decode()
        if ju_recv[96:128]==try_recv[32:64]:
            realflag=realflag+chr(j)
            print('realflag:{}'.format(realflag))
            if chr(j) == '}':
                exit(0)
            break

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值