AES-CBC算法中Padding Oracle Attack的实现

Author Avatar
Timerain
发表:2024-04-15 22:29:00
修改:2024-04-15 22:35:59

Padding Oracle Attack攻击的实现

Padding Oracle Attack是较早的一种漏洞利用方式,在2011年的Pwnie Rewards中被评为”最具有价值的服务器漏洞“。该漏洞主要是由于设计使用的场景不当,导致可以利用密码算法通过”旁路攻击“被破解,并不是对算法的破解。

利用该漏洞可以破解出密文的明文以及将明文加密成密文,该漏洞存在条件如下:

1. 攻击者能够获取到密文(基于分组密码模式),以及IV向量(通常附带在密文前面,初始化向量)

2. 攻击者能够修改密文触发解密过程,解密成功和解密失败存在差异性

例如请求[http://www.example.com/decrypt.jsp?data=0000000000000000EFC2807233F9D7C097116BB33E813C5E](https://links.jianshu.com/go?to=http%3A%2F%2Fwww.example.com%2Fdecrypt.jsp%3Fdata%3D0000000000000000EFC2807233F9D7C097116BB33E813C5E),当攻击者在篡改data值时会有以下不同的响应:

1. 如果data值没有被篡改,则解密成功,并且业务校验成功,响应200

2. 如果data值被篡改,服务端无法完成解密,解密校验失败,则响应500

3. 如果data值被篡改,但是服务端解密成功,但业务逻辑校验失败,则可能返回200或302等响应码,而不是响应500

攻击者只需要关注解密成功和解密失败的响应即可(第三种属于解密成功的响应),即可完成攻击。

对于安洵杯2023的密码学题的分析:

# -*- coding:utf-8 -*-

from Crypto.Util.number import isPrime, long_to_bytes, getStrongPrime, bytes_to_long

from Crypto.Cipher import AES

from Crypto.Util.Padding import pad

import os

import binascii

import random

import string

import hashlib

import socketserver

FLAG = "{0P@d4Ttk}"

KEY = b'your_key_key_key'

# IV = b'key_key_key_your'

IV = b'D0g3{U_Get_Fl4g}'

def cbc_decrypt(c, iv):

aes = AES.new(KEY, AES.MODE_CBC, iv=iv)

return aes.decrypt(c)

def encrypt():

plain_text = ''.join([random.choice(string.ascii_letters) for _ in range(2)]) + FLAG

aes = AES.new(KEY, AES.MODE_CBC, iv=IV)

plain_text = pad(plain_text.encode(), AES.block_size)

# print(plain_text)

cipher = aes.encrypt(plain_text)

# print(cipher)

# print(cipher.hex())

# print(IV.hex() + cipher.hex())

# print(len(cipher))

return IV.hex() + cipher.hex()

def asserts(pt: bytes):

num = pt[-1]

if len(pt) == 16:

result = pt[::-1]

count = 0

for i in result:

if i == num:

count += 1

else:

break

if count == num:

return True

else:

return False

else:

return False

def decrypt(c):

iv = c[:32]

cipher = c[32:]

plain_text = cbc_decrypt(binascii.unhexlify(cipher), binascii.unhexlify(iv))

if asserts(plain_text):

return True

else:

return False

class MyServer(socketserver.BaseRequestHandler):

def proof(self):

random.seed(os.urandom(8))

random_str = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(20)])

str_sha256 = hashlib.sha256(random_str.encode()).hexdigest()

self.request.sendall(('SHA256(XXXX + %s):%s\n' % (random_str[4:], str_sha256)).encode())

self.request.sendall('Give Me XXXX:\n'.encode())

XXXX = self.request.recv(2048).strip()

if hashlib.sha256((XXXX + random_str[4:].encode())).hexdigest() != str_sha256:

return False

return True

def handle(self):

if not self.proof():

self.request.sendall(b'Error Hash!')

return

cipher = encrypt()

self.request.sendall('Welcome to AES System, please choose the following options:\n1. encrypt the flag\n2. decrypt the flag\n'.encode())

# self.request.sendall(''.encode())

# self.request.sendall(''.encode())

n = 0

while n < 65536:

options = self.request.recv(512).strip().decode()

if options == '1':

self.request.sendall(('This is your flag: %s\n' % cipher).encode())

elif options == '2':

self.request.sendall('Please enter ciphertext:\n'.encode())

recv_cipher = self.request.recv(512).strip().decode()

if decrypt(recv_cipher):

self.request.sendall('True\n'.encode())

else:

self.request.sendall('False\n'.encode())

else:

self.request.sendall('Input wrong! Please re-enter\n'.encode())

n += 1

return

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):

pass

if name == '__main__':

sever = socketserver.ThreadingTCPServer(('0.0.0.0', 10010), MyServer)

ThreadedTCPServer.allow_reuse_address = True

ThreadedTCPServer.allow_reuse_port = True

sever.serve_forever()

评论