Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Pingback Payloads #12129

Merged
merged 80 commits into from Jul 30, 2019
Merged
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
47ee86a
WIP: REST API for async-callbacks
asoto-r7 Mar 19, 2019
a1b5136
Bring pingback-payload changes into public framework
asoto-r7 Mar 19, 2019
353e8e6
WIP: Remove PUT and DELETE endpoints
asoto-r7 Mar 19, 2019
67d3bf5
WIP: REST API for async-callbacks, added UUID search
asoto-r7 Mar 19, 2019
542bf00
REST API for async-callbacks, removed array datatypes, cleaned up Swa…
asoto-r7 Mar 19, 2019
f0f45d9
Maybe stage the new files, too
bwatters-r7 Apr 8, 2019
e798a0d
Add pingback changes
bwatters-r7 Apr 10, 2019
4ca6c35
Add new files
bwatters-r7 Apr 10, 2019
cc69fa2
Let's try it as a single this time....
bwatters-r7 Apr 16, 2019
9805a14
Add support for pingback as a single and session...
bwatters-r7 Apr 22, 2019
d626e56
Updated to have a handler
bwatters-r7 Apr 23, 2019
5202a85
Use nonvolitile register for the counter
bwatters-r7 Apr 23, 2019
1b64b9f
Fix odd edge case converting binary to hex string
bwatters-r7 Apr 23, 2019
3b54fb3
Record UUID upon reverse_tcp_pingback generation and callback
asoto-r7 Apr 30, 2019
6d6b339
Record UUID upon pingback_reverse_tcp generation and callback
asoto-r7 Apr 30, 2019
8c6f2d9
Enable database support in msfvenom to allow for saving UUIDs
asoto-r7 Apr 30, 2019
f4fa70d
Add error handling for users without a database configured
asoto-r7 Apr 30, 2019
247f246
Linux pingback payloads
asoto-r7 May 13, 2019
94c6ee3
Python pingback payload (bind only)
asoto-r7 May 13, 2019
8991392
Remove 'workspace' option from Mdm::Payload.create
asoto-r7 May 16, 2019
1d45c3a
python pingback_bind_tcp: send UUID as raw bytes instead of ASCII
asoto-r7 May 16, 2019
4241d33
Python pingback payload (reverse only)
asoto-r7 May 16, 2019
be011da
Ruby pingback payload (bind and reverse)
asoto-r7 May 17, 2019
e51e271
Remove extra stuff that was part of the staged attempt at pingback.
bwatters-r7 May 20, 2019
c866e0a
First swing at x86 windows reverse_tcp pingback
bwatters-r7 May 21, 2019
9989c73
That's better.....
bwatters-r7 May 22, 2019
58f3a06
cmd/unix/pingback_reverse and cmd/unix/pingback_bind
asoto-r7 May 26, 2019
79c45a6
Clean up `require`'s and calculate CachedSize
asoto-r7 May 26, 2019
92fa8f4
Clean up `require`s and `include`s
asoto-r7 May 26, 2019
cb270cd
WIP: Adding default pingback payload to parent check method
asoto-r7 May 26, 2019
e1e75d8
Code deduplication
bwatters-r7 May 28, 2019
2a242d9
Add the new file
bwatters-r7 May 29, 2019
f7f7e96
Hold off on venom changes for a new PR
bwatters-r7 May 30, 2019
08a765d
Shut up, nmsftidy.... I hope
bwatters-r7 May 30, 2019
3e76509
Fix some spacing
bwatters-r7 May 30, 2019
39f193e
Stupid last trailing space
bwatters-r7 May 30, 2019
33513bd
Undo changes to windows/bind_tcp
bwatters-r7 May 31, 2019
7778ada
Remove workspace reference in async_callback database table
asoto-r7 Jun 4, 2019
8f0aaa7
cmd/unix/pingback_* payloads now use 'printf' in place of 'echo'
asoto-r7 Jun 4, 2019
374b56d
Should not have changed reverse_tcp.rb
bwatters-r7 Jun 4, 2019
5344746
Remove a left-over 'pry' debugger invocation
asoto-r7 Jun 4, 2019
398a5dc
Reset send_uuid because it should not have been changed
bwatters-r7 Jun 10, 2019
a12f9a5
Revert f162822
asoto-r7 Jun 11, 2019
949b356
Update the session to die after callback
bwatters-r7 Jun 13, 2019
f098a83
Stupid pry...
bwatters-r7 Jun 13, 2019
88213f1
Pingback: Addressed some comments and suggestions
asoto-r7 Jun 26, 2019
9b6d458
cmd/unix/pingback_bind: Add resiliency to netcat, per wvu's suggestion
asoto-r7 Jun 27, 2019
72977e6
pingback: Removing seemingly unnecessary 'generate_raw' method
asoto-r7 Jun 27, 2019
26257fa
Updated json_to_mdm_object() calls, removing third parameter
asoto-r7 Jun 28, 2019
14039b1
Correctly fixed json_to_mdm_object (thanks @mkienow-r7 for the catch)!
asoto-r7 Jun 28, 2019
80dbef2
Follow acammack's guidance for excluding filedropped exploits
bwatters-r7 Jul 11, 2019
310533f
First stab at filtering payloads that require cleanup
bwatters-r7 Jul 17, 2019
6ae3f97
Maybe include the super pingback type in the payloads?
bwatters-r7 Jul 17, 2019
7c2d214
Clean up debugging, move options to one place and delete superflous file
bwatters-r7 Jul 18, 2019
e1ba4bd
delete extra file
bwatters-r7 Jul 18, 2019
2aadd63
Fix printing in session handler while I'm at it...
bwatters-r7 Jul 18, 2019
b0d602e
Added autoload entries for AsyncCallback
asoto-r7 Jul 19, 2019
af28534
Copy-pasta badness
asoto-r7 Jul 19, 2019
68f7ece
Removed superfluous assignment and populated datastore with pingback_…
bwatters-r7 Jul 22, 2019
3536e8a
Remove extra assignments elsewhere
bwatters-r7 Jul 22, 2019
7a8090c
Fix variable name
bwatters-r7 Jul 22, 2019
9ed8aa9
update the read/write for pingback data
bwatters-r7 Jul 24, 2019
8af6cad
fix copy/pasta error on payload data read
bwatters-r7 Jul 24, 2019
cd4ba13
Unpry
bwatters-r7 Jul 24, 2019
4a59c1b
Other pry...
bwatters-r7 Jul 24, 2019
e710c93
Remove async callback stuff for later work and change db checks
bwatters-r7 Jul 25, 2019
93f8d94
Changes to venom to handle pingbacks and really delete extra files, t…
bwatters-r7 Jul 25, 2019
463c147
fix method check in metadata updates
busterb Jul 26, 2019
2f804fa
Rubocop and @acammack cleanup suggestions
bwatters-r7 Jul 26, 2019
79b7bbd
Update payload cache size and fix import bug
bwatters-r7 Jul 26, 2019
cec29c6
More fixes for syntax
bwatters-r7 Jul 26, 2019
bd6a0c8
Remove workspace reqs from remote db payloads
acammack-r7 Jul 26, 2019
6bf10e1
Fixups for syntax
bwatters-r7 Jul 29, 2019
d6dc397
Fix bugs introduced by syntax changes.
bwatters-r7 Jul 29, 2019
05ffa6e
More updates, optimizations, and style fixes
bwatters-r7 Jul 29, 2019
79e17d0
Remove unsupported options
bwatters-r7 Jul 30, 2019
3cb1b45
Golf Python payload variable names
acammack-r7 Jul 30, 2019
e6ea0c9
Use binascii for Python pingback UUID encoding
acammack-r7 Jul 30, 2019
23ea772
Golf Ruby pingback payload syntax
acammack-r7 Jul 30, 2019
517d32b
Update payload cache sizes
acammack-r7 Jul 30, 2019
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -41,4 +41,4 @@ module DataProxyAutoLoader
include VulnAttemptDataProxy
include MsfDataProxy
include PayloadDataProxy
end
end
@@ -3,7 +3,6 @@ module PayloadDataProxy
def payloads(opts)
begin
self.data_service_operation do |data_service|
add_opts_workspace(opts)
data_service.payloads(opts)
end
rescue => e
@@ -14,7 +13,6 @@ def payloads(opts)
def create_payload(opts)
begin
self.data_service_operation do |data_service|
add_opts_workspace(opts)
data_service.create_payload(opts)
end
rescue => e
@@ -41,5 +41,4 @@ module DataServiceAutoLoader
include RemoteMsfDataService
include RemoteDbImportDataService
include RemotePayloadDataService

