Skip to content

Instantly share code, notes, and snippets.

@rxwx
Created July 3, 2023 12:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rxwx/1717e95e5ec11bea12d33e93a3832508 to your computer and use it in GitHub Desktop.
Save rxwx/1717e95e5ec11bea12d33e93a3832508 to your computer and use it in GitHub Desktop.
Determine redirection path for SxS DotLocal DLL Hijacking
using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using static GetSxsPath.NativeMethods;
namespace GetSxsPath
{
internal class NativeMethods
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ACTCTX
{
public int cbSize;
public uint dwFlags;
public string lpSource;
public UInt16 wProcessorArchitecture;
public UInt16 wLangId;
public string lpAssemblyDirectory;
public UInt16 lpResourceName;
public string lpApplicationName;
public IntPtr hModule;
}
public const uint ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID = 0x001;
public const uint ACTCTX_FLAG_LANGID_VALID = 0x002;
public const uint ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;
public const uint ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x008;
public const uint ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x010;
public const uint ACTCTX_FLAG_APPLICATION_NAME_VALID = 0x020;
public const uint ACTCTX_FLAG_HMODULE_VALID = 0x080;
public const uint RT_MANIFEST = 24;
public const uint CREATEPROCESS_MANIFEST_RESOURCE_ID = 1;
public const uint ISOLATIONAWARE_MANIFEST_RESOURCE_ID = 2;
public const uint ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID = 3;
[Flags]
public enum LoadLibraryFlags : uint
{
None = 0,
DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008,
LOAD_LIBRARY_REQUIRE_SIGNED_TARGET = 0x00000080,
LOAD_LIBRARY_SAFE_CURRENT_DIRS = 0x00002000,
}
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateActCtx(ref ACTCTX pActCtx);
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeactivateActCtx(int dwFlags, IntPtr lpCookie);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern void ReleaseActCtx(IntPtr hActCtx);
[DllImport("psapi.dll", SetLastError = true)]
public static extern uint GetMappedFileName(IntPtr hProcess, IntPtr lpv, StringBuilder lpFilename, uint nSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr hModule);
}
internal class Program
{
/// <summary>
/// Creates an Activation Context using the specified executable's manifest
/// The function then returns the redirected SxS path for the requested DLL
/// </summary>
private static string GetSxsPathFromActivationContext(string executablePath,
string queryDll = "comctl32.dll")
{
string sxsPath = null;
var actCtx = new ACTCTX
{
cbSize = Marshal.SizeOf(typeof(ACTCTX)),
lpSource = Path.GetFullPath(executablePath),
lpResourceName = 1,
dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID,
};
IntPtr hActCtx = CreateActCtx(ref actCtx);
if (hActCtx == new IntPtr(-1))
{
Console.WriteLine("[!] Failed to create Activation Context: {0}", Marshal.GetLastWin32Error());
return sxsPath;
}
Console.WriteLine("[+] Created Activation Context");
if (!ActivateActCtx(hActCtx, out IntPtr cookie))
{
Console.WriteLine("[!] Unable to activate ActCtx: {0}", Marshal.GetLastWin32Error());
goto CLEANUP_RELEASE;
}
Console.WriteLine("[*] Cookie: 0x{0:X}", cookie);
// Load the library we're interested in
IntPtr hModule = LoadLibraryEx(queryDll, IntPtr.Zero, LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE);
if (hModule == IntPtr.Zero)
{
Console.WriteLine("[!] Unable to load redirected DLL");
goto CLEANUP_DEACTIVATE;
}
else
{
Console.WriteLine("[*] LoadLibraryEx success");
StringBuilder fullPath = new StringBuilder(260);
GetMappedFileName(Process.GetCurrentProcess().Handle, hModule, fullPath, (uint)fullPath.Capacity);
if (fullPath.ToString().ToLower().Contains(queryDll) &&
fullPath.ToString().ToLower().Contains("winsxs"))
{
sxsPath = new DirectoryInfo(Path.GetDirectoryName(fullPath.ToString())).Name;
Console.WriteLine($"[+] Found SxS path: {sxsPath}");
}
FreeLibrary(hModule);
}
CLEANUP_DEACTIVATE:
if (!DeactivateActCtx(0, cookie))
{
Console.WriteLine("[!] Unable to deactivate ActCtx: {0}", Marshal.GetLastWin32Error());
}
CLEANUP_RELEASE:
ReleaseActCtx(hActCtx);
return sxsPath;
}
static void Main(string[] args)
{
string defaultFile = "uninstaller.exe";
if (!File.Exists(defaultFile) && (args.Length == 0 || !File.Exists(args[0])))
{
Console.WriteLine($"[!] {defaultFile} is not found in current directory, please provide the path as the first argument");
return;
}
// Determine SxS path to spoof, e.g.
// amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.22621.608_none_a9444ca7c10bb01d
// x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.19041.1110_none_c0da534e38c01f4d
string sxsPath = GetSxsPathFromActivationContext(File.Exists(defaultFile) ? defaultFile : args[0]);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment