mirror of
https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite.git
synced 2025-12-10 10:49:02 +00:00
455 lines
19 KiB
C#
455 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace winPEAS.Helpers.AppLocker
|
|
{
|
|
internal static class AppLockerHelper
|
|
{
|
|
private static readonly HashSet<string> _appLockerByPassDirectoriesSet = new HashSet<string>
|
|
{
|
|
@"C:\Windows\Temp",
|
|
@"C:\Windows\System32\spool\drivers\color",
|
|
@"C:\Windows\Tasks",
|
|
@"C:\windows\tracing",
|
|
@"C:\Windows\Registration\CRMLog",
|
|
@"C:\Windows\System32\FxsTmp",
|
|
@"C:\Windows\System32\com\dmp",
|
|
@"C:\Windows\System32\Microsoft\Crypto\RSA\MachineKeys",
|
|
@"C:\Windows\System32\spool\PRINTERS",
|
|
@"C:\Windows\System32\spool\SERVERS",
|
|
@"C:\Windows\System32\Tasks\Microsoft\Windows\SyncCenter",
|
|
@"C:\Windows\System32\Tasks_Migrated",
|
|
@"C:\Windows\SysWOW64\FxsTmp",
|
|
@"C:\Windows\SysWOW64\com\dmp",
|
|
@"C:\Windows\SysWOW64\Tasks\Microsoft\Windows\SyncCenter",
|
|
@"C:\Windows\SysWOW64\Tasks\Microsoft\Windows\PLA\System",
|
|
};
|
|
|
|
// https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/working-with-applocker-rules
|
|
private static readonly Dictionary<string, HashSet<string>> _appLockerFileExtensionsByType = new Dictionary<string, HashSet<string>>()
|
|
{
|
|
{ "appx", new HashSet<string> { ".appx" } },
|
|
{ "dll", new HashSet<string> { ".dll", ".ocx" } },
|
|
{ "exe", new HashSet<string> { ".exe", ".com" } },
|
|
{ "msi", new HashSet<string> { ".msi", ".msp", ".mst" } },
|
|
{ "script", new HashSet<string> { ".ps1", ".bat", ".cmd", ".vbs", ".js"} },
|
|
};
|
|
|
|
private static Dictionary<string, HashSet<string>> _appLockerByPassDirectoriesByPath = null;
|
|
private const int FolderCheckMaxDepth = 3;
|
|
|
|
public static void CreateLists()
|
|
{
|
|
if (_appLockerByPassDirectoriesByPath != null) return;
|
|
|
|
_appLockerByPassDirectoriesByPath = new Dictionary<string, HashSet<string>>();
|
|
|
|
foreach (var appLockerByPassDirectory in _appLockerByPassDirectoriesSet)
|
|
{
|
|
string directoryLower = appLockerByPassDirectory.ToLower();
|
|
string currentDirectory = Directory.GetParent(directoryLower)?.FullName;
|
|
|
|
while (!string.IsNullOrEmpty(currentDirectory))
|
|
{
|
|
if (!_appLockerByPassDirectoriesByPath.ContainsKey(currentDirectory))
|
|
{
|
|
_appLockerByPassDirectoriesByPath[currentDirectory] = new HashSet<string>();
|
|
}
|
|
|
|
if (!_appLockerByPassDirectoriesByPath[currentDirectory].Contains(appLockerByPassDirectory))
|
|
{
|
|
_appLockerByPassDirectoriesByPath[currentDirectory].Add(appLockerByPassDirectory);
|
|
}
|
|
|
|
currentDirectory = Directory.GetParent(currentDirectory)?.FullName;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void PrintAppLockerPolicy()
|
|
{
|
|
Beaprint.MainPrint("Checking AppLocker effective policy");
|
|
|
|
try
|
|
{
|
|
string[] ruleTypes = { "All" };
|
|
var appLockerSettings = SharpAppLocker.GetAppLockerPolicy(SharpAppLocker.PolicyType.Effective, ruleTypes, string.Empty, false, false);
|
|
|
|
Beaprint.NoColorPrint($" AppLockerPolicy version: {appLockerSettings.Version}\n listing rules:\n\n");
|
|
|
|
foreach (var rule in appLockerSettings.RuleCollection)
|
|
{
|
|
PrintFileHashRules(rule);
|
|
PrintFilePathRules(rule);
|
|
PrintFilePublisherRules(rule);
|
|
}
|
|
}
|
|
catch (COMException)
|
|
{
|
|
Beaprint.ColorPrint(" AppLocker unsupported on this Windows version.", Beaprint.ansi_color_yellow);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Beaprint.PrintException(ex.Message);
|
|
}
|
|
}
|
|
|
|
private static void PrintFilePublisherRules(AppLockerPolicyRuleCollection rule)
|
|
{
|
|
if (rule?.FilePublisherRule == null) return;
|
|
|
|
foreach (var filePublisherRule in rule.FilePublisherRule)
|
|
{
|
|
Beaprint.GoodPrint($" File Publisher Rule\n");
|
|
|
|
Beaprint.NoColorPrint($" Rule Type: {rule.Type}\n" +
|
|
$" Enforcement Mode: {rule.EnforcementMode}\n" +
|
|
$" Name: {filePublisherRule.Name}\n" +
|
|
$" Description: {filePublisherRule.Description}\n" +
|
|
$" Action: {filePublisherRule.Action}");
|
|
|
|
var color = GetColorBySid(filePublisherRule.UserOrGroupSid);
|
|
|
|
Beaprint.ColorPrint( $" User Or Group Sid: {filePublisherRule.UserOrGroupSid}\n", color);
|
|
|
|
Beaprint.GoodPrint($" Conditions");
|
|
|
|
foreach (var condition in filePublisherRule.Conditions)
|
|
{
|
|
Beaprint.NoColorPrint(
|
|
$" Binary Name: {condition.BinaryName}\n" +
|
|
$" Binary Version Range: ({condition.BinaryVersionRange.LowSection} - {condition.BinaryVersionRange.HighSection})\n" +
|
|
$" Product Name: {condition.ProductName}\n" +
|
|
$" Publisher Name: {condition.PublisherName}\n");
|
|
}
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
|
|
private static void PrintFilePathRules(AppLockerPolicyRuleCollection rule)
|
|
{
|
|
if (rule?.FilePathRule == null) return;
|
|
|
|
foreach (var filePathRule in rule.FilePathRule)
|
|
{
|
|
Beaprint.GoodPrint($" File Path Rule\n");
|
|
|
|
var normalizedName = NormalizePath(filePathRule.Name);
|
|
|
|
|
|
Beaprint.NoColorPrint($" Rule Type: {rule.Type}\n" +
|
|
$" Enforcement Mode: {rule.EnforcementMode}\n" +
|
|
$" Name: {filePathRule.Name}\n" +
|
|
$" Translated Name: {normalizedName}\n" +
|
|
$" Description: {filePathRule.Description}\n" +
|
|
$" Action: {filePathRule.Action}");
|
|
|
|
var color = GetColorBySid(filePathRule.UserOrGroupSid);
|
|
|
|
Beaprint.ColorPrint( $" User Or Group Sid: {filePathRule.UserOrGroupSid}\n", color);
|
|
|
|
Beaprint.GoodPrint($" Conditions");
|
|
|
|
foreach (var condition in filePathRule.Conditions)
|
|
{
|
|
// print wildcards as red and continue
|
|
if (condition.Path == "*" || condition.Path == "*.*")
|
|
{
|
|
Beaprint.ColorPrint(
|
|
$" Path: {condition.Path}", Beaprint.ansi_color_bad);
|
|
|
|
continue;
|
|
}
|
|
|
|
Beaprint.NoColorPrint(
|
|
$" Path: {condition.Path}");
|
|
|
|
|
|
// TODO
|
|
// cache permissions in a dictionary
|
|
|
|
var normalizedPath = NormalizePath(condition.Path);
|
|
|
|
// it's a file rule
|
|
if (IsFilePath(normalizedPath))
|
|
{
|
|
// TODO
|
|
// load permissions from cache
|
|
|
|
// check file
|
|
CheckFileWriteAccess(normalizedPath);
|
|
|
|
// check directories
|
|
string directory = Path.GetDirectoryName(normalizedPath);
|
|
|
|
CheckDirectoryAndParentsWriteAccess(directory);
|
|
}
|
|
|
|
// it's a directory rule
|
|
else
|
|
{
|
|
// TODO
|
|
// load permissions from cache
|
|
|
|
|
|
// does the directory exists?
|
|
if (Directory.Exists(normalizedPath))
|
|
{
|
|
// can we write to the directory ?
|
|
var folderPermissions = PermissionsHelper.GetPermissionsFolder(normalizedPath, Checks.Checks.CurrentUserSiDs, isOnlyWriteOrEquivalentCheck: true);
|
|
|
|
// we can write
|
|
if (folderPermissions.Count > 0)
|
|
{
|
|
Beaprint.BadPrint($" Directory \"{normalizedPath}\" Permissions: " + string.Join(",", folderPermissions));
|
|
}
|
|
// we cannot write to the folder
|
|
else
|
|
{
|
|
// first check well known AppLocker bypass locations
|
|
if (_appLockerByPassDirectoriesByPath.ContainsKey(normalizedPath))
|
|
{
|
|
// iterate over applocker bypass directories and check them
|
|
foreach (var subfolders in _appLockerByPassDirectoriesByPath[normalizedPath])
|
|
{
|
|
var subfolderPermissions = PermissionsHelper.GetPermissionsFolder(subfolders, Checks.Checks.CurrentUserSiDs, isOnlyWriteOrEquivalentCheck: true);
|
|
|
|
// we can write
|
|
if (subfolderPermissions.Count > 0)
|
|
{
|
|
Beaprint.BadPrint($" Directory \"{subfolders}\" Permissions: " + string.Join(",", subfolderPermissions));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// the well-known bypass location does not contain the folder
|
|
// check file / subfolder write permissions
|
|
else
|
|
{
|
|
// start with the current directory
|
|
bool isFileOrSubfolderWriteAccess = CheckFilesAndSubfolders(normalizedPath, rule.Type, 0);
|
|
|
|
if (!isFileOrSubfolderWriteAccess)
|
|
{
|
|
Beaprint.ColorPrint($" No potential bypass found while recursively checking files/subfolders " +
|
|
$"for write or equivalent permissions with depth: {FolderCheckMaxDepth}\n" +
|
|
$" Check permissions manually.", Beaprint.YELLOW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// do we have write access recursively for the parent folder(s)?
|
|
CheckDirectoryAndParentsWriteAccess(normalizedPath);
|
|
}
|
|
|
|
// TODO
|
|
// save to cache for faster next search
|
|
}
|
|
|
|
Beaprint.GoodPrint("");
|
|
}
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
|
|
private static void PrintFileHashRules(AppLockerPolicyRuleCollection rule)
|
|
{
|
|
if (rule?.FileHashRule == null) return;
|
|
|
|
foreach (var fileHashRule in rule.FileHashRule)
|
|
{
|
|
Beaprint.GoodPrint($" File Hash Rule\n");
|
|
|
|
Beaprint.NoColorPrint(
|
|
$" Rule Type: {rule.Type}\n" +
|
|
$" Enforcement Mode: {rule.EnforcementMode}\n" +
|
|
$" Name: {fileHashRule.Name}\n" +
|
|
$" Description: {fileHashRule.Description}\n" +
|
|
$" Action: {fileHashRule.Action}");
|
|
|
|
var color = GetColorBySid(fileHashRule.UserOrGroupSid);
|
|
|
|
Beaprint.ColorPrint(
|
|
$" User Or Group Sid: {fileHashRule.UserOrGroupSid}\n", color);
|
|
|
|
Beaprint.GoodPrint($" Conditions");
|
|
|
|
foreach (var condition in fileHashRule.Conditions)
|
|
{
|
|
Beaprint.NoColorPrint(
|
|
$" Source File Name: {condition.FileHash.SourceFileName}\n" +
|
|
$" Data: {condition.FileHash.Data}\n" +
|
|
$" Source File Length: {condition.FileHash.SourceFileLength}\n" +
|
|
$" Type: {condition.FileHash.Type}\n");
|
|
}
|
|
|
|
Beaprint.PrintLineSeparator();
|
|
}
|
|
}
|
|
|
|
private static string GetColorBySid(string sid)
|
|
{
|
|
var color = Checks.Checks.CurrentUserSiDs.ContainsKey(sid)
|
|
? Beaprint.ansi_color_bad
|
|
: Beaprint.ansi_color_good;
|
|
|
|
return color;
|
|
}
|
|
|
|
private static string NormalizePath(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path)) return string.Empty;
|
|
|
|
var systemDrive = Environment.GetEnvironmentVariable("SystemDrive");
|
|
path = path.Replace("%OSDRIVE%", systemDrive);
|
|
path = path.Replace("*", string.Empty);
|
|
path = path.TrimEnd('\\');
|
|
|
|
path = Environment.ExpandEnvironmentVariables(path);
|
|
path = path.ToLower();
|
|
|
|
return path;
|
|
}
|
|
|
|
private static bool CheckFilesAndSubfolders(string path, string ruleType, int depth)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(ruleType)) throw new ArgumentNullException(nameof(ruleType));
|
|
if (depth == FolderCheckMaxDepth) return false;
|
|
|
|
try
|
|
{
|
|
var subfolders = Directory.EnumerateDirectories(path);
|
|
var files = Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly);
|
|
|
|
ruleType = ruleType.ToLower();
|
|
|
|
if (!_appLockerFileExtensionsByType.ContainsKey(ruleType))
|
|
{
|
|
throw new ArgumentException(nameof(ruleType));
|
|
}
|
|
|
|
var filteredFiles =
|
|
(from file in files
|
|
let extension = Path.GetExtension(file)?.ToLower() ?? string.Empty
|
|
where _appLockerFileExtensionsByType[ruleType].Contains(extension)
|
|
select file).ToList();
|
|
|
|
// first check write access for files
|
|
if (filteredFiles.Any(CheckFileWriteAccess))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// if we have not found any writable file,
|
|
// check subfolders for write access
|
|
if (subfolders.Any(subfolder => CheckDirectoryWriteAccess(subfolder, out bool _, isGoodPrint: false)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// check recursively all the subfolders for files/sub-subfolders
|
|
if (subfolders.Any(subfolder => CheckFilesAndSubfolders(subfolder, ruleType, depth + 1)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool CheckFileWriteAccess(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path)) return false;
|
|
|
|
if (File.Exists(path))
|
|
{
|
|
var filePermissions = PermissionsHelper.GetPermissionsFile(path, Checks.Checks.CurrentUserSiDs, isOnlyWriteOrEquivalentCheck: true);
|
|
|
|
if (filePermissions.Count > 0)
|
|
{
|
|
Beaprint.BadPrint($" File \"{path}\" Permissions: " + string.Join(",", filePermissions));
|
|
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Beaprint.BadPrint($" File \"{path}\" does not exist.");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool CheckDirectoryAndParentsWriteAccess(string directory)
|
|
{
|
|
while (!string.IsNullOrEmpty(directory))
|
|
{
|
|
// first check if we have write permission on the directory
|
|
if (CheckDirectoryWriteAccess(directory, out var isDirectoryExisting))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// the directory exists and we don't have write permissions
|
|
// we can return false;
|
|
if (isDirectoryExisting)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// if the current folder does not exists, check it's parent directory recursively
|
|
directory = Directory.GetParent(directory)?.FullName;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool CheckDirectoryWriteAccess(string directory, out bool isDirectoryExisting, bool isGoodPrint = true)
|
|
{
|
|
isDirectoryExisting = true;
|
|
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
Beaprint.BadPrint($" Directory \"{directory}\" does not exist.");
|
|
isDirectoryExisting = false;
|
|
}
|
|
else
|
|
{
|
|
var folderPermissions = PermissionsHelper.GetPermissionsFolder(directory, Checks.Checks.CurrentUserSiDs, isOnlyWriteOrEquivalentCheck: true);
|
|
|
|
if (folderPermissions.Count > 0)
|
|
{
|
|
Beaprint.BadPrint($" Directory \"{directory}\" Permissions: " + string.Join(",", folderPermissions));
|
|
}
|
|
else
|
|
{
|
|
if (isGoodPrint)
|
|
{
|
|
Beaprint.GoodPrint($" {directory}");
|
|
}
|
|
}
|
|
|
|
return folderPermissions.Count > 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsFilePath(string path)
|
|
{
|
|
return !string.IsNullOrEmpty(path) &&
|
|
!string.IsNullOrWhiteSpace(Path.GetExtension(path));
|
|
}
|
|
}
|
|
}
|