Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Veracrypt Self-Destruct Trigger #60

Open
maltfield opened this issue Jan 11, 2023 · 0 comments
Open

Veracrypt Self-Destruct Trigger #60

maltfield opened this issue Jan 11, 2023 · 0 comments
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed

Comments

@maltfield
Copy link
Member

maltfield commented Jan 11, 2023

This ticket will track the effort to implement a self-destruct trigger for veracrypt.

Work was started on this by @jneplokh here:

But I believe they got stuck on privilege escalation in Windows. When developing the soft-shutdown trigger on MacOS, I also encountered issues running as a non-root user, so I wrote a simple wrapper to launch a child process as root. I believe this would need to be ported to Windows for this task:

Deliverables would be:

  1. A function spawn_root_child() - A python function that, when executed as a non-root user, asks the user (via the official OS UAC prompt) for their password and then launches another python script (root_child_win.py) as child process with root privileges.
  2. root_child_win.sh A python script that, when executed as root by wrapper spawn_root_child(), it loops and waits for a command sent over stdin. If sent a veracrypt-self-destruct command, then it calls a function trigger_veracrypt-self-destruct()
  3. A function trigger_veracrypt-self-destruct() that finds all veracrypt volumes, securely wipes the veracrypt header and footer, and initiates a hard shutdown
  4. a forensic analysis that proves this works

[1] above would be similar to spawn_root_child() in src/packages/buskill/__init__.py