end
@@ -14,6 +14,7 @@ module Sessions
# with the server instance both at an API level as well as at a console level.
#
###

class Meterpreter < Rex::Post::Meterpreter::Client

include Msf::Session
@@ -0,0 +1,98 @@
# -*- coding: binary -*-
require 'msf/base'

module Msf
module Sessions

###
#
# This class provides the ability to receive a pingback UUID
#
###
class Pingback

#
# This interface supports basic interaction.
#
include Msf::Session
include Msf::Session::Basic

attr_accessor :arch
attr_accessor :platform
attr_accessor :uuid_string

#
# Returns the type of session.
#
def self.type
"pingback"
end

def initialize(rstream, opts = {})
super
self.platform ||= ""
self.arch ||= ""
datastore = opts[:datastore]
end

def self.create_session(rstream, opts = {})
Msf::Sessions::Pingback.new(rstream, opts)
end

def process_autoruns(datastore)
uuid_read
cleanup
end

def cleanup
if rstream

This comment has been minimized.

Copy link
@acammack-r7

acammack-r7 Jul 26, 2019

Contributor

Given the rescue and assignment that happen in here, this guard doesn't do any thing.

This comment has been minimized.

This comment has been minimized.

Copy link
@acammack-r7

acammack-r7 Jul 27, 2019

