mirror of
https://github.com/peass-ng/PEASS-ng.git
synced 2026-11-07 09:25:57 -10:00
Merge pull request #815 from peass-ng/fix/writable-executable-audit
Audit writable executable privilege-escalation leads
This commit is contained in:
4 files changed
+74
-10
No files matched your search
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using winPEAS.Helpers;
|
||||
using winPEAS.Info.ApplicationInfo;
|
||||
@@ -37,12 +40,13 @@ namespace winPEAS.Tests
|
||||
[TestMethod]
|
||||
public void WritableInstalledExecutableIsReportedByFilePathOnce()
|
||||
{
|
||||
string executable = @"C:\Program Files\Vendor\bin\sample.exe";
|
||||
string executable = @"C:\Program Files\Vendor.Agent\bin\sample.exe";
|
||||
var files = new[]
|
||||
{
|
||||
new CustomFileInfo("sample.exe", ".exe", executable, 1, false),
|
||||
new CustomFileInfo("SAMPLE.EXE", ".EXE", executable.ToUpperInvariant(), 1, false),
|
||||
new CustomFileInfo("read-only.exe", ".exe", @"C:\Program Files\Vendor\read-only.exe", 1, false),
|
||||
new CustomFileInfo("deny-only.exe", ".exe", @"C:\Program Files\Vendor\deny-only.exe", 1, false),
|
||||
new CustomFileInfo("notes.txt", ".txt", @"C:\Program Files\Vendor\notes.txt", 1, false),
|
||||
new CustomFileInfo("directory.exe", ".exe", @"C:\Program Files\Vendor\directory.exe", 0, true)
|
||||
};
|
||||
@@ -50,6 +54,8 @@ namespace winPEAS.Tests
|
||||
var findings = winPEAS.Checks.FilesInfo.FindWritableExecutables(files, path =>
|
||||
{
|
||||
checkedPaths.Add(path);
|
||||
if (path.EndsWith("deny-only.exe"))
|
||||
return new List<string> { "Users [Deny: AllAccess]" };
|
||||
return path.Equals(executable, StringComparison.OrdinalIgnoreCase)
|
||||
? new List<string> { "Users [Allow: AllAccess]" }
|
||||
: new List<string>();
|
||||
@@ -58,7 +64,36 @@ namespace winPEAS.Tests
|
||||
Assert.AreEqual(1, findings.Count);
|
||||
Assert.AreEqual(executable, findings[0].Key);
|
||||
Assert.AreEqual("Users [Allow: AllAccess]", findings[0].Value[0]);
|
||||
Assert.AreEqual(2, checkedPaths.Count);
|
||||
Assert.AreEqual(3, checkedPaths.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExactFilePermissionLookupHandlesDottedParentDirectory()
|
||||
{
|
||||
string parent = Path.Combine(Path.GetTempPath(), "winpeas-acl." + Guid.NewGuid().ToString("N"));
|
||||
string executable = Path.Combine(parent, "agent.exe");
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(parent);
|
||||
File.WriteAllText(executable, "fixture");
|
||||
SecurityIdentifier sid = WindowsIdentity.GetCurrent().User;
|
||||
FileSecurity security = File.GetAccessControl(executable);
|
||||
security.AddAccessRule(new FileSystemAccessRule(sid, FileSystemRights.WriteData,
|
||||
AccessControlType.Allow));
|
||||
File.SetAccessControl(executable, security);
|
||||
|
||||
var sids = new Dictionary<string, string> { { sid.Value, "fixture-user" } };
|
||||
var permissions = PermissionsHelper.GetPermissionsFile(executable, sids,
|
||||
PermissionType.WRITEABLE_OR_EQUIVALENT);
|
||||
Assert.IsTrue(permissions.Any(right => right.Contains("[Allow:")));
|
||||
var quotedCommandPermissions = PermissionsHelper.GetPermissionsFile(
|
||||
"\"" + executable + "\" --run", sids, PermissionType.WRITEABLE_OR_EQUIVALENT);
|
||||
Assert.IsTrue(quotedCommandPermissions.Any(right => right.Contains("[Allow:")));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(parent)) Directory.Delete(parent, true);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
@@ -109,6 +109,7 @@ namespace winPEAS.Checks
|
||||
{
|
||||
Beaprint.MainPrint("Installed Applications --Via Program Files/Uninstall registry--", "T1518");
|
||||
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#applications", "Check if you can modify installed software");
|
||||
Beaprint.GrayPrint(" Folder ACLs are review leads. Run filesinfo for executable-level write-ACL candidates; privilege escalation requires privileged execution and effective write access.");
|
||||
PrintDockerDesktopVersionRisk();
|
||||
PrintCheckmkAgentVersionRisk();
|
||||
PrintVeeamBackupVersionRisk();
|
||||
|
||||
@@ -116,11 +116,11 @@ namespace winPEAS.Checks
|
||||
};
|
||||
|
||||
|
||||
public string[] MitreAttackIds { get; } = new[] { "T1083", "T1552.001", "T1552.002", "T1552.004", "T1552.006", "T1003.002", "T1003.004", "T1564.001", "T1574.001", "T1059.004", "T1114.001", "T1218", "T1649" };
|
||||
public string[] MitreAttackIds { get; } = new[] { "T1083", "T1552.001", "T1552.002", "T1552.004", "T1552.006", "T1003.002", "T1003.004", "T1564.001", "T1574", "T1059.004", "T1114.001", "T1218", "T1649" };
|
||||
|
||||
public void PrintInfo(bool isDebug)
|
||||
{
|
||||
Beaprint.GreatPrint("Interesting files and registry", "T1083,T1552.001,T1552.002,T1552.004,T1552.006,T1003.002,T1003.004,T1564.001,T1574.001,T1059.004,T1114.001,T1218,T1649");
|
||||
Beaprint.GreatPrint("Interesting files and registry", "T1083,T1552.001,T1552.002,T1552.004,T1552.006,T1003.002,T1003.004,T1564.001,T1574,T1059.004,T1114.001,T1218,T1649");
|
||||
|
||||
new List<Action>
|
||||
{
|
||||
@@ -1005,7 +1005,7 @@ namespace winPEAS.Checks
|
||||
|
||||
private void PrintExecutablesWithWritePermissions()
|
||||
{
|
||||
Beaprint.MainPrint("Searching executable files with write (equivalent) permissions (can be slow)", "T1574.001");
|
||||
Beaprint.MainPrint("Executable file write-ACL candidates (can be slow)", "T1574");
|
||||
|
||||
var systemDrive = $"{Environment.GetEnvironmentVariable("SystemDrive")}\\";
|
||||
|
||||
@@ -1038,9 +1038,12 @@ namespace winPEAS.Checks
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var finding in FindWritableExecutables(files,
|
||||
var findings = FindWritableExecutables(files,
|
||||
path => PermissionsHelper.GetPermissionsFile(path, Checks.CurrentUserSiDs,
|
||||
PermissionType.WRITEABLE_OR_EQUIVALENT)))
|
||||
PermissionType.WRITEABLE_OR_EQUIVALENT));
|
||||
if (findings.Count > 0)
|
||||
Beaprint.GrayPrint(" Confirm effective write access and that a more privileged service, task, installer, or user executes the file before treating it as privilege escalation.");
|
||||
foreach (var finding in findings)
|
||||
{
|
||||
string log = $" File Permissions \"{finding.Key}\": " + string.Join(",", finding.Value);
|
||||
if (IsPathWithin(finding.Key, currentUserDir))
|
||||
@@ -1070,7 +1073,7 @@ namespace winPEAS.Checks
|
||||
try
|
||||
{
|
||||
var rights = getPermissions(file.FullPath);
|
||||
if (rights != null && rights.Count > 0)
|
||||
if (rights != null && rights.Any(right => right != null && right.Contains("[Allow:")))
|
||||
findings.Add(new KeyValuePair<string, List<string>>(file.FullPath, rights));
|
||||
}
|
||||
catch (Exception) { /* Inaccessible files should not stop the scan. */ }
|
||||
|
||||
@@ -92,9 +92,34 @@ namespace winPEAS.Helpers
|
||||
*/
|
||||
|
||||
List<string> results = new List<string>();
|
||||
path = path.Trim();
|
||||
if (path == null || path == "")
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return results;
|
||||
path = path.Trim();
|
||||
|
||||
// Most callers already have an exact path. Parsing it as a command line
|
||||
// can truncate paths with dotted parent directories (Vendor.App\bin.exe).
|
||||
try
|
||||
{
|
||||
return GetMyPermissionsF(File.GetAccessControl(path.Trim('"')), SIDs, permissionType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fall back to extracting a path from command lines and quoted values.
|
||||
}
|
||||
|
||||
if (path.StartsWith("\"", StringComparison.Ordinal))
|
||||
{
|
||||
int closingQuote = path.IndexOf('"', 1);
|
||||
if (closingQuote > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetMyPermissionsF(File.GetAccessControl(path.Substring(1, closingQuote - 1)),
|
||||
SIDs, permissionType);
|
||||
}
|
||||
catch { /* Keep the legacy command-line fallback below. */ }
|
||||
}
|
||||
}
|
||||
|
||||
Match reg_path = Regex.Match(path.ToString(), @"\W*([a-z]:\\[^.]+(\.[a-zA-Z0-9_-]+)?)\W*", RegexOptions.IgnoreCase);
|
||||
string binaryPath = reg_path.Groups[1].ToString();
|
||||
|
||||
Reference in new issue
Block a user