ECB模式攻击

ECB加密

ecb模式使用相同的key分块对明文分别进行加密,相同的明文获得相同的密文输出。如下所示:


加密模式

ECB模式对明文进行分块,然后用key进行加密,生成密文,然后将所有的密文组合在一起,完成加密。

加密条件

1、文明分块长度一般为16,或16的倍数
2、文明长度不满16,或16的倍数,使用填充(具体看算法。默认是\x00)

加密过程

Plaintext: 待加密的数据。
Key : 被一些如AES的对称加密算法使用。
Ciphertext:加密后的数据。
首先将明文进行分块成Plaintext = Plaintext0+Plaintext1+...Plaintextn.

Ciphertext0 = Encrypt(Plaintext0)
Ciphertext1 = Encrypt(Plaintext1)
.....
Ciphertextn = Encrypt(Plaintextn)

Ciphertext = Ciphertext0+Ciphertext1+....+Ciphertextn

解密过程

Plaintext0= Decrypt(Ciphertext0)
Plaintext1= Decrypt(Ciphertext1)
.....
Plaintextn= Decrypt(Ciphertextn)

Plaintext = Plaintext0 + Plaintext1 +.... +Plaintextn

ECB攻击

服务器代码

server.py

from twisted.internet import reactor, protocol
from Crypto.Cipher import AES

PORT = 8001
KEY = "@Ecb_aTTack>esew"
SECRET = "flag{ese@qq!hooray}"

def encrypt_block(key, plaintext):
        encobj = AES.new(key, AES.MODE_ECB)
        return encobj.encrypt(plaintext).encode('hex')

def encrypt(key, ptxt):
    if (len(ptxt) % 16 != 0):
        ptxt = ptxt + "*" * (16 - (len(ptxt) % 16))
    return encrypt_block(key, ptxt)

class Server(protocol.Protocol):

    def connectionMade(self):
        msg = '''
        Welcome to the AES_ECB Cryptography System
        To encrypt, enter "encrypt [plaintext]". Ex, encrypt 123
        1. cliphertext = plaintext + SECRET
        2. this padding is *      
        \r\n>'''
        self.transport.write(msg)

    def dataReceived(self, data):
        try:
            data = data.strip().split(" ")
            print (data)
            if data[0] == "encrypt":
                response = encrypt(KEY,data[1] + SECRET)
            else:
                raise
            self.transport.write(">" + response + "\r\n>")
        except:
            self.transport.write(">Something is wrong here\r\n>")

def main():
    factory = protocol.ServerFactory()
    factory.protocol = Server
    reactor.listenTCP(PORT, factory)
    reactor.run()

if __name__ == '__main__':
    main()

运行
nohub python server.py &

分析

nc连接到服务器
nc xxxx 8001
发现

1、加密模式AES ECB
2、cliphertext = plaintext + SECRET
3、this padding is * (使用填充方式为*)

利用两点

1、填充加密
2、相同的明文获得相同的密文输出

这里输入32字节的a[两个16字节]

encrypt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

结果如下
4f41ee8a60e859118bd54bcfe6a23fc04f41ee8a60e859118bd54bcfe6a23fc04a4c43aa0d91f3a4906047558d0d559024514659c34bbb29dd3567c801bb63ba

发现1-32与33-64位完全一样,如下:
4f41ee8a60e859118bd54bcfe6a23fc0
4f41ee8a60e859118bd54bcfe6a23fc0
相同的文明得到相同的密文。

同时发现后面还有64位,说明SECRET的长度在17-32之间。

枚举SECRET长度

这里进行枚举

输入1个字符进行加密,查看加密后的长度并记住len
输入2个字符进行加密,查看加密后的长度是否大于len,没有继续
输入3个字符进行加密,查看加密后的长度是否大于len,没有继续
....
输入14个字符进行加密,查看加密后的长度是否大于len,发现长度变了。那么padding应该填充13个字符

这里也知道flag长度为19位。

枚举SECRET字符

这里有个公式

当填充为13字节时,加密明文字符串为13+19=32位

当填充为14字节时,加密明文字符串为14+19=32+1位,发现最后一位是flag的最后一位加密[后面15位是填充]。如果我们输入的16位中,第一个是字符,其余15个填充,当这个字符与flag最后一个字符相同时,他们的密文也相同,可以爆破出flag中的最后一位[这里我们需要多输入16个字节构造爆破]。当填充为16+14字节时,加密明文字符串为16+14+19=16+32+1位,可以爆破出flag中的最后一位。

当填充为16+15字节时,加密明文字符串为16+15+19=16+32+2位,最后两位是flag的最后两位加密[后面14位是填充],因为上面知道最后一位了[这里的"}"],构造第一个是字符+"}",其余14个填充。可以爆破出倒数第二位。

这样可以一直枚举出所有flag字符

exp

import socket
import re

host = '172.10.22.70'
port = 8001
def Tostr(st):
    return st.encode(encoding='UTF8')

def connect():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    return s

def getCliphertext(data):
    p1 = r"(>)(.*?)(\r\n)"
    pattern1 = re.compile(p1)
    data = pattern1.findall(data)[0][1]
    return data

def get_pad_len(s):
    s.recv(1024)
    for i in range(1,16):
        payload1 = "encrypt "+'a'*i
        s.send(Tostr(payload1))
        data = (s.recv(1024)).decode('utf-8')
        data = getCliphertext(data)
        if i==1:
            slen = len(data)
        if len(data)>slen:
            break
    return i-1

def forcerFlag(s,slen):
    padd = 'a'*(slen+1)
    plaintext = ""
    print("start...")
    array = "`1234567890-=+qwertyuiop[]asdfghjkl;'zxcvbnm,./?<>!@#$%^&*()QWERTYUIOP{}ASDFGHJKLZXCVBNM:"
    for i in range(19):
        for ch in array:
            payload2 = "encrypt "+ch+plaintext+'*'*15+padd
            s.send(Tostr(payload2))
            data = (s.recv(1024)).decode('utf-8')
            data = getCliphertext(data)
            bp = data[:32]
            sec = data[96:128]
            if bp==sec:
                plaintext = ch +plaintext
                print(plaintext)
                break
    return plaintext

def exp():
    s = connect()
    slen = get_pad_len(s)
    plaintext = forcerFlag(s,slen)
    print(plaintext)

if __name__ == '__main__':
    exp()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,107评论 19 139
  • 本文主要介绍移动端的加解密算法的分类、其优缺点特性及应用,帮助读者由浅入深地了解和选择加解密算法。文中会包含算法的...
    苹果粉阅读 13,963评论 5 29
  • 0x01 目录 常见编码: ASCII编码 Base64/32/16编码 shellcode编码 Quoted-p...
    H0f_9阅读 14,530评论 2 17
  • CTF中那些脑洞大开的编码和加密 0x00 前言 正文开始之前先闲扯几句吧,玩CTF的小伙伴也许会遇到类似这样的问...
    查无此人asdasd阅读 11,360评论 0 19
  • 一、整洁的代码 整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。 读写的花费时间比例超过...
    我爱学习_学习使我快乐阅读 2,981评论 0 0

友情链接更多精彩内容