Contributor

I definitely wouldn't want to generate unnecessary exceptions in a tight loop or hot code path. This isn't in one of those, though, and in my testing this method doesn't get called with a falsey rstream value (not that it never could be called that way or should require a non-null rstream as a precondition). This guard is optimizing for the exceptional case and thus is overly defensive. It doesn't need to be one way or the other, I was just pointing out that it was extra code that could be safely removed.

This comment has been minimized.

Copy link
@busterb

busterb Jul 29, 2019

Author Member

I'll note that this method is just a clone of the same from lib/msf/base/sessions/command_shell.rb minus the shell interaction bits. While I can appreciate further minimization, this may not be the only thing we ever use pingback payloads for (there may be other work we do on cleanup when there are other protocols implemented). It may be necessary later to provide some sort of interactive cleanup trigger, for instance.

# this is also a best-effort
rstream.close rescue nil
rstream = nil
end
end

def uuid_read
uuid_raw = rstream.get_once(16, 1)
return nil unless uuid_raw
self.uuid_string = uuid_raw.each_byte.map { |b| "%02x" % b.to_i() }.join
print_status("Incoming UUID = #{uuid_string}")
if framework.db.active
begin
payload = framework.db.payloads(uuid: uuid_string).first
if payload.nil?
print_warning("Provided UUID (#{uuid_string}) was not found in database!")
else
print_good("UUID identified (#{uuid_string})")
end
rescue ActiveRecord::ConnectionNotEstablished
print_status("WARNING: UUID verification and logging is not available, because the database is not active.")
rescue => e
# TODO: Can we have a more specific exception handler?
# Test: what if we send no bytes back? What if we send less than 16 bytes? Or more than?
elog("Can't get original UUID")
elog("Exception Class: #{e.class.name}")
elog("Exception Message: #{e.message}")
elog("Exception Backtrace: #{e.backtrace}")
end
else
print_warning("WARNING: UUID verification and logging is not available, because the database is not active.")
end
end

