diff --git a/attack.py b/attack.py
new file mode 100644
index 0000000000000000000000000000000000000000..607a38e4655114b88c20a3a65239d6f1e41c866e
--- /dev/null
+++ b/attack.py
@@ -0,0 +1,75 @@
+import time
+
+from oracle import encrypt, is_padding_ok, BLOCK_SIZE
+
+
+def attack(ciphertext):
+
+    pause_print("Attacking. . .\n. . .", 2000)
+    split_string = lambda x, n: [x[i:i + n] for i in range(0, len(x), n)]
+    blocks = split_string(ciphertext, BLOCK_SIZE)
+
+    guessed_clear = b''
+    for block_n in range(len(blocks) - 1, 0, -1):  # build pair of blocks starting from end of message
+        spliced_ciphertext = blocks[block_n - 1] + blocks[block_n]
+
+        decoded_bytes = b'?' * BLOCK_SIZE  # output of block cipher decoding values
+
+        # GET VALUE OF SECRET BYTE byte
+        for byte in range(BLOCK_SIZE - 1, -1, -1):
+            padding_length = BLOCK_SIZE - byte
+
+            # Build hacked ciphertext tail with values to obtain desired padding
+            hacked_ciphertext_tail = b''
+            for padder_index in range(1, padding_length):
+                hacked_ciphertext_tail += bytearray.fromhex(
+                    '{:02x}'.format(padding_length ^ decoded_bytes[byte + padder_index]))
+
+            for i in range(0, 256):
+                attack_str = bytearray.fromhex('{:02x}'.format((i ^ spliced_ciphertext[byte])))
+                hacked_ciphertext = spliced_ciphertext[:byte] + attack_str + hacked_ciphertext_tail + \
+                                    spliced_ciphertext[byte + padding_length:]
+
+                if is_padding_ok(hacked_ciphertext):
+
+                    test_correctness = hacked_ciphertext[:byte - 1] + bytearray.fromhex(
+                        '{:02x}'.format((1 ^ hacked_ciphertext[byte]))) + hacked_ciphertext[byte:]
+                    if not is_padding_ok(test_correctness):
+                        continue
+
+                    decoded_bytes = decoded_bytes[:byte] + bytearray.fromhex(
+                        '{:02x}'.format(hacked_ciphertext[byte] ^ padding_length)) + decoded_bytes[byte + 1:]
+                    guessed_clear = bytearray.fromhex('{:02x}'.format(i ^ padding_length)) + guessed_clear
+                    # pause_print("Decoded bytes: {:s}".format(str(decoded_bytes)), 100)
+                    pause_print("Guessed bytes: {:s}".format(str(guessed_clear)), 100)
+                    pause_print(". . .", 400)
+                    break
+
+    return guessed_clear[:-guessed_clear[-1]]  # remove padding!
+
+
+def test_the_attack():
+    plaintext = input("Enter plaintext: ").encode("UTF-8")
+
+    pause_print("Plaintext: {:s}".format(str(plaintext)), 500)
+    pause_print("Length: {:d}".format(len(plaintext)), 500)
+    encrypted = encrypt(plaintext)
+    pause_print("Ciphertext: {:s}".format(str(encrypted)), 700)
+    pause_print("Block size: {:d}".format(BLOCK_SIZE))
+    padding_length = BLOCK_SIZE - (len(plaintext) % BLOCK_SIZE)
+    pause_print("Padding should be: {:d} bytes of {:s}"
+                .format(padding_length, str(bytes([padding_length]))), 2000)
+
+    cracked_ct = attack(encrypted)
+    pause_print("Remove {:d} paddings and you got:\n{:s}".format(padding_length, str(cracked_ct)))
+
+    assert (cracked_ct == plaintext)
+
+
+def pause_print(msg, ms=1500):
+    print(msg)
+    time.sleep(ms / 1000)
+
+
+if __name__ == '__main__':
+    test_the_attack()
diff --git a/oracle.py b/oracle.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e7bb239d09c33267f1eb9a594627ca6f528723c
--- /dev/null
+++ b/oracle.py
@@ -0,0 +1,45 @@
+from Crypto.Cipher import AES
+from Crypto import Random
+
+KEY_LENGTH = 16  # AES128
+BLOCK_SIZE = AES.block_size
+
+_random_gen = Random.new()
+_key = _random_gen.read(KEY_LENGTH)
+
+
+def _add_padding(msg):
+    pad_len = BLOCK_SIZE - (len(msg) % BLOCK_SIZE)
+    padding = bytes([pad_len]) * pad_len
+    return msg + padding
+
+
+def _remove_padding(data):
+    pad_len = data[-1]
+
+    if pad_len < 1 or pad_len > BLOCK_SIZE:
+        return None
+    for i in range(1, pad_len):
+        if data[-i - 1] != pad_len:
+            return None
+    return data[:-pad_len]
+
+
+def encrypt(msg):
+    iv = _random_gen.read(AES.block_size)
+    cipher = AES.new(_key, AES.MODE_CBC, iv)
+    return iv + cipher.encrypt(_add_padding(msg))
+
+
+def _decrypt(data):
+    iv = data[:BLOCK_SIZE]
+    cipher = AES.new(_key, AES.MODE_CBC, iv)
+    return _remove_padding(cipher.decrypt(data[BLOCK_SIZE:]))
+
+
+def is_padding_ok(data):
+    return _decrypt(data) is not None
+
+
+if __name__ == '__main__':
+    print("RUN attack.py!!")