# launches a root child process
def spawn_root_child(self):
msg = "DEBUG: Called spawn_root_child()"
print( msg ); logger.debug( msg )
# SECURITY NOTE:
#
# Whenever you execute something as root, it's very important that you know
# _what_ you're executing. For example, never execute a script as root that is
# world-writeable. In general, assuming the script is named 'root_child.py':
#
# 1. Make sure root_child.py has permissions root:root 0500 (and therefore only
# writeable and executable by root)
# 2. Make sure I specify the absolute path to root_child.py, and that path
# cannot be maliciously manipulated
# 3. Make sure that root_child.py isn't actually a (sym)link
#
# See also:
# * https://github.com/BusKill/buskill-app/issues/14#issuecomment-1272449172
if self.OS_NAME_SHORT == 'lin':
msg = "ERROR: root_child_lin.py not yet implemented"
print( msg ); logger.error( msg )
elif self.OS_NAME_SHORT == 'win':
msg = "ERROR: root_child_win.py not yet implemented"
print( msg ); logger.error( msg )
elif self.OS_NAME_SHORT == 'mac':
# is the root child process already started?
if self.root_child == None:
# the root child process hasn't been started; start it
msg = "DEBUG: No root_child detected. Attempting to spawn one."
print( msg ); logger.debug( msg )
msg = "INFO: You have requested BusKill to do something that requires elevated privliges on your platform. If you'd like to proceed, please authorize BusKill to preform actions as Administrator. Your system may prompt you for your password to proceed."
print( msg ); logger.info( msg )
# To spawn a child process as root in MacOS, we use
# AuthorizationExecuteWithPrivileges(), which triggers the OS to
# display an auth challenge in the GUI for the user. See also
# * https://stackoverflow.com/a/74001980/1174102
# * https://stackoverflow.com/a/74083291/1174102
# * https://github.com/BusKill/buskill-app/issues/14
# was BusKill called as a binary or a script?
if self.EXECUTED_AS_SCRIPT == False:
# this execution was a binary
# let's call the root child binary
root_child_path = self.SRC_DIR +os.sep+ 'root_child_mac'
# and we'll be calling the binary directly
exe = [root_child_path, self.LOG_FILE_PATH]
else:
# this execution was a script; let's call the root child script
root_child_path = self.SRC_DIR +os.sep+ 'packages' +os.sep+ 'buskill' +os.sep+ 'root_child_mac.py'
# and we'll pass the script as an argument to the python
# interpreter
exe = [sys.executable, root_child_path, self.LOG_FILE_PATH]
msg = "DEBUG: root_child_path:|" +str(root_child_path)+ "|"
print( msg ); logger.debug( msg )
# SANITY CHECKS
mode = oct(os.stat(root_child_path).st_mode)[-4:]
owner = os.stat(root_child_path).st_uid
group = os.stat(root_child_path).st_gid
# verify the mode of the file is exactly 0500 (octal)
if mode != '0500':
msg = 'ERROR: Permissions on root_child are not 0500. Refusing to spawn script as root!'
print( msg ); logger.error( msg )
return False
# unfortunaetly we can't package a .dmg with a file owned by root, so on
# first run, we expect that the root child script will be owned by the
# user that executed the BusKill app
# https://github.com/BusKill/buskill-app/issues/14#issuecomment-1279975783
# verify the file is owned by user = root (or current user)
if owner != 0 and owner != os.getuid():
msg = 'ERROR: root_child is not owned by root nor your user. Refusing to spawn script as root!'
print( msg ); logger.error( msg )
return False
# verify the file is owned by group = root (or current group)
if group != 0 and group != os.getgid():
msg = 'ERROR: root_child is not owned by gid=0 nor your group. Refusing to spawn script as root!'
print( msg ); logger.error( msg )
return False
# verify the "file" isn't actually a symlink
if os.path.islink( root_child_path ):
msg = 'ERROR: root_child is a link. Refusing to spawn script as root!'
print( msg ); logger.error( msg )
return False
# import some C libraries for interacting via ctypes with the MacOS API
libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c"))
# https://developer.apple.com/documentation/security
sec = ctypes.cdll.LoadLibrary(ctypes.util.find_library("Security"))
kAuthorizationFlagDefaults = 0
auth = ctypes.c_void_p()
r_auth = byref(auth)
sec.AuthorizationCreate(None,None,kAuthorizationFlagDefaults,r_auth)
args = (ctypes.c_char_p * len(exe))()
for i,arg in enumerate(exe[1:]):
args[i] = arg.encode('utf8')
if self.root_child == None:
self.root_child = dict()
self.root_child['io'] = ctypes.c_void_p()
msg = "DEBUG: Attempting to spawn root child (" +str(exe)+ ")"
print( msg ); logger.debug( msg )
err = sec.AuthorizationExecuteWithPrivileges(
auth,
exe[0].encode('utf8'),
0,
args,
byref(self.root_child['io'])
)
msg = "DEBUG: AuthorizationExecuteWithPrivileges.err:|" +str(err)+ "|"
print( msg ); logger.debug( msg )
# did the attempt to spawn the child process return an error?
if err == -60007:
# https://developer.apple.com/documentation/security/1540004-authorization_services_result_co/errauthorizationinteractionnotallowed
msg = 'ERROR: root_child spwan attempt returned errAuthorizationInteractionNotAllowed = -60007. Did you execute BusKill from a headless CLI? The credential challenge requires a GUI when launching a child process as root.'
print( msg ); logger.error( msg )
return False
elif err == -60031:
# https://developer.apple.com/documentation/security/1540004-authorization_services_result_co/errauthorizationtoolexecutefailure
msg = 'ERROR: root_child spwan attempt returned errAuthorizationToolExecuteFailure = -60031. Is the root child binary executable? Check permissions.'
print( msg ); logger.error( msg )
return False
elif err != 0:
# catch all other errors
msg = 'ERROR: root_child spawn attempt returned ' +str(err)+ '. Please see reference documentation for Apple Authorization Services Result Codes @ https://developer.apple.com/documentation/security/1540004-authorization_services_result_co'
print( msg ); logger.error( msg )
return False
msg = "DEBUG: Root child spawned successfully!"
print( msg ); logger.debug( msg )
return True
# this basically just re-implmenets python's readline().strip() but in C
def read_from_root_child_mac(self):
libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c"))
# get the output from the child process character-by-character until we hit a new line
buf = ctypes.create_string_buffer(1)
result = ''
for x in range(1,100):
# read one byte from the child process' communication PIPE and store it to the buffer
libc.fread( byref(buf), 1, 1, self.root_child['io'] )
# decode the byte stored to the buffer as ascii
char = buf.raw[:1].decode('ascii')
# is the character a newline?
if char == "\n":
# the character is a newline; stop reading
break
else:
# the character is not a newline; append it to the string and continue reading
result += char
return result

[2] would be similar to https://github.com/BusKill/buskill-app/blob/master/src/packages/buskill/root_child_mac.py
[3] above would be similar to https://github.com/BusKill/buskill-linux/blob/master/triggers/buskill-selfdestruct.sh
[4] above would be similar to https://www.buskill.in/luks-self-destruct/

@maltfield maltfield added enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed labels Jan 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

1 participant