Add basic tags support to dnscnc and sessions

This commit is contained in:
Oleksii Shevchuk 2017-04-20 14:09:41 +03:00
parent 96bbb4f895
commit c8d4f68359
7 changed files with 239 additions and 37 deletions

View File

@ -166,10 +166,6 @@ class DnsCommandServerHandler(BaseResolver):
@locked
def reset_commands(self, session=None, default=False):
if session:
if type(session) in (str, unicode):
session = int(session, 16)
if default:
self.commands = []

View File

@ -107,6 +107,9 @@ class Connection(object):
struct.pack(
'BB', 0x5, ERRNO_TO_SOCKS5.get(error, CODE_GENERAL_SRV_FAILURE)
) + self.socks5[2:])
except:
pass
finally:
self.close(error, mutual=False)
else:

View File

@ -77,6 +77,9 @@ class PupyClient(object):
except Exception:
return "unknown"
def node(self):
return self.desc['macaddr'].replace(':', '').lower()
def is_unix(self):
return not self.is_windows()

View File

@ -446,6 +446,48 @@ class PupyCmd(cmd.Cmd):
doc = ''
self.stdout.write("{:<25} {}\n".format("%s/%s"%(mod.category,mod.get_name()), color(doc.title().split("\n",1)[0],'grey')))
def do_tag(self, arg):
""" add tag to current session """
arg_parser = PupyArgumentParser(prog='sessions', description=self.do_tag.__doc__)
arg_parser.add_argument('-a', '--add', metavar='tag', nargs='+', help='Add tags')
arg_parser.add_argument('-r', '--remove', metavar='tag', nargs='+', help='Remove tags')
arg_parser.add_argument('-w', '--write-project', action='store_true',
default=False, help='save config to project folder')
arg_parser.add_argument('-W', '--write-user', action='store_true',
default=False, help='save config to user folder')
try:
modargs = arg_parser.parse_args(shlex.split(arg))
except PupyModuleExit:
return
data = []
clients = self.pupsrv.get_clients(self.default_filter)
if not clients:
return
for client in clients:
tags = self.pupsrv.config.tags(client.node())
if modargs.remove:
tags.remove(*modargs.remove)
if modargs.add:
tags.add(*modargs.add)
data.append({
'ID': client.node(),
'TAGS': tags
})
self.config.save(project=modargs.write_project, user=modargs.write_user)
self.display(
PupyCmd.table_format(data)
)
def do_sessions(self, arg):
""" list/interact with established sessions """
arg_parser = PupyArgumentParser(prog='sessions', description=self.do_sessions.__doc__)
@ -501,15 +543,26 @@ class PupyCmd(cmd.Cmd):
columns = [
'id', 'user', 'hostname', 'platform', 'release', 'os_arch',
'proc_arch', 'intgty_lvl', 'address'
'proc_arch', 'intgty_lvl', 'address', 'tags'
]
self.display(PupyCmd.table_format([
{
k:colorize(v, 'white' if x in filtered_clients else 'darkgrey')
for k,v in x.desc.iteritems() if k in columns
} for x in client_list
], wl=columns))
content = []
for client in client_list:
color = 'white' if client in filtered_clients else 'darkgrey'
data = {
k:colorize(v, color)
for k,v in client.desc.iteritems() if k in columns
}
data.update({
'tags': colorize(self.config.tags(client.node()), color)
})
content.append(data)
self.display(PupyCmd.table_format(content, wl=columns))
elif modargs.killall:
client_list=self.pupsrv.get_clients_list()
@ -950,7 +1003,7 @@ class PupyCmd(cmd.Cmd):
]))
elif args.command == 'info':
sessions = self.dnscnc.list()
sessions = self.dnscnc.list(args.node)
if not sessions:
self.display_success('No active DNSCNC sesisons found')
return
@ -977,15 +1030,17 @@ class PupyCmd(cmd.Cmd):
'EST': '{:d}'.format(session.system_status['remote']),
'USERS': '{:d}'.format(session.system_status['users']),
'IDLE': '{}'.format(session.system_status['idle']),
'TAGS': '{}'.format(self.config.tags(session.system_info['node']))
}
pupy_session = None
for c in self.pupsrv.clients:
if c.desc['macaddr'].replace(':','').lower() == \
'{:012x}'.format(session.system_info['node']):
pupy_session = c.desc['id']
objects
break
if 'spi' in c.desc:
if c.desc['spi'] == '{:08x}'.format(session.spi):
pupy_session = c.desc['id']
elif c.node() == '{:012x}'.format(session.system_info['node']):
pupy_session = c.desc['id']
break
color = ''
if pupy_session:
@ -1005,7 +1060,7 @@ class PupyCmd(cmd.Cmd):
columns = [
'#', 'P', 'NODE', 'SESSION', 'IP', 'OS',
'CPU', 'MEM', 'LIS', 'EST', 'USERS', 'IDLE'
'CPU', 'MEM', 'LIS', 'EST', 'USERS', 'IDLE', 'TAGS'
]
self.display(
@ -1013,7 +1068,7 @@ class PupyCmd(cmd.Cmd):
)
elif args.command == 'list':
sessions = self.dnscnc.list()
sessions = self.dnscnc.list(args.node)
if not sessions:
self.display_success('No active DNSCNC sesisons found')
return

