Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4c10979
Add beginnings of channel-based MSF named pipe handler/payload
OJ Apr 14, 2016
13c6c3a
Half-baked, debugging impl of named pipe stuff, verbose errors
OJ Apr 18, 2016
c72ff39
Remove debug stuff, add support for ExitOnSession false
OJ Apr 20, 2016
1a8ef35
Fix issue with tracking of server channels
OJ Apr 21, 2016
7678cf9
Fix stupid mistake in the handler resulting in no cleanup
OJ Apr 21, 2016
b31f800
Stager for reverse_named_pipe, bunch of fixes, add info
OJ Apr 21, 2016
021ca2e
Small fixes to the named pipe extension module
OJ Apr 21, 2016
98d7e91
First pass of x64 stager and accompanying stuff
OJ May 2, 2016
f8c788f
Merge branch 'upstream/master' into reverse_named_pipe
OJ May 9, 2016
85eb6e0
Fixed up documentation
OJ May 9, 2016
09a81a8
Initial work on MSF-side migrate stub generation
OJ May 9, 2016
bef18cb
Add http migrate support (x86)
OJ May 9, 2016
ad57d74
Add support for migration via named pipes
OJ May 9, 2016
cd5ba0a
Initial work on x64 migrate rework support
OJ May 9, 2016
309b0dc
Fix up a few small issues with module names
OJ May 9, 2016
177dac6
Format fix, comment fix
OJ May 9, 2016
9e6e623
WriteUUID fixes
OJ May 9, 2016
11f8b34
Reverse transport param flags for pipe host/name
OJ May 9, 2016
7e7a6af
Fix typo in payload spec
OJ May 9, 2016
0ef8232
Fix up migrate stagers to indicate purpose of ESI
OJ May 17, 2016
1025612
Switch to 1.9+ hashes in transport config
OJ May 17, 2016
cea8059
Use patched metasploit-payloads
crmaxx Jan 12, 2017
91b206d
1
crmaxx Mar 28, 2017
03cd578
2
crmaxx Mar 28, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ source 'https://rubygems.org'
# spec.add_runtime_dependency '<name>', [<version requirements>]
gemspec name: 'metasploit-framework'

# Needed for Meterpreter
gem 'metasploit-payloads', github: 'sempervictus/metasploit-payloads', branch: 'feature-update_named_pipes_pr'

# separate from test as simplecov is not run on travis-ci
group :coverage do
# code coverage for tests
Expand Down Expand Up @@ -39,7 +42,7 @@ group :test do
# cucumber extension for testing command line applications, like msfconsole
gem 'aruba'
# cucumber + automatic database cleaning with database_cleaner
gem 'cucumber-rails', :require => false
gem 'cucumber-rails', require: false
gem 'shoulda-matchers'
# Manipulate Time.now in specs
gem 'timecop'
Expand Down
28 changes: 17 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
GIT
remote: git://github.com/sempervictus/metasploit-payloads.git
revision: 084cde42e5fd21ff0cf7a9a28930b7f7c7de87b3
branch: feature-update_named_pipes_pr
specs:
metasploit-payloads (1.2.5)

