Skip to content

Commit 8602fbe

Browse files
committed
clean history
1 parent f244728 commit 8602fbe

11 files changed

+537
-41
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.pyc
2+
venv/

README.md

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Some code for learning Padding Oracle Attack
2+
There is a exploitable backend and an exploit script
3+
4+
# Setup
5+
```bash
6+
git clone https://gitlab.com/kmille/padding-oracle.git
7+
python -m virtualenv -p python2 venv
8+
source venv/bin/activate
9+
cd padding-oracle
10+
pip install -r requirements.txt
11+
12+
# Run the backend
13+
source venv/bin/activate
14+
gunicorn --bind 127.0.0.1:5000 backend:app
15+
16+
# Run the backend script in a new tab
17+
source venv/bin/activate
18+
cd padding-oracle
19+
python exploit_backend.py # for more debug output disable some comments in oracle.Oracle.solve_oracle
20+
```
21+
22+
# Example Output
23+
<pre>
24+
Get the encrypted message
25+
IV: 7468697373686f756c646265726e646d
26+
Cipher: 4eff7c78220e0a1d63439eb7707c2583d1acf7be521e2cdb7dcdd77e22676481
27+
Let the backend decrypt the message for testing
28+
Die ist ein beta Test
29+
Ciphertext in hex(64): 4eff7c78220e0a1d63439eb7707c2583d1acf7be521e2cdb7dcdd77e22676481
30+
Ciphertext Blocks in hex: [u'4eff7c78220e0a1d63439eb7707c2583', u'd1acf7be521e2cdb7dcdd77e22676481']
31+
Block counter: 1
32+
Processing block #1: d1acf7be521e2cdb7dcdd77e22676481
33+
Round counter: 0
34+
Got no Padding Error for guess 0x89
35+
Got intermediate byte #16: 88
36+
All intermediates for this block: 88
37+
Round counter: 1
38+
Got no Padding Error for guess 0x2c
39+
Got intermediate byte #15: 2e
40+
All intermediates for this block: 882e
41+
Round counter: 2
42+
Got no Padding Error for guess 0x74
43+
Got intermediate byte #14: 77
44+
All intermediates for this block: 882e77
45+
Round counter: 3
46+
Got no Padding Error for guess 0x7f
47+
Got intermediate byte #13: 7b
48+
All intermediates for this block: 882e777b
49+
Round counter: 4
50+
Got no Padding Error for guess 0xb9
51+
Got intermediate byte #12: bc
52+
All intermediates for this block: 882e777bbc
53+
Round counter: 5
54+
Got no Padding Error for guess 0x93
55+
Got intermediate byte #11: 95
56+
All intermediates for this block: 882e777bbc95
57+
Round counter: 6
58+
Got no Padding Error for guess 0x4f
59+
Got intermediate byte #10: 48
60+
All intermediates for this block: 882e777bbc9548
61+
Round counter: 7
62+
Got no Padding Error for guess 0x60
63+
Got intermediate byte #9: 68
64+
All intermediates for this block: 882e777bbc954868
65+
Round counter: 8
66+
Got no Padding Error for guess 0x1f
67+
Got intermediate byte #8: 16
68+
All intermediates for this block: 882e777bbc95486816
69+
Round counter: 9
70+
Got no Padding Error for guess 0xb
71+
Got intermediate byte #7: 01
72+
All intermediates for this block: 882e777bbc9548681601
73+
Round counter: 10
74+
Got no Padding Error for guess 0xe
75+
Got intermediate byte #6: 05
76+
All intermediates for this block: 882e777bbc954868160105
77+
Round counter: 11
78+
Got no Padding Error for guess 0x5a
79+
Got intermediate byte #5: 56
80+
All intermediates for this block: 882e777bbc95486816010556
81+
Round counter: 12
82+
Got no Padding Error for guess 0x6
83+
Got intermediate byte #4: 0b
84+
All intermediates for this block: 882e777bbc954868160105560b
85+
Round counter: 13
86+
Got no Padding Error for guess 0x17
87+
Got intermediate byte #3: 19
88+
All intermediates for this block: 882e777bbc954868160105560b19
89+
Round counter: 14
90+
Got no Padding Error for guess 0xa4
91+
Got intermediate byte #2: ab
92+
All intermediates for this block: 882e777bbc954868160105560b19ab
93+
Round counter: 15
94+
Got no Padding Error for guess 0x7e
95+
Got intermediate byte #1: 6e
96+
All intermediates for this block: 882e777bbc954868160105560b19ab6e
97+
Done with this round. Now XORING the intermediate bytes with the Cipher text of the previous ciphertext
98+
Cipher before in hex : 4eff7c78220e0a1d63439eb7707c2583
99+
Recovered plain text: [' ', 'T', 'e', 's', 't', '\x0b', '\x0b', '\x0b', '\x0b', '\x0b', '\x0b', '\x0b', '\x0b', '\x0b', '\x0b', '\x0b']
100+
Block counter: 0
101+
Processing block #0: 4eff7c78220e0a1d63439eb7707c2583
102+
Round counter: 0
103+
Got no Padding Error for guess 0xd
104+
Got intermediate byte #16: 0c
105+
All intermediates for this block: 0c
106+
Round counter: 1
107+
Got no Padding Error for guess 0x12
108+
Got intermediate byte #15: 10
109+
All intermediates for this block: 0c10
110+
Round counter: 2
111+
Got no Padding Error for guess 0x8
112+
Got intermediate byte #14: 0b
113+
All intermediates for this block: 0c100b
114+
Round counter: 3
115+
Got no Padding Error for guess 0x14
116+
Got intermediate byte #13: 10
117+
All intermediates for this block: 0c100b10
118+
Round counter: 4
119+
Got no Padding Error for guess 0x40
120+
Got intermediate byte #12: 45
121+
All intermediates for this block: 0c100b1045
122+
Round counter: 5
123+
Got no Padding Error for guess 0xa
124+
Got intermediate byte #11: 0c
125+
All intermediates for this block: 0c100b10450c
126+
Round counter: 6
127+
Got no Padding Error for guess 0xa
128+
Got intermediate byte #10: 0d
129+
All intermediates for this block: 0c100b10450c0d
130+
Round counter: 7
131+
Got no Padding Error for guess 0x1
132+
Got intermediate byte #9: 09
133+
All intermediates for this block: 0c100b10450c0d09
134+
Round counter: 8
135+
Got no Padding Error for guess 0x5c
136+
Got intermediate byte #8: 55
137+
All intermediates for this block: 0c100b10450c0d0955
138+
Round counter: 9
139+
Got no Padding Error for guess 0x11
140+
Got intermediate byte #7: 1b
141+
All intermediates for this block: 0c100b10450c0d09551b
142+
Round counter: 10
143+
Got no Padding Error for guess 0x10
144+
Got intermediate byte #6: 1b
145+
All intermediates for this block: 0c100b10450c0d09551b1b
146+
Round counter: 11
147+
Got no Padding Error for guess 0x16
148+
Got intermediate byte #5: 1a
149+
All intermediates for this block: 0c100b10450c0d09551b1b1a
150+
Round counter: 12
151+
Got no Padding Error for guess 0x5e
152+
Got intermediate byte #4: 53
153+
All intermediates for this block: 0c100b10450c0d09551b1b1a53
154+
Round counter: 13
155+
Got no Padding Error for guess 0x2
156+
Got intermediate byte #3: 0c
157+
All intermediates for this block: 0c100b10450c0d09551b1b1a530c
158+
Round counter: 14
159+
Got no Padding Error for guess 0xe
160+
Got intermediate byte #2: 01
161+
All intermediates for this block: 0c100b10450c0d09551b1b1a530c01
162+
Round counter: 15
163+
Got no Padding Error for guess 0x20
164+
Got intermediate byte #1: 30
165+
All intermediates for this block: 0c100b10450c0d09551b1b1a530c0130
166+
Done with this round. Now XORING the intermediate bytes with the Cipher text of the previous ciphertext
167+
Cipher before in hex : 7468697373686f756c646265726e646d
168+
Recovered plain text: ['D', 'i', 'e', ' ', 'i', 's', 't', ' ', 'e', 'i', 'n', ' ', 'b', 'e', 't', 'a']
169+
Done
170+
Decrypted message: Die ist ein beta Test
171+
</pre>
172+
173+
# Resources
174+
- https://en.wikipedia.org/wiki/Padding_oracle_attack
175+

