forked from p34709852/monkey
Vastly improved communicate as new user PBA code structure, also not leaking any more process or thread handles.
This commit is contained in:
parent
e9cd20a345
commit
e618378c95
|
@ -0,0 +1,64 @@
|
|||
import logging
|
||||
import subprocess
|
||||
|
||||
from infection_monkey.post_breach.actions.add_user import BackdoorUser
|
||||
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NewUserError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NewUser(object):
|
||||
"""
|
||||
RAII object to use for creating and using a new user in Windows. Use with `with`.
|
||||
User will be created when the instance is instantiated.
|
||||
User will log on start of `with` scope.
|
||||
User will log off on end of `with` scope.
|
||||
|
||||
Example:
|
||||
# Created # Logged on
|
||||
with NewUser("user", "pass") as new_user:
|
||||
...
|
||||
...
|
||||
# Logged off
|
||||
...
|
||||
"""
|
||||
def __init__(self, username, password):
|
||||
"""
|
||||
Creates a user with the username + password.
|
||||
:raises: subprocess.CalledProcessError if failed to add the user.
|
||||
"""
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
windows_cmds = BackdoorUser.get_windows_commands_to_add_user(self.username, self.password, True)
|
||||
logger.debug("Trying these commands: {}".format(str(windows_cmds)))
|
||||
_ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
|
||||
|
||||
def __enter__(self):
|
||||
# Importing these only on windows, as they won't exist on linux.
|
||||
import win32security
|
||||
import win32con
|
||||
|
||||
try:
|
||||
# Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
|
||||
self.logon_handle = win32security.LogonUser(
|
||||
self.username,
|
||||
".", # Use current domain.
|
||||
self.password,
|
||||
win32con.LOGON32_LOGON_INTERACTIVE, # Logon type - interactive (normal user).
|
||||
win32con.LOGON32_PROVIDER_DEFAULT) # Which logon provider to use - whatever Windows offers.
|
||||
except Exception as err:
|
||||
raise NewUserError("Can't logon as {}. Error: {}".format(self.username, str(err)))
|
||||
return self
|
||||
|
||||
def get_logon_handle(self):
|
||||
return self.logon_handle
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.logon_handle.Close()
|
||||
# TODO Delete user
|
|
@ -5,6 +5,7 @@ import string
|
|||
import subprocess
|
||||
|
||||
from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
|
||||
from infection_monkey.monkey_utils.windows.new_user import NewUser, NewUserError
|
||||
from infection_monkey.post_breach.actions.add_user import BackdoorUser
|
||||
from infection_monkey.post_breach.pba import PBA
|
||||
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
||||
|
@ -31,29 +32,31 @@ class CommunicateAsNewUser(PBA):
|
|||
def run(self):
|
||||
username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
|
||||
if is_windows_os():
|
||||
self.communicate_as_new_user_windows(username)
|
||||
else:
|
||||
self.communicate_as_new_user_linux(username)
|
||||
|
||||
def communicate_as_new_user_linux(self, username):
|
||||
try:
|
||||
linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
|
||||
commandline = "ping -c 2 google.com"
|
||||
linux_cmds.extend([";", "sudo", "-u", username, commandline])
|
||||
final_command = ' '.join(linux_cmds)
|
||||
logger.debug("Trying to execute these commands: {}".format(final_command))
|
||||
output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
|
||||
PostBreachTelem(self, (
|
||||
CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send()
|
||||
except subprocess.CalledProcessError as e:
|
||||
PostBreachTelem(self, (e.output, False)).send()
|
||||
|
||||
def communicate_as_new_user_windows(self, username):
|
||||
# Importing these only on windows, as they won't exist on linux.
|
||||
import win32con
|
||||
import win32process
|
||||
import win32security
|
||||
|
||||
if not self.try_to_create_user_windows(username, PASSWORD):
|
||||
return # no point to continue if failed creating the user.
|
||||
import win32api
|
||||
|
||||
try:
|
||||
# Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
|
||||
new_user_logon_token_handle = win32security.LogonUser(
|
||||
username,
|
||||
".", # use current domain
|
||||
PASSWORD,
|
||||
win32con.LOGON32_LOGON_INTERACTIVE, # logon type - interactive (normal user)
|
||||
win32con.LOGON32_PROVIDER_DEFAULT) # logon provider
|
||||
except Exception as e:
|
||||
PostBreachTelem(
|
||||
self,
|
||||
("Can't logon as {}. Error: {}".format(username, e.message), False)
|
||||
).send()
|
||||
return # no point to continue if can't log on.
|
||||
|
||||
with NewUser(username, PASSWORD) as new_user:
|
||||
# Using os.path is OK, as this is on windows for sure
|
||||
ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
|
||||
if not os.path.exists(ping_app_path):
|
||||
|
@ -63,9 +66,9 @@ class CommunicateAsNewUser(PBA):
|
|||
try:
|
||||
# Open process as that user:
|
||||
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
|
||||
commandline = "{} {}".format(ping_app_path, "google.com")
|
||||
_ = win32process.CreateProcessAsUser(
|
||||
new_user_logon_token_handle, # A handle to the primary token that represents a user.
|
||||
commandline = "{} {} {} {}".format(ping_app_path, "google.com", "-n", "2")
|
||||
process_handle = win32process.CreateProcessAsUser(
|
||||
new_user.get_logon_handle(), # A handle to the primary token that represents a user.
|
||||
None, # The name of the module to be executed.
|
||||
commandline, # The command line to be executed.
|
||||
None, # Process attributes
|
||||
|
@ -80,39 +83,25 @@ class CommunicateAsNewUser(PBA):
|
|||
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
|
||||
)
|
||||
|
||||
PostBreachTelem(self, (
|
||||
CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send()
|
||||
return
|
||||
PostBreachTelem(self,
|
||||
(CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send()
|
||||
|
||||
win32api.CloseHandle(process_handle[0]) # Process handle
|
||||
win32api.CloseHandle(process_handle[1]) # Thread handle
|
||||
|
||||
except Exception as e:
|
||||
# TODO: if failed on 1314, we can try to add elevate the rights of the current user with the "Replace a
|
||||
# process level token" right, using Local Security Policy editing. Worked, but only after reboot. So:
|
||||
# TODO: if failed on 1314, we can try to add elevate the rights of the current user with the
|
||||
# "Replace a process level token" right, using Local Security Policy editing. Worked, but only
|
||||
# after reboot. So:
|
||||
# 1. need to decide if worth it, and then
|
||||
# 2. need to find how to do this using python...
|
||||
PostBreachTelem(self, (
|
||||
"Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
|
||||
return
|
||||
else:
|
||||
try:
|
||||
linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
|
||||
commandline = "ping -c 2 google.com"
|
||||
linux_cmds.extend([";", "sudo", "-u", username, commandline])
|
||||
final_command = ' '.join(linux_cmds)
|
||||
logger.debug("Trying to execute these commands: {}".format(final_command))
|
||||
output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
|
||||
PostBreachTelem(self, (
|
||||
CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send()
|
||||
return
|
||||
except subprocess.CalledProcessError as e:
|
||||
PostBreachTelem(self, (e.output, False)).send()
|
||||
return
|
||||
|
||||
def try_to_create_user_windows(self, username, password):
|
||||
try:
|
||||
windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password, True)
|
||||
logger.debug("Trying these commands: {}".format(str(windows_cmds)))
|
||||
subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Nothing more we can do. Leak the process handle.
|
||||
except subprocess.CalledProcessError as err:
|
||||
PostBreachTelem(self, (
|
||||
"Couldn't create the user '{}'. Error output is: '{}'".format(username, e.output), False)).send()
|
||||
return False
|
||||
"Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)),
|
||||
False)).send()
|
||||
except NewUserError as e:
|
||||
PostBreachTelem(self, (str(e), False)).send()
|
||||
|
|
Loading…
Reference in New Issue