PATH
remote: .
specs:
Expand All @@ -13,7 +20,6 @@ PATH
metasploit-concern
metasploit-credential (= 1.1.0)
metasploit-model (= 1.1.0)
metasploit-payloads (= 1.1.10)
metasploit_data_models (= 1.3.0)
msgpack
network_interface (~> 0.0.1)
Expand Down Expand Up @@ -62,8 +68,8 @@ GEM
tzinfo (~> 0.3.37)
addressable (2.3.8)
arel (4.0.2)
arel-helpers (2.2.0)
activerecord (>= 3.1.0, < 5)
arel-helpers (2.3.0)
activerecord (>= 3.1.0, < 6)
aruba (0.6.2)
childprocess (>= 0.3.6)
cucumber (>= 1.1.1)
Expand Down Expand Up @@ -110,7 +116,7 @@ GEM
i18n (0.7.0)
jsobfu (0.4.1)
rkelly-remix (= 0.0.6)
json (1.8.3)
json (2.0.2)
mail (2.6.3)
mime-types (>= 1.16, < 3)
metasm (1.0.2)
Expand All @@ -130,7 +136,6 @@ GEM
activemodel (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0)
railties (>= 4.0.9, < 4.1.0)
metasploit-payloads (1.1.10)
metasploit_data_models (1.3.0)
activerecord (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0)
Expand All @@ -145,7 +150,7 @@ GEM
mime-types (2.6.1)
mini_portile2 (2.0.0)
minitest (4.7.5)
msgpack (0.7.4)
msgpack (1.0.2)
multi_json (1.11.2)
multi_test (0.1.2)
multipart-post (2.0.0)
Expand All @@ -159,8 +164,8 @@ GEM
network_interface (~> 0.0)
pcaprub (~> 0.12)
patch_finder (1.0.2)
pcaprub (0.12.1)
pg (0.18.4)
pcaprub (0.12.4)
pg (0.19.0)
pg_array_parser (0.0.9)
postgres_ext (3.0.0)
activerecord (>= 4.0.0)
Expand Down Expand Up @@ -210,7 +215,7 @@ GEM
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
rubyntlm (0.6.0)
rubyntlm (0.6.1)
rubyzip (1.2.0)
sawyer (0.6.0)
addressable (~> 2.3.5)
Expand All @@ -232,7 +237,7 @@ GEM
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
sqlite3 (1.3.11)
sqlite3 (1.3.13)
thor (0.19.1)
thread_safe (0.3.5)
tilt (1.4.1)
Expand All @@ -251,6 +256,7 @@ DEPENDENCIES
factory_girl_rails (~> 4.5.0)
fivemat (~> 1.3.1)
metasploit-framework!
metasploit-payloads!
octokit (~> 4.0)
pry
rake (>= 10.0.0)
Expand All @@ -262,4 +268,4 @@ DEPENDENCIES
yard

BUNDLED WITH
1.12.2
1.12.5
2 changes: 1 addition & 1 deletion lib/msf/base/sessions/meterpreter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def initialize(rstream, opts={})
:ssl => supports_ssl?,
:zlib => supports_zlib?
}

#
# The caller didn't request to skip ssl, so make sure we support it
if not opts[:skip_ssl]
opts.merge!(:skip_ssl => (not supports_ssl?))
Expand Down
6 changes: 3 additions & 3 deletions lib/msf/core/handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,13 @@ def interrupt_wait_for_session
#
def create_session(conn, opts={})
# If there is a parent payload, then use that in preference.
return parent_payload.create_session(conn, opts) if (parent_payload)
return parent_payload.create_session(conn, opts) if parent_payload

# If the payload we merged in with has an associated session factory,
# allocate a new session.
if (self.session)
if (self.session_klass)
begin
s = self.session.new(conn, opts)
s = self.session_klass.new(conn, opts)
rescue ::Exception => e
# We just wanna show and log the error, not trying to swallow it.
print_error("#{e.class} #{e.message}")
Expand Down
185 changes: 185 additions & 0 deletions lib/msf/core/handler/reverse_named_pipe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# -*- coding: binary -*-
require 'thread'
require 'msf/core/post_mixin'

module Msf
module Handler
###
#
# This module implements the reverse named pipe handler. This handler
# requires an existing session via Meterpreter, as it creates a named
# pipe server on the target session and traffic is pivoted through that
# using the channel functionality. This is Windows-only at the moment.
#
###
module ReverseNamedPipe

include Msf::Handler
include Msf::PostMixin

#
# Returns the string representation of the handler type, in this case
# 'reverse_named_pipe'.
#
def self.handler_type
"reverse_named_pipe"
end

#
# Returns the connection-described general handler type, in this case
# 'reverse'.
#
def self.general_handler_type
"reverse"
end

#
# Initializes the reverse handler and ads the options that are required
# for reverse named pipe payloads.
#
def initialize(info = {})
super

register_options([
OptString.new('PIPENAME', [true, 'Name of the pipe to listen on', 'msf-pipe']),
OptString.new('PIPEHOST', [true, 'Host of the pipe to connect to', '.'])
], Msf::Handler::ReverseNamedPipe)

self.conn_threads = []
end

#
# Closes the listener socket if one was created.
#
def cleanup_handler
stop_handler

# Kill any remaining handle_connection threads that might
# be hanging around
conn_threads.each do |thr|
begin
thr.kill
rescue
nil
end
end
end

# A string suitable for displaying to the user
#
# @return [String]
def human_name
"reverse named pipe"
end