__init__.py

Whitespace-only changes.

backend.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env python2
2+
from flask import Flask, request, redirect
3+
from binascii import hexlify, unhexlify
4+
from base64 import b64decode, b64encode
5+
import json
6+
from crypto import AESCipher
7+
8+
app = Flask(__name__)
9+
10+
# test if encryption/decryption works. ciphertext will be used as challenge
11+
key = "very strong pw!!"
12+
iv = "thisshouldberndm"
13+
message = "Die ist ein beta Test"
14+
aes = AESCipher(key, iv, 16)
15+
cipher_text = aes.encrypt(message)
16+
assert message == aes.decrypt(cipher_text)
17+
18+
19+
@app.route("/")
20+
def index():
21+
return r"Please go to /cipher to get the cipher text. The content is IV\nCIPHERTEXT"
22+
23+
24+
@app.route("/cipher")
25+
def cipher():
26+
# returns the ciphertext/secret you want to decrypt
27+
# format of IV and ciphertext is hex string seperated with a new line
28+
return "%s\n%s" % (hexlify(iv), hexlify(cipher_text))
29+
30+
31+
@app.route("/decrypt", methods=['POST'])
32+
def decrypt():
33+
# decrypts messages from the client
34+
# cipher_text: ciphertext to decrypt in hex string format
35+
# return: plaintext string (or bytes if plaintext were bytes/something goes wrong
36+
json = request.get_json()
37+
cipher = json['cipher_text']
38+
plain = aes.decrypt(unhexlify(cipher))
39+
return plain
40+
41+
42+
if __name__ == '__main__':
43+
app.run(debug=True)

crypto.py

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from Crypto.Cipher import AES
2+
3+
# for debugging
4+
from binascii import hexlify, unhexlify
5+
from ipdb import set_trace
6+
7+
BLOCKSIZE = None
8+
9+
class AESCipher(object):
10+
11+
def __init__(self, key, iv, block_size):
12+
global BLOCKSIZE
13+
self.key = key
14+
self.iv = iv
15+
self.bs = block_size
16+
BLOCKSIZE = block_size
17+
18+
19+
def encrypt(self, raw):
20+
# raw: string or bytes to encrypt
21+
# return: ciphertext in bytes
22+
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
23+
cipher_text = cipher.encrypt(AESCipher.pad(raw))
24+
print("Plaintext %s %s " % (hexlify(raw)[:32], hexlify(raw)[32:]))
25+
print("Encrypted %s %s " % (hexlify(cipher_text)[:32], hexlify(cipher_text)[32:]))
26+
return cipher_text
27+
28+
29+
def decrypt(self, enc):
30+
# enc: ciphertext in bytes
31+
# return: plaintext as string
32+
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
33+
plain_padded = cipher.decrypt(enc)
34+
print("Encrypted %s %s " % (hexlify(enc)[:32], hexlify(enc)[32:]))
35+
print("Decrypted %s %s " % (hexlify(plain_padded)[:32], hexlify(plain_padded)[32:]))
36+
plain = AESCipher.unpad(plain_padded)
37+
return plain
38+
39+
@staticmethod
40+
def pad(plaintext):
41+
return plaintext + (BLOCKSIZE - len(plaintext) % BLOCKSIZE) * \
42+
chr(BLOCKSIZE - len(plaintext) % BLOCKSIZE)
43+
44+
@staticmethod
45+
def unpad(plaintext):
46+
last_byte = ord(plaintext[-1])
47+
if last_byte > BLOCKSIZE or last_byte == 0:
48+
raise Exception("Padding Exception")
49+
for i in range(last_byte):
50+
if ord(plaintext[-i-1]) != last_byte:
51+
raise Exception("Padding Exception")
52+
return plaintext [:-last_byte]

exploit_backend.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import requests
2+
from binascii import hexlify, unhexlify
3+
from time import sleep
4+
from oracle import Oracle
5+
from ipdb import set_trace
6+
7+
target = "http://localhost:5000%s"
8+
9+
cipher_text = None
10+
iv = None
11+
12+
def get_encrypted_message():
13+
# gets IV and ciphertext from backend and saves them
14+
# format is hex string
15+
print("Get the encrypted message")
16+
global cipher_text, iv
17+
resp = requests.get(target % "/cipher")
18+
iv, cipher_text = resp.text.splitlines()
19+
print("IV: %s\nCipher: %s" % (iv, cipher_text))
20+
21+
22+
def decrypt():
23+
# format of cipher_text is hex string
24+
print("Let the backend decrypt the message for testing")
25+
json = { 'cipher_text': cipher_text }
26+
resp = requests.post(target % "/decrypt", json=json)
27+
print(resp.text)
28+
29+
30+
def oracle(cipher):
31+
# padding oracle client for this challenge
32+
# cipher: ciphertext for the backend in byte format
33+
# returns: True if no Padding Exception occured else False
34+
json = { 'cipher_text': hexlify(cipher) }
35+
resp = requests.post(target % "/decrypt", json=json)
36+
return resp.status_code == 200
37+
38+
39+
def padding_oracle_attack():
40+
Oracle(unhexlify(cipher_text), 16, oracle, debug=False, iv=unhexlify(iv))
41+
42+
43+
get_encrypted_message()
44+
decrypt()
45+
padding_oracle_attack()

0 commit comments

Comments
 (0)