View File

@ -10,6 +10,48 @@ import platform
import random
import string
class Tags(object):
def __init__(self, config, node):
self.config = config
self.node = node
def __iter__(self):
return iter(self.get())
def get(self):
try:
return set(self.config.get('tags', self.node).split(','))
except:
return set()
def set(self, tags):
return self.config.set('tags', self.node, ','.join([
str(x) for x in tags
]))
def add(self, *tags):
current_tags = self.get()
for tag in tags:
current_tags.add(tag)
self.set(current_tags)
def remove(self, *tags):
current_tags = self.get()
for tag in tags:
if tag in current_tags:
current_tags.remove(tag)
if current_tags:
self.set(current_tags)
else:
self.clear()
def clear(self):
self.config.remove_option('tags', self.node)
def __str__(self):
return ','.join(self.get())
class PupyConfig(ConfigParser):
NoSectionError = NoSectionError
@ -31,6 +73,35 @@ class PupyConfig(ConfigParser):
ConfigParser.__init__(self)
self.read(self.files)
def tags(self, node):
if type(node) in (int, long):
node = '{:012x}'.format(node)
return Tags(self, node)
def by_tags(self, tags):
available_tags = {
k:self.get('tags', k).split(',') for k in self.options('tags')
}
if '&' in tags:
tags = tags.split('&')
op_filter = all
elif '|' in tags:
tags = tags.split('|')
op_filter = any
else:
tags = tags.split(',')
op_filter = any
result = []
for node, node_tags in available_tags.iteritems():
if op_filter(x in node_tags for x in tags):
result.append(node)
return result
def save(self, project=True, user=False):
if project:
project_dir = path.dirname(self.project_path)
@ -98,7 +169,8 @@ class PupyConfig(ConfigParser):
def remove_option(self, section, key):
if section != 'randoms':
ConfigParser.unset(self, section, key)
ConfigParser.remove_option(self, section, key)
elif section in self.command_line and key in self.command_line[section]:
del self.command_line[section][key]
if not self.command_line[section]:
@ -120,7 +192,12 @@ class PupyConfig(ConfigParser):
if not self.command_line[section]:
del self.command_line[section]
ConfigParser.set(self, section, key, value)
try:
ConfigParser.set(self, section, key, value)
except NoSectionError:
ConfigParser.add_section(self, section)
ConfigParser.set(self, section, key, value)
else:
if not key:
N = kwargs.get('random', 10)

View File