#
# Returns the session description.
#
def desc
"Pingback"
end

#
# Calls the class method
#
def type
self.class.type
end
end
end
end
@@ -8,7 +8,6 @@ def create_payload(opts)
end
end

wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
Mdm::Payload.create!(opts)
end
end
@@ -20,7 +19,7 @@ def payloads(opts)
else
# Check the database for a matching UUID, returning an empty array if no results are found
begin
return Array.wrap(Mdm::Payload.find(uuid: opts[:uuid]))
return Array.wrap(Mdm::Payload.where(uuid: opts[:uuid]))
rescue ActiveRecord::RecordNotFound
return []
end
@@ -30,9 +29,6 @@ def payloads(opts)

def update_payload(opts)
::ActiveRecord::Base.connection_pool.with_connection do
wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework, false)
opts[:workspace] = wspace if wspace

id = opts.delete(:id)
Mdm::Payload.update(id, opts)
end
@@ -273,6 +273,8 @@ def self.mixins
return mixins
end

attr_accessor :needs_cleanup

#
# Creates an instance of the exploit module. Mad skillz.
#
@@ -752,6 +754,10 @@ def is_payload_compatible?(name)
# what not?
return false if !compatible?(pi)

if self.needs_cleanup && !pi.can_cleanup
return false
end

# If the payload is privileged but the exploit does not give
# privileged access, then fail it.
return false if !self.privileged && pi.privileged
@@ -6,6 +6,7 @@ module Exploit::FileDropper
def initialize(info = {})
super

self.needs_cleanup = true
@dropped_files = []
@dropped_dirs = []

@@ -81,6 +81,8 @@ def initialize(module_instance, obj_hash = nil)
@path = module_instance.file_path
@mod_time = ::File.mtime(@path) rescue Time.now
@ref_name = module_instance.refname
@needs_cleanup = module_instance.respond_to?(:needs_cleanup) && module_instance.needs_cleanup

if module_instance.respond_to?(:autofilter_ports)
@autofilter_ports = module_instance.autofilter_ports
end
@@ -134,7 +136,8 @@ def to_json(*args)
'check' => @check,
'post_auth' => @post_auth,
'default_credential' => @default_credential,
'notes' => @notes
'notes' => @notes,
'needs_cleanup' => @needs_cleanup
}.to_json(*args)
end

@@ -183,6 +186,8 @@ def init_from_hash(obj_hash)
@post_auth = obj_hash['post_auth']
@default_credential = obj_hash['default_credential']
@notes = obj_hash['notes'].nil? ? {} : obj_hash['notes']
@needs_cleanup = obj_hash['needs_cleanup']

end

def sort_platform_string
@@ -68,7 +68,7 @@ module Type
#
def initialize(info = {})
super

self.can_cleanup = true
# If this is a staged payload but there is no stage information,
# then this is actually a stager + single combination. Set up the
# information hash accordingly.
@@ -537,6 +537,11 @@ def on_session(session)

end

#
# This attribute designates if the payload supports onsession()
# method calls (typically to clean up artifacts)
#
attr_accessor :can_cleanup
#
# This attribute holds the string that should be prepended to the buffer
# when it's generated.
@@ -668,6 +673,7 @@ def internal_generate
# Merge the name to prefix the existing one and separate them
# with a comma
#

def merge_name(info, val)
if (info['Name'])
info['Name'] = val + ',' + info['Name']
@@ -0,0 +1,39 @@
# -*- coding => binary -*-

require 'msf/core'
require 'msf/core/module/platform'
require 'rex/text'

#
# This class provides methods for calculating, extracting, and parsing
# unique ID values used by payloads.
#
module Msf::Payload::Pingback

attr_accessor :pingback_uuid
attr_accessor :can_cleanup

# Generate a Pingback UUID and write it to the database
def generate_pingback_uuid
self.pingback_uuid ||= SecureRandom.uuid()
self.pingback_uuid.to_s.gsub!("-", "")
datastore['PingbackUUID'] = self.pingback_uuid
vprint_status("PingbackUUID = #{datastore['PingbackUUID']}")
if framework.db.active
vprint_status("Writing UUID #{datastore['PingbackUUID']} to database...")
framework.db.create_payload(name: datastore['PayloadUUIDName'],
uuid: datastore['PingbackUUID'],
description: 'pingback',
platform: platform.platforms.first.realname.downcase)
else
print_warning("Unable to save UUID #{datastore['PingbackUUID']} to database -- database support not active")
end
self.pingback_uuid
end

def initialize(info = {})
super(info)
self.can_cleanup = false
self
end
end
@@ -0,0 +1,21 @@
# -*- coding => binary -*-

require 'msf/core'
require 'msf/core/payload/pingback'

#
# This module provides datastore option definitions and helper methods for payload modules that support UUIDs
#
module Msf::Payload::Pingback::Options

def initialize(info = {})
super
register_advanced_options(
[
Msf::OptInt.new('PingbackRetries', [true, "How many additional successful pingbacks", 0]),
Msf::OptInt.new('PingbackSleep', [true, "Time (in seconds) to sleep between pingbacks", 30])
], self.class)
end


end
@@ -1,13 +1,15 @@
# -*- coding: binary -*-

require 'msf/core/payload/uuid/options'
require 'msf/core/payload/pingback/options'

##
# This module contains helper functions for creating the transport
# configuration stubs that are used for Meterpreter payloads.
##
module Msf::Payload::TransportConfig

include Msf::Payload::Pingback::Options
include Msf::Payload::UUID::Options

def transport_config_reverse_tcp(opts={})
@@ -20,7 +20,6 @@ module Payload::Windows::ReverseTcp_x64
include Msf::Payload::Windows
include Msf::Payload::Windows::SendUUID_x64
include Msf::Payload::Windows::BlockApi_x64
include Msf::Payload::Windows::Exitfunk_x64

#
# Register reverse_tcp specific options
@@ -374,6 +374,10 @@ def generate_java_payload
# methods in order based on the supplied options and returns the finished payload.
# @return [String] A string containing the bytes of the payload in the format selected
def generate_payload
if payload.include?("pingback") and framework.db.active == false
cli_print "[-] WARNING: UUID cannot be saved because database is inactive."
end

if platform == "java" or arch == "java" or payload.start_with? "java/"
raw_payload = generate_java_payload
encoded_payload = raw_payload
@@ -0,0 +1,50 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/handler/bind_tcp'
require 'msf/core/payload/pingback'
require 'msf/base/sessions/pingback'

module MetasploitModule

CachedSize = 103

include Msf::Payload::Single
include Msf::Payload::Pingback
include Msf::Payload::Pingback::Options

def initialize(info = {})
super(merge_info(info,
'Name' => 'Unix Command Shell, Pingback Bind TCP (via netcat)',
'Description' => 'Accept a connection, send a UUID, then exit',
'Author' =>
[
'asoto-r7'
],
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Handler' => Msf::Handler::BindTcp,
'Session' => Msf::Sessions::Pingback,
'PayloadType' => 'cmd',
'RequiredCmd' => 'netcat'
))
end

#
# Constructs the payload
#
def generate
super.to_s + command_string
end

#
# Returns the command string to use for execution
#
def command_string
self.pingback_uuid ||= self.generate_pingback_uuid
"printf '#{pingback_uuid.scan(/../).map { |x| "\\x" + x }.join}' | (nc -lp #{datastore['LPORT']} || nc -l #{datastore['LPORT']})"
end
end
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.