#
# Starts monitoring for an inbound connection.
#
def start_handler
queue = ::Queue.new

# The 'listen' option says "behave like a server".
# The 'repeat' option tells the target to create another named pipe
# handle when a new client is established so that it operates like
# a typical server. Named pipes are a bit awful in this regard.
# So we use the 'ExitOnSession' functionality to tell the target
# whether or not to do "one-shot" or "keep going".
self.server_pipe = session.net.named_pipe.create({
listen: true,
name: datastore['PIPENAME'],
host: datastore['PIPEHOST'],
repeat: datastore['ExitOnSession'] == false
})

server_pipe = self.server_pipe

self.listener_thread = framework.threads.spawn(listener_name, false, queue) { |lqueue|
loop do
# Accept a client connection
begin
channel = server_pipe.accept
if channel
self.pending_connections += 1
lqueue.push(channel)
end
rescue StandardError => e
wlog [
"#{listener_name}: Exception raised during listener accept: #{e.class}",
"#{$ERROR_INFO}",
"#{$ERROR_POSITION.join("\n")}"
].join("\n")
end
end
}

self.handler_thread = framework.threads.spawn(worker_name, false, queue) { |cqueue|
loop do
begin
channel = cqueue.pop

unless channel
elog("#{worker_name}: Queue returned an empty result, exiting...")
end

# Timeout and datastore options need to be passed through to the channel.
# We indicate that we want to skip SSL because that isn't suppored (or
# needed?) over the named pipe comms.
opts = {
datastore: datastore,
channel: channel,
skip_ssl: true,
expiration: datastore['SessionExpirationTimeout'].to_i,
comm_timeout: datastore['SessionCommunicationTimeout'].to_i,
retry_total: datastore['SessionRetryTotal'].to_i,
retry_wait: datastore['SessionRetryWait'].to_i
}

# pass this right through to the handler, the channel should "just work"
handle_connection(channel.lsock, opts)
rescue StandardError
elog("Exception raised from handle_connection: #{$ERROR_INFO.class}: #{$ERROR_INFO}\n\n#{$ERROR_POSITION.join("\n")}")
end
end
}
end

#
# Stops monitoring for an inbound connection.
#
def stop_handler
# Terminate the listener thread
listener_thread.kill if listener_thread && listener_thread.alive? == true

# Terminate the handler thread
handler_thread.kill if handler_thread && handler_thread.alive? == true

if server_pipe
begin
server_pipe.close
rescue IOError
# Ignore if it's listening on a dead session
dlog("IOError closing pipe listener; listening on dead session?", LEV_1)
end
end
end

protected

def listener_name
@listener_name |= "ReverseNamedPipeHandlerListener-#{datastore['PIPENAME']}-#{datastore['SESSION']}"
@listener_name
end

def worker_name
@worker_name |= "ReverseNamedPipeHandlerWorker-#{datastore['PIPENAME']}-#{datastore['SESSION']}"
@worker_name
end

attr_accessor :server_pipe # :nodoc:
attr_accessor :listener_thread # :nodoc:
attr_accessor :handler_thread # :nodoc:
attr_accessor :conn_threads # :nodoc:
end
end
end

2 changes: 1 addition & 1 deletion lib/msf/core/payload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def handler_klass
# Returns the session class that is associated with this payload and will
# be used to create a session as necessary.
#
def session
def session_klass
return module_info['Session']
end

Expand Down
8 changes: 3 additions & 5 deletions lib/msf/core/payload/stager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,7 @@ def generate_stage(opts={})
def handle_connection(conn, opts={})
# If the stage should be sent over the client connection that is
# established (which is the default), then go ahead and transmit it.
if (stage_over_connection?)
opts = {}

if stage_over_connection?
if respond_to? :include_send_uuid
if include_send_uuid
uuid_raw = conn.get_once(16, 1)
Expand Down Expand Up @@ -200,8 +198,8 @@ def handle_connection(conn, opts={})
# The connection should always have a peerhost (even if it's a
# tunnel), but if it doesn't, erroring out here means losing the
# session, so make sure it does, just to be safe.
if conn.respond_to? :peerhost
sending_msg << " to #{conn.peerhost}"
if conn.respond_to?(:peerhost)
#sending_msg << " to #{conn.peerhost}"
end
print_status(sending_msg)

Expand Down
Loading