@ -18,6 +18,12 @@ from network.lib.igd import IGDClient, UPNPError
class PupyDnsCommandServerHandler(DnsCommandServerHandler):
def __init__(self, *args, **kwargs):
if 'config' in kwargs:
self.config = kwargs.get('config')
del kwargs['config']
else:
self.config = None
DnsCommandServerHandler.__init__(self, *args, **kwargs)
def connect(self, hosts, port, transport, node=None, default=False):
@ -55,6 +61,47 @@ class PupyDnsCommandServerHandler(DnsCommandServerHandler):
session=node, default=default
)
def find_sessions(self, spi=None, node=None):
if spi or node:
results = []
if self.config and node:
if type(node) in (str,unicode):
nodes = []
for n in node.split(','):
try:
int(n, 16)
nodes.append(n)
except:
for tagged in self.config.by_tags(n):
nodes.append(tagged)
if nodes:
results = DnsCommandServerHandler.find_sessions(
self, node=','.join(nodes)
)
else:
results = []
if spi:
if type(spi) in (str,unicode):
spis = []
for s in spi.split(','):
try:
int(s, 16)
spis.append(s)
except:
pass
if spis:
results += DnsCommandServerHandler.find_sessions(
self, spi=','.join(spis)
)
else:
results = DnsCommandServerHandler.find_sessions(self)
return results
class PupyDnsCnc(object):
def __init__(
self, igd=None, connect_host=None,
@ -101,7 +148,8 @@ class PupyDnsCnc(object):
self.handler = PupyDnsCommandServerHandler(
domain,
credentials['DNSCNC_PRIV_KEY'],
recursor=recursor
recursor=recursor,
config=self.config
)
self.server = DnsCommandServer(
@ -120,7 +168,8 @@ class PupyDnsCnc(object):
self.server.stop()
def list(self, node=None):
return [ session for session in self.handler.find_sessions(node=node) ]
return self.handler.find_sessions(node=node) \
or self.handler.find_sessions(spi=node)
def connect(self, host=None, port=None, transport=None, node=None, default=False):
return self.handler.connect(

View File

@ -221,6 +221,10 @@ class PupyServer(threading.Thread):
def get_clients(self, search_criteria):
""" return a list of clients corresponding to the search criteria. ex: platform:*win* """
#if the criteria is a simple id we return the good client
if not search_criteria:
return self.clients
try:
indexes = set(
int(x) for x in str(search_criteria).split(',')
@ -236,32 +240,47 @@ class PupyServer(threading.Thread):
return
l=set([])
if search_criteria=="*":
return self.clients
for c in self.clients:
take=False
take = False
tags = self.config.tags(c.node())
for sc in search_criteria.split():
tab=sc.split(":",1)
if len(tab)==2 and tab[0] in [x for x in c.desc.iterkeys()]:#if the field is specified we search for the value in this field
tab = sc.split(":",1)
#if the field is specified we search for the value in this field
if len(tab)==2 and tab[0] in c.desc:
take=True
if not tab[1].lower() in str(c.desc[tab[0]]).lower():
take=False
break
elif len(tab)==2 and tab[0] == 'tag' and tab[1] in tags:
take = True
elif len(tab)==2 and tab[0] == 'tags':
if '&' in tab[1]:
take = all(x in tags for x in tab[1].split('&') if x)
else:
take = any(x in tags for x in tab[1].split(',') if x)
elif len(tab)!=2:#if there is no field specified we search in every field for at least one match
take=False
for k,v in c.desc.iteritems():
if type(v) is unicode or type(v) is str:
if tab[0].lower() in v.decode('utf8').lower():
take=True
break
else:
if tab[0].lower() in str(v).decode('utf8').lower():
take=True
break
if not take:
break
if tab[0] in tags:
take = True
else:
for k,v in c.desc.iteritems():
if type(v) is unicode or type(v) is str:
if tab[0].lower() in v.decode('utf8').lower():
take=True
break
else:
if tab[0].lower() in str(v).decode('utf8').lower():
take=True
break
if not take:
break
if take:
l.add(c)
return list(l)
def get_clients_list(self):