check windows exploits

This commit is contained in:
Carlos Polop
2026-02-26 01:33:15 +01:00
parent 98e5e7c3f2
commit a441eee83a
7 changed files with 562 additions and 2 deletions

View File

@@ -0,0 +1,35 @@
name: Update Windows Version Definitions
on:
schedule:
- cron: "17 4 */14 * *"
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update-definitions:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Update windows version definitions
run: python3 build_lists/update_windows_version_defs.py
- name: Create pull request
uses: peter-evans/create-pull-request@v7
with:
commit-message: "chore(winpeas): update windows version vulnerability definitions"
title: "chore(winpeas): update windows version vulnerability definitions"
body: "Automated update of `build_lists/windows_version_exploits.json` from latest WES-NG definitions."
branch: "bot/update-windows-version-definitions"
delete-branch: true

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Build a compact WinPEAS definitions file from WES-NG definitions.zip."""
from __future__ import annotations
import argparse
import csv
import io
import json
import tempfile
import zipfile
from collections import defaultdict
from pathlib import Path
from urllib.request import urlretrieve
DEFAULT_DEFINITIONS_URL = "https://raw.githubusercontent.com/bitsadmin/wesng/master/definitions.zip"
def _read_csv_from_zip(zip_file: zipfile.ZipFile, prefix: str) -> list[dict]:
target = next((name for name in zip_file.namelist() if name.startswith(prefix)), None)
if not target:
return []
with zip_file.open(target, "r") as raw:
return list(csv.DictReader(io.TextIOWrapper(raw, encoding="utf-8-sig")))
def build_definitions(definitions_zip: Path) -> dict:
with zipfile.ZipFile(definitions_zip, "r") as zf:
cve_file = next(name for name in zf.namelist() if name.startswith("CVEs_"))
generated = cve_file.split("_", 1)[1].split(".", 1)[0]
rows = _read_csv_from_zip(zf, "CVEs_")
rows.extend(_read_csv_from_zip(zf, "Custom_"))
products: dict[str, dict[str, dict[str, str]]] = defaultdict(dict)
for row in rows:
if not (row.get("Exploits") or "").strip():
continue
product = (row.get("AffectedProduct") or "").strip()
if not product:
continue
if "windows" not in product.lower():
continue
cve = (row.get("CVE") or "").strip()
kb = (row.get("BulletinKB") or "").strip()
vuln_key = cve or f"KB{kb}"
if not vuln_key:
continue
if vuln_key in products[product]:
continue
products[product][vuln_key] = {
"cve": cve,
"kb": kb,
"severity": (row.get("Severity") or "").strip(),
"impact": (row.get("Impact") or "").strip(),
}
data = {"generated": generated, "products": {}}
for product in sorted(products):
entries = [products[product][key] for key in sorted(products[product])]
data["products"][product] = entries
return data
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--definitions-url", default=DEFAULT_DEFINITIONS_URL)
parser.add_argument("--definitions-zip", default="")
parser.add_argument(
"--output",
default=str(Path("build_lists") / "windows_version_exploits.json"),
)
args = parser.parse_args()
output_path = Path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
if args.definitions_zip:
definitions_zip = Path(args.definitions_zip)
if not definitions_zip.exists():
raise FileNotFoundError(f"Definitions zip not found: {definitions_zip}")
else:
with tempfile.TemporaryDirectory(prefix="wesng_defs_") as temp_dir:
temp_zip = Path(temp_dir) / "definitions.zip"
urlretrieve(args.definitions_url, str(temp_zip))
data = build_definitions(temp_zip)
output_path.write_text(json.dumps(data, separators=(",", ":")) + "\n", encoding="utf-8")
if args.definitions_zip:
data = build_definitions(definitions_zip)
output_path.write_text(json.dumps(data, separators=(",", ":")) + "\n", encoding="utf-8")
total_products = len(data["products"])
total_entries = sum(len(v) for v in data["products"].values())
print(
f"Generated {output_path} (date={data['generated']}, products={total_products}, vulnerabilities={total_entries})"
)
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

View File

@@ -128,7 +128,7 @@ Once you have installed and activated it you need to:
- **System Information**
- [x] Basic System info information
- [x] Use WES-NG to search for vulnerabilities
- [x] Use embedded WES-NG definitions to flag known exploitable vulnerabilities for the running Windows version (version-based)
- [x] Enumerate Microsoft updates
- [x] PS, Audit, WEF and LAPS Settings
- [x] LSA protection

View File

@@ -29,6 +29,7 @@ namespace winPEAS.Checks
static string badUAC = "No prompting|PromptForNonWindowsBinaries";
static string goodUAC = "PromptPermitDenyOnSecureDesktop";
static string badLAPS = "LAPS not installed";
static Dictionary<string, string> _basicSystemInfo;
private static readonly Dictionary<string, string> _asrGuids = new Dictionary<string, string>
@@ -57,6 +58,7 @@ namespace winPEAS.Checks
new List<Action>
{
PrintBasicSystemInfo,
PrintWindowsVersionVulnerabilities,
PrintMicrosoftUpdatesCOM,
PrintSystemLastShutdownTime,
PrintUserEV,
@@ -105,6 +107,7 @@ namespace winPEAS.Checks
Beaprint.MainPrint("Basic System Information");
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#version-exploits", "Check if the Windows versions is vulnerable to some known exploit");
Dictionary<string, string> basicDictSystem = Info.SystemInfo.SystemInfo.GetBasicOSInfo();
_basicSystemInfo = basicDictSystem;
basicDictSystem["Hotfixes"] = Beaprint.ansi_color_good + basicDictSystem["Hotfixes"] + Beaprint.NOCOLOR;
Dictionary<string, string> colorsSI = new Dictionary<string, string>
{
@@ -119,6 +122,62 @@ namespace winPEAS.Checks
}
}
private static void PrintWindowsVersionVulnerabilities()
{
try
{
Beaprint.MainPrint("Windows Version Vulnerabilities");
var basicInfo = _basicSystemInfo ?? Info.SystemInfo.SystemInfo.GetBasicOSInfo();
var report = WindowsVersionVulns.GetVulnerabilityReport(basicInfo);
if (report.CandidateProducts.Count == 0)
{
Beaprint.InfoPrint("Unable to map this OS to WES-NG product definitions.");
return;
}
Beaprint.InfoPrint("WES-NG product candidates: " + string.Join(" | ", report.CandidateProducts));
if (!string.IsNullOrEmpty(report.DefinitionsDate))
{
Beaprint.InfoPrint("Definitions date: " + report.DefinitionsDate);
}
if (report.Vulnerabilities.Count == 0)
{
Beaprint.GoodPrint("No known exploited vulnerabilities matched this running Windows version.");
return;
}
Beaprint.BadPrint($"Matched {report.Vulnerabilities.Count} known exploited vulnerabilities for this running Windows version.");
if (report.MatchedProducts.Count > 0)
{
Beaprint.BadPrint("Matched products: " + string.Join(" | ", report.MatchedProducts));
}
int maxToPrint = 20;
foreach (var vuln in report.Vulnerabilities.Take(maxToPrint))
{
string vulnId = string.IsNullOrWhiteSpace(vuln.cve) ? $"KB{vuln.kb}" : vuln.cve;
string kbInfo = string.IsNullOrWhiteSpace(vuln.kb) ? "" : $" KB{vuln.kb}";
string severityInfo = string.IsNullOrWhiteSpace(vuln.severity) ? "" : $" [{vuln.severity}]";
string impactInfo = string.IsNullOrWhiteSpace(vuln.impact) ? "" : $" {vuln.impact}";
Beaprint.BadPrint($" {vulnId}{kbInfo}{severityInfo}{impactInfo}");
}
if (report.Vulnerabilities.Count > maxToPrint)
{
Beaprint.InfoPrint($"Showing {maxToPrint}/{report.Vulnerabilities.Count} results.");
}
Beaprint.InfoPrint("This check is version-based and does not validate installed KBs.");
}
catch (Exception ex)
{
Beaprint.PrintException(ex.Message);
}
}
private static void PrintMicrosoftUpdatesCOM()
{
try

View File

@@ -0,0 +1,355 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
namespace winPEAS.Info.SystemInfo
{
internal class WindowsVersionVulns
{
private const string DefinitionsFileName = "windows_version_exploits.json";
private static readonly object _cacheLock = new object();
private static WindowsVersionDefinitions _cachedDefinitions;
private static readonly SortedDictionary<int, string> BuildNumbers = new SortedDictionary<int, string>
{
{ 10240, "1507" },
{ 10586, "1511" },
{ 14393, "1607" },
{ 15063, "1703" },
{ 16299, "1709" },
{ 17134, "1803" },
{ 17763, "1809" },
{ 18362, "1903" },
{ 18363, "1909" },
{ 19041, "2004" },
{ 19042, "20H2" },
{ 19043, "21H1" },
{ 19044, "21H2" },
{ 19045, "22H2" },
{ 20348, "21H2" },
{ 22000, "21H2" },
{ 22621, "22H2" },
{ 22631, "23H2" },
{ 26100, "24H2" },
};
internal static WindowsVersionVulnReport GetVulnerabilityReport(Dictionary<string, string> basicInfo)
{
var report = new WindowsVersionVulnReport();
var definitions = LoadDefinitions();
if (definitions == null || definitions.products == null)
{
return report;
}
report.DefinitionsDate = definitions.generated ?? "";
report.CandidateProducts = BuildCandidateProducts(basicInfo);
var matchedProducts = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var vulnById = new Dictionary<string, WindowsVersionVulnEntry>(StringComparer.OrdinalIgnoreCase);
foreach (var candidate in report.CandidateProducts)
{
AddProductMatches(definitions.products, candidate, matchedProducts, vulnById);
}
report.MatchedProducts = matchedProducts.OrderBy(p => p).ToList();
report.Vulnerabilities = vulnById.Values
.OrderByDescending(v => GetSeverityPriority(v.severity))
.ThenBy(v => string.IsNullOrEmpty(v.cve) ? v.kb : v.cve, StringComparer.OrdinalIgnoreCase)
.ToList();
return report;
}
private static void AddProductMatches(
Dictionary<string, List<WindowsVersionVulnEntry>> products,
string candidate,
HashSet<string> matchedProducts,
Dictionary<string, WindowsVersionVulnEntry> vulnById)
{
if (string.IsNullOrWhiteSpace(candidate))
{
return;
}
var candidateVariants = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
candidate,
candidate.Replace(" Systems", " systems"),
candidate.Replace(" systems", " Systems")
};
foreach (var candidateVariant in candidateVariants)
{
if (products.TryGetValue(candidateVariant, out var exactEntries))
{
matchedProducts.Add(candidateVariant);
AddEntries(exactEntries, vulnById);
}
}
if (!candidate.StartsWith("Windows Server ", StringComparison.OrdinalIgnoreCase))
{
return;
}
foreach (var kv in products)
{
if (kv.Key.StartsWith(candidate, StringComparison.OrdinalIgnoreCase))
{
matchedProducts.Add(kv.Key);
AddEntries(kv.Value, vulnById);
}
}
}
private static void AddEntries(IEnumerable<WindowsVersionVulnEntry> entries, Dictionary<string, WindowsVersionVulnEntry> vulnById)
{
foreach (var entry in entries ?? Enumerable.Empty<WindowsVersionVulnEntry>())
{
var key = !string.IsNullOrWhiteSpace(entry.cve) ? entry.cve : $"KB{entry.kb}";
if (string.IsNullOrWhiteSpace(key))
{
continue;
}
if (!vulnById.ContainsKey(key))
{
vulnById[key] = entry;
}
}
}
private static int GetSeverityPriority(string severity)
{
switch ((severity ?? "").Trim().ToLowerInvariant())
{
case "critical":
return 4;
case "important":
return 3;
case "moderate":
return 2;
case "low":
return 1;
default:
return 0;
}
}
private static List<string> BuildCandidateProducts(Dictionary<string, string> basicInfo)
{
var candidates = new List<string>();
string osName = GetValue(basicInfo, "OS Name");
string productName = GetValue(basicInfo, "ProductName");
string osVersion = GetValue(basicInfo, "OS Version");
string systemType = GetValue(basicInfo, "System Type");
string text = $"{osName} {productName}";
string arch = GetArchitectureLabel(systemType);
string servicePack = GetServicePack(osVersion);
int build = GetBuildNumber(osVersion);
string clientVersion = GetVersionFromBuild(build);
if (Contains(text, "Windows 11") && !string.IsNullOrEmpty(clientVersion))
{
candidates.Add($"Windows 11 Version {clientVersion} for {arch} Systems");
}
if (Contains(text, "Windows 10") && !string.IsNullOrEmpty(clientVersion))
{
candidates.Add($"Windows 10 Version {clientVersion} for {arch} Systems");
}
if (Contains(text, "Windows 8.1"))
{
candidates.Add($"Windows 8.1 for {arch} systems");
candidates.Add($"Windows 8.1 for {arch} Systems");
}
if (Contains(text, "Windows 8") && !Contains(text, "Windows 8.1"))
{
candidates.Add($"Windows 8 for {arch} Systems");
}
if (Contains(text, "Windows 7"))
{
string win7 = $"Windows 7 for {arch} Systems";
if (!string.IsNullOrEmpty(servicePack))
{
win7 += $" Service Pack {servicePack}";
}
candidates.Add(win7);
}
AddServerCandidates(candidates, text, build, arch, servicePack);
return candidates
.Where(c => !string.IsNullOrWhiteSpace(c))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
}
private static void AddServerCandidates(List<string> candidates, string productText, int build, string arch, string servicePack)
{
string serverName = "";
if (Contains(productText, "Server 2025")) serverName = "2025";
else if (Contains(productText, "Server 2022")) serverName = "2022";
else if (Contains(productText, "Server 2019")) serverName = "2019";
else if (Contains(productText, "Server 2016")) serverName = "2016";
else if (Contains(productText, "Server 2012 R2")) serverName = "2012 R2";
else if (Contains(productText, "Server 2012")) serverName = "2012";
else if (Contains(productText, "Server 2008 R2")) serverName = "2008 R2";
else if (Contains(productText, "Server 2008")) serverName = "2008";
else if (Contains(productText, "Server 2003 R2")) serverName = "2003 R2";
else if (Contains(productText, "Server 2003")) serverName = "2003";
if (string.IsNullOrEmpty(serverName))
{
if (build >= 26100) serverName = "2025";
else if (build >= 20348) serverName = "2022";
else if (build >= 17763) serverName = "2019";
else if (build >= 14393) serverName = "2016";
}
if (string.IsNullOrEmpty(serverName))
{
return;
}
if (serverName == "2008" || serverName == "2008 R2")
{
string item = $"Windows Server {serverName} for {arch} Systems";
if (!string.IsNullOrEmpty(servicePack))
{
item += $" Service Pack {servicePack}";
}
candidates.Add(item);
candidates.Add(item + " (Server Core installation)");
return;
}
candidates.Add($"Windows Server {serverName}");
candidates.Add($"Windows Server {serverName} (Server Core installation)");
}
private static string GetArchitectureLabel(string systemType)
{
string value = (systemType ?? "").ToLowerInvariant();
if (value.Contains("x64"))
{
return "x64-based";
}
if (value.Contains("x86") || value.Contains("32"))
{
return "32-bit";
}
return "x64-based";
}
private static int GetBuildNumber(string osVersion)
{
var match = Regex.Match(osVersion ?? "", @"Build\s+(\d+)", RegexOptions.IgnoreCase);
return match.Success ? int.Parse(match.Groups[1].Value) : 0;
}
private static string GetServicePack(string osVersion)
{
var match = Regex.Match(osVersion ?? "", @"Service Pack\s+(\d+)", RegexOptions.IgnoreCase);
return match.Success ? match.Groups[1].Value : "";
}
private static string GetVersionFromBuild(int build)
{
string version = "";
foreach (var kv in BuildNumbers)
{
if (build == kv.Key)
{
return kv.Value;
}
if (build > kv.Key)
{
version = kv.Value;
continue;
}
break;
}
return version;
}
private static bool Contains(string input, string value)
{
return (input ?? "").IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
}
private static string GetValue(Dictionary<string, string> data, string key)
{
if (data == null || !data.TryGetValue(key, out var value))
{
return "";
}
return value ?? "";
}
private static WindowsVersionDefinitions LoadDefinitions()
{
if (_cachedDefinitions != null)
{
return _cachedDefinitions;
}
lock (_cacheLock)
{
if (_cachedDefinitions != null)
{
return _cachedDefinitions;
}
var assembly = Assembly.GetExecutingAssembly();
string resourceName = $"{assembly.GetName().Name}.{DefinitionsFileName}";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
return null;
}
using (var reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
var serializer = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
_cachedDefinitions = serializer.Deserialize<WindowsVersionDefinitions>(content);
}
}
}
return _cachedDefinitions;
}
}
internal class WindowsVersionVulnReport
{
public string DefinitionsDate { get; set; } = "";
public List<string> CandidateProducts { get; set; } = new List<string>();
public List<string> MatchedProducts { get; set; } = new List<string>();
public List<WindowsVersionVulnEntry> Vulnerabilities { get; set; } = new List<WindowsVersionVulnEntry>();
}
internal class WindowsVersionDefinitions
{
public string generated { get; set; }
public Dictionary<string, List<WindowsVersionVulnEntry>> products { get; set; }
}
internal class WindowsVersionVulnEntry
{
public string cve { get; set; }
public string kb { get; set; }
public string severity { get; set; }
public string impact { get; set; }
}
}

View File

@@ -1308,6 +1308,7 @@
<Compile Include="Info\SystemInfo\SysMon\SysmonHashAlgorithm.cs" />
<Compile Include="Info\SystemInfo\SysMon\SysmonInfo.cs" />
<Compile Include="Info\SystemInfo\SysMon\SysmonOptions.cs" />
<Compile Include="Info\SystemInfo\WindowsVersionVulns.cs" />
<Compile Include="Info\SystemInfo\WindowsDefender\AsrRule.cs" />
<Compile Include="Info\SystemInfo\WindowsDefender\AsrSettings.cs" />
<Compile Include="Info\SystemInfo\WindowsDefender\WindowsDefender.cs" />
@@ -1483,6 +1484,9 @@
<EmbeddedResource Include="..\..\..\build_lists\sensitive_files.yaml">
<Link>sensitive_files.yaml</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\..\..\build_lists\windows_version_exploits.json">
<Link>windows_version_exploits.json</Link>
</EmbeddedResource>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="TaskScheduler\V1\TaskSchedulerV1Schema.xsd">
@@ -1543,4 +1547,4 @@
<Import Project="..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.119.0\build\net451\Stub.System.Data.SQLite.Core.NetFramework.targets" Condition="Exists('..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.119.0\build\net451\Stub.System.Data.SQLite.Core.NetFramework.targets')" />
<Import Project="..\packages\Fody.6.5.5\build\Fody.targets" Condition="Exists('..\packages\Fody.6.5.5\build\Fody.targets')" />
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" />
</Project>
</Project>