From c8d4f683594e8affcdd82750d176cbff60cab0c7 Mon Sep 17 00:00:00 2001
From: Oleksii Shevchuk
Date: Thu, 20 Apr 2017 14:09:41 +0300
Subject: [PATCH] Add basic tags support to dnscnc and sessions
---
pupy/network/lib/picocmd/server.py | 4 --
pupy/packages/all/pyuvproxy.py | 3 ++
pupy/pupylib/PupyClient.py | 3 ++
pupy/pupylib/PupyCmd.py | 85 ++++++++++++++++++++++++------
pupy/pupylib/PupyConfig.py | 81 +++++++++++++++++++++++++++-
pupy/pupylib/PupyDnsCnc.py | 53 ++++++++++++++++++-
pupy/pupylib/PupyServer.py | 47 ++++++++++++-----
7 files changed, 239 insertions(+), 37 deletions(-)
diff --git a/pupy/network/lib/picocmd/server.py b/pupy/network/lib/picocmd/server.py
index 997621eb..38e5dcb0 100644
--- a/pupy/network/lib/picocmd/server.py
+++ b/pupy/network/lib/picocmd/server.py
@@ -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 = []
diff --git a/pupy/packages/all/pyuvproxy.py b/pupy/packages/all/pyuvproxy.py
index 64fc560b..c5aaba8c 100644
--- a/pupy/packages/all/pyuvproxy.py
+++ b/pupy/packages/all/pyuvproxy.py
@@ -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:
diff --git a/pupy/pupylib/PupyClient.py b/pupy/pupylib/PupyClient.py
index 0d79656d..54c7a0f3 100755
--- a/pupy/pupylib/PupyClient.py
+++ b/pupy/pupylib/PupyClient.py
@@ -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()
diff --git a/pupy/pupylib/PupyCmd.py b/pupy/pupylib/PupyCmd.py
index aada9ad6..3ffa5d54 100644
--- a/pupy/pupylib/PupyCmd.py
+++ b/pupy/pupylib/PupyCmd.py
@@ -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
diff --git a/pupy/pupylib/PupyConfig.py b/pupy/pupylib/PupyConfig.py
index c267c91e..6ecd188f 100644
--- a/pupy/pupylib/PupyConfig.py
+++ b/pupy/pupylib/PupyConfig.py
@@ -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)
diff --git a/pupy/pupylib/PupyDnsCnc.py b/pupy/pupylib/PupyDnsCnc.py
index d4d6b71c..967a01c3 100644
--- a/pupy/pupylib/PupyDnsCnc.py
+++ b/pupy/pupylib/PupyDnsCnc.py
@@ -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(
diff --git a/pupy/pupylib/PupyServer.py b/pupy/pupylib/PupyServer.py
index e03a017a..2e0d91a6 100644
--- a/pupy/pupylib/PupyServer.py
+++ b/pupy/pupylib/PupyServer.py
@@ -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):