Metasploit – exploring a Windows kernel exploit module

Now that we have a little background, we're going to watch the attack in action with Metasploit. The exploit module specific to this vulnerability is called exploit/windows/local/ms14_058_track_popup_menu (recall that MS14-058 is the Microsoft Security Bulletin designation for this flaw). Notice that this exploit falls under the local subcategory? The nature of this flaw requires that we are able to execute a program as a privileged user – this is a local attack, as opposed to a remote attack. Sometimes you'll see security publications discuss local exploits with phrases like the risk is limited by the fact that the attacker must be local to the machine. The pen tester in you should be chuckling at this point, because you know that the context of distinguishing local from remote essentially removes the human factor sitting at the keyboard. If we can convince the user to take some action, we're as good as local. These local attacks can become remotely controlled with just a little finesse.

Before we get to the fun stuff, let's examine the Metasploit module in detail so we understand how it works. As always, we need to take a look at the include lines so we can review the functionality that's being imported into this module:

require 'msf/core/post/windows/reflective_dll_injection'
class MetasploitModule < Msf::Exploit::Local
Rank = NormalRanking
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process
include Msf::Post::Windows::FileInfo
include Msf::Post::Windows::ReflectiveDLLInjection

So, we have several Windows post-exploit modules loaded here: File, Priv, Process, FileInfo, and ReflectiveDLLInjection. I won't bog you down with dumping the code from all five post modules here, but you should always consider the proper review of the included modules as a requirement. Recall that the include statement makes those modules mixins whose parameters are directly referenceable within this parent module.

Back to the parent module; we're going to skip over the first two defined methods: initialize(info={}) and check. You will remember that the info initialization provides useful information for the user, but it isn't necessary for the module to function. The most practical purpose of this is making keywords available to the search function within msfconsole. The check method is also not strictly necessary, but it makes this module available to the compatibility checking functionality of Metasploit. When a target is selected, you can load an exploit and check whether the target is probably vulnerable. Personally, I find the check functionality to be nifty and potentially a time-saver, but in general I would never recommend relying on it.

Now, at long last: the exploit method. Please note that the method starts with some error checking that we're skipping over; it makes sure we aren't already SYSTEM (just in case you're still racing after crossing the finish line!) and it checks that the session host architecture and the options-defined architecture match:

def exploit
print_status('Launching notepad to host the exploit...')
notepad_process = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true})
begin
process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)
print_good("Process #{process.pid} launched.")

  rescue Rex::Post::Meterpreter::RequestError
print_error('Operation failed. Trying to elevate the current process...')
process = client.sys.process.open
end

The method starts with an attempt to launch Notepad. Note that the {'Hidden' => true} argument is passed to execute. This ensures that Notepad will execute but the friendly editor window won't actually appear for the user (that would certainly tip off the user that something is wrong). We then handle the successful launch of Notepad and nab the process ID for the next stage of the exploit; alternatively, rescue comes to the rescue to handle the failure to launch Notepad and instead nabs the currently open process for the next stage.

As a review, DLLs are the Windows implementation of the shared library model. They are executable code that can be shared by programs. For all intents and purposes, they should be regarded as executables. The main difference from an EXE is that DLLs require an entry point that is provided by a running program. From a security perspective, DLLs are very dangerous because they are loaded in the memory space of the calling process, which means they have the same permissions as the running process. If we can inject a malicious DLL into a privileged process, this is pretty much game over.

And now, our big finale: reflective DLL injection. DLLs are meant to be loaded into the memory space of a process, so DLL injection is simply forcing this with our chosen DLL. However, since a DLL is an independent file in its own right, DLL injection typically involves pulling the DLL's code off of disk. Reflective DLL injection allows us to source the code straight out of memory. Let's take a look at what our module does with reflective DLL injection in the context of our Win32k.sys exploit:

    print_status("Reflectively injecting the exploit DLL into #{process.pid}...")
if target.arch.first == ARCH_X86
dll_file_name = 'cve-2014-4113.x86.dll'
else
dll_file_name = 'cve-2014-4113.x64.dll'
end
library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2014-4113', dll_file_name)
library_path = ::File.expand_path(library_path)
print_status("Injecting exploit into #{process.pid}...")
exploit_mem, offset = inject_dll_into_process(process, library_path)
print_status("Exploit injected. Injecting payload into #{process.pid}...")
payload_mem = inject_into_process(process, payload.encoded)
print_status('Payload injected. Executing exploit...')
process.thread.create(exploit_mem + offset, payload_mem)
print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
end

Let's examine this step by step and skip over the status printouts:

  • First, the if...else, target.arch.first == ARCH_X86 statement. This is self-explanatory: the module is pulling an exploit DLL from the Metasploit DataExploits folder, and this check allows for the architecture to be targeted correctly.
  • library_path allows the module to find and load the exploit DLL from the attacker's local disk. I hope the creative side has kicked in and you just realized that you could modify this module to point at any DLL you like.
  • exploit_mem, offset = inject_dll_into_process() is the first slap across the target's face. Note that inject_dll_into_process() is defined in the included ReflectiveDLLInjection module. This particular method takes the target process and the DLL's local path as arguments and then returns an array with two values: the allocated memory address and the offset. Our module takes these returned values and stores them as exploit_mem and offset, respectively.
  • payload_mem = inject_into_process() is the second slap across the target's face. payload.encoded is our shellcode (encoded as needed). This method returns only one value: the location of the shellcode in the target process's memory. So as you can see, at this point in our attack, payload_mem is now the location in our target's memory where our shellcode begins.
  • If those first two instance methods for DLL injection were the slaps in the face, then process.thread.create(exploit_mem + offset, payload_mem) is our coup de grace. We're passing two parameters to process.thread.create(): first, exploit_mem with our offset added to it, then the location of our shellcode in memory, payload_mem.

So, why are we injecting a DLL into a process? The vulnerable kernel-mode driver, Win32k.sys, has more than 600 entry points that allow its functionality to be accessed; it handles a lot of useful tasks. As we covered in this chapter, Win32k.sys is responsible for window management. Win32k.sys represents a necessary evil of this operating system design: the blend of its needed power and accessibility to user-mode programs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.125.171