Compare commits

...

7 Commits

Author SHA1 Message Date
SirBroccoli
be3fe91da4 Merge pull request #507 from CravateRouge/master
Add ADCS ESC DC registry checks
2025-10-07 10:50:43 +02:00
CravateRouge
b8b4a0fc14 Fix InterfaceFlags syntax 2025-10-07 11:14:45 +08:00
CravateRouge
7042a182df Add ADCS ESC DC registry checks 2025-10-06 17:18:44 +02:00
SirBroccoli
c83eef9cd8 Merge pull request #502 from peass-ng/update_PEASS-linpeas-HTB_Planning__Grafana_CVE-2024-9264__20250913_182726
[LINPEAS] Add privilege escalation check: HTB Planning Grafana CVE-2024-9264 to Co...
2025-10-04 10:38:22 +02:00
SirBroccoli
e15a1f2e12 Update 16_Crontab_UI_misconfig.sh 2025-10-04 10:38:02 +02:00
SirBroccoli
ee83c23a74 Update 16_Crontab_UI_misconfig.sh 2025-10-04 10:34:04 +02:00
HackTricks News Bot
bdcebadde0 Add linpeas privilege escalation checks from: HTB Planning: Grafana CVE-2024-9264 to Container Root, Env-Creds Pivot, Crontab 2025-09-13 18:33:45 +00:00
3 changed files with 227 additions and 8 deletions

View File

@@ -3954,3 +3954,24 @@ search:
type: f
search_in:
- common
- name: Crontab-UI
value:
config:
auto_check: True
files:
- name: "crontab.db"
value:
bad_regex: "-P[[:space:]]+\\S+|--password[[:space:]]+\\S+|[Pp]ass(word)?|[Tt]oken|[Ss]ecret"
only_bad_lines: True
type: f
search_in:
- common
- name: "crontab-ui.service"
value:
just_list_file: True
type: f
search_in:
- common

View File

@@ -0,0 +1,127 @@
# Title: Processes & Cron & Services & Timers - Crontab UI (root) Misconfiguration
# ID: PR_Crontab_UI_misconfig
# Author: HT Bot
# Last Update: 2025-09-13
# Description: Detect Crontab UI service and risky configurations that can lead to privesc:
# - Root-run Crontab UI exposed on localhost
# - Basic-Auth credentials in systemd Environment= (BASIC_AUTH_USER/PWD)
# - Cron DB path (CRON_DB_PATH) and weak permissions / embedded secrets in jobs
# License: GNU GPL
# Version: 1.0
# Functions Used: print_2title, print_info, print_list, echo_not_found
# Global Variables: $SEARCH_IN_FOLDER, $SED_RED, $SED_RED_YELLOW, $NC
# Initial Functions:
# Generated Global Variables: $svc, $state, $user, $envvals, $port, $dbpath, $dbfile, $candidates, $procs, $perms, $basic_user, $basic_pwd, $uprint, $pprint, $dir, $found
# Fat linpeas: 0
# Small linpeas: 1
if ! [ "$SEARCH_IN_FOLDER" ]; then
print_2title "Crontab UI (root) misconfiguration checks"
print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#scheduledcron-jobs"
# Collect candidate services referencing crontab-ui
candidates=""
if command -v systemctl >/dev/null 2>&1; then
candidates=$(systemctl list-units --type=service --all 2>/dev/null | awk '{print $1}' | grep -Ei '^crontab-ui\.service$' 2>/dev/null)
fi
# Fallback: grep service files for ExecStart containing crontab-ui
if [ -z "$candidates" ]; then
for dir in /etc/systemd/system /lib/systemd/system; do
[ -d "$dir" ] || continue
found=$(grep -RIl "^Exec(Start|StartPre|StartPost)=.*crontab-ui" "$dir" 2>/dev/null | xargs -r -I{} basename {} 2>/dev/null)
if [ -n "$found" ]; then
candidates=$(printf "%s\n%s" "$candidates" "$found" | sort -u)
fi
done
fi
# Also flag if the binary exists or a process seems to be running
if command -v crontab-ui >/dev/null 2>&1; then
print_list "crontab-ui binary found at: $(command -v crontab-ui)"$NC
else
echo_not_found "crontab-ui"
fi
procs=$(ps aux 2>/dev/null | grep -E "(crontab-ui|node .*crontab-ui)" | grep -v grep)
if [ -n "$procs" ]; then
print_list "Processes matching crontab-ui? ..................... "$NC
printf "%s\n" "$procs"
echo ""
fi
# If no candidates detected, exit quietly
if [ -z "$candidates" ]; then
exit 0
fi
# Iterate candidates and extract interesting data
printf "%s\n" "$candidates" | while read -r svc; do
[ -n "$svc" ] || continue
# Ensure suffix .service if missing
case "$svc" in
*.service) : ;;
*) svc="$svc.service" ;;
esac
state=""
user=""
if command -v systemctl >/dev/null 2>&1; then
state=$(systemctl is-active "$svc" 2>/dev/null)
user=$(systemctl show "$svc" -p User 2>/dev/null | cut -d= -f2)
fi
[ -z "$state" ] && state="unknown"
[ -z "$user" ] && user="unknown"
echo "Service: $svc (state: $state, User: $user)" | sed -${E} "s,root,${SED_RED},g"
# Read Environment from systemd (works even if file unreadable in many setups)
envvals=$(systemctl show "$svc" -p Environment 2>/dev/null | cut -d= -f2-)
if [ -n "$envvals" ]; then
basic_user=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^BASIC_AUTH_USER=' | head -n1 | cut -d= -f2-)
basic_pwd=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^BASIC_AUTH_PWD=' | head -n1 | cut -d= -f2-)
dbpath=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^CRON_DB_PATH=' | head -n1 | cut -d= -f2-)
port=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^PORT=' | head -n1 | cut -d= -f2-)
if [ -n "$basic_user" ] || [ -n "$basic_pwd" ]; then
uprint="$basic_user"
pprint="$basic_pwd"
[ -n "$basic_pwd" ] && pprint="$basic_pwd"
echo " └─ Basic-Auth credentials in Environment: user='${uprint}' pwd='${pprint}'" | sed -${E} "s,pwd='[^']*',${SED_RED_YELLOW},g"
fi
if [ -n "$dbpath" ]; then
echo " └─ CRON_DB_PATH: $dbpath"
fi
# Check listener bound to localhost
[ -z "$port" ] && port=8000
if command -v ss >/dev/null 2>&1; then
if ss -ltn 2>/dev/null | grep -qE "127\.0\.0\.1:${port}[[:space:]]"; then
echo " └─ Listener detected on 127.0.0.1:${port} (likely Crontab UI)."
fi
else
if netstat -tnl 2>/dev/null | grep -qE "127\.0\.0\.1:${port}[[:space:]]"; then
echo " └─ Listener detected on 127.0.0.1:${port} (likely Crontab UI)."
fi
fi
# If we know DB path, try to read crontab.db for obvious secrets and check perms
if [ -n "$dbpath" ] && [ -d "$dbpath" ] && [ -r "$dbpath" ]; then
dbfile="$dbpath/crontab.db"
if [ -f "$dbfile" ]; then
perms=$(ls -ld "$dbpath" 2>/dev/null | awk '{print $1, $3, $4}')
echo " └─ DB dir perms: $perms"
if [ -w "$dbpath" ] || [ -w "$dbfile" ]; then
echo " └─ Writable by current user -> potential job injection!" | sed -${E} "s,.*,${SED_RED},g"
fi
echo " └─ Inspecting $dbfile for embedded secrets in commands (zip -P / --password / pass/token/secret)..."
grep -E "-P[[:space:]]+\S+|--password[[:space:]]+\S+|[Pp]ass(word)?|[Tt]oken|[Ss]ecret" "$dbfile" 2>/dev/null | head -n 20 | sed -${E} "s,(${SED_RED_YELLOW}),\1,g"
fi
fi
fi
echo ""
done
fi

View File

@@ -4,6 +4,8 @@ using System.DirectoryServices;
using System.Security.AccessControl;
using System.Security.Principal;
using winPEAS.Helpers;
using winPEAS.Helpers.Registry;
using winPEAS.Info.FilesInfo.Certificates;
namespace winPEAS.Checks
{
@@ -17,7 +19,7 @@ namespace winPEAS.Checks
new List<Action>
{
PrintGmsaReadableByCurrentPrincipal,
PrintAdcsEsc4LikeTemplates
PrintAdcsMisconfigurations
}.ForEach(action => CheckRunner.Run(action, isDebug));
}
@@ -152,22 +154,91 @@ namespace winPEAS.Checks
}
}
// Detect AD CS certificate templates where current principal has dangerous control rights (ESC4-style)
private void PrintAdcsEsc4LikeTemplates()
// Detect AD CS misconfigurations
private void PrintAdcsMisconfigurations()
{
try
{
Beaprint.MainPrint("AD CS templates with dangerous ACEs (ESC4)");
Beaprint.LinkPrint(
"https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/ad-certificates.html#esc4",
"If you can modify a template (WriteDacl/WriteOwner/GenericAll), you can abuse ESC4");
Beaprint.MainPrint("AD CS misconfigurations for ESC");
Beaprint.LinkPrint("https://book.hacktricks.wiki/en/windows-hardening/active-directory-methodology/ad-certificates.html");
if (!Checks.IsPartOfDomain)
{
Beaprint.GrayPrint(" [-] Host is not domain-joined. Skipping.");
return;
}
Beaprint.InfoPrint("Check for ADCS misconfigurations in the local DC registry");
bool IsDomainController = RegistryHelper.GetReg("HKLM", @"SYSTEM\CurrentControlSet\Services\NTDS")?.ValueCount > 0;
if (IsDomainController)
{
// For StrongBinding and CertificateMapping, More details in KB014754 - Registry key information:
// https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16
uint? strongBinding = RegistryHelper.GetDwordValue("HKLM", @"SYSTEM\CurrentControlSet\Services\Kdc", "StrongCertificateBindingEnforcement");
switch (strongBinding)
{
case 0:
Beaprint.BadPrint(" StrongCertificateBindingEnforcement: 0 — Weak mapping allowed, vulnerable to ESC9.");
break;
case 2:
Beaprint.GoodPrint(" StrongCertificateBindingEnforcement: 2 — Prevents weak UPN/DNS mappings even if SID extension missing, not vulnerable to ESC9.");
break;
// 1 is default behavior now I think?
case 1:
default:
Beaprint.NoColorPrint($" StrongCertificateBindingEnforcement: {strongBinding} — Allow weak mapping if SID extension missing, may be vulnerable to ESC9.");
break;
}
uint? certMapping = RegistryHelper.GetDwordValue("HKLM", @"SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL", "CertificateMappingMethods");
if (certMapping.HasValue && (certMapping & 0x4) != 0)
Beaprint.BadPrint($" CertificateMappingMethods: {certMapping} — Allow UPN-based mapping, vulnerable to ESC10.");
else if(certMapping.HasValue && ((certMapping & 0x1) != 0 || (certMapping & 0x2) != 0))
Beaprint.NoColorPrint($" CertificateMappingMethods: {certMapping} — Allow weak Subject/Issuer certificate mapping.");
// 0x18 (strong mapping) is default behavior if not the flags above I think?
else
Beaprint.GoodPrint($" CertificateMappingMethods: {certMapping} — Strong Certificate mapping enabled.");
// We take the Active CA, can they be several?
string caName = RegistryHelper.GetRegValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration", "Active");
if (!string.IsNullOrWhiteSpace(caName))
{
// Obscure Source for InterfaceFlag Enum:
// https://www.sysadmins.lv/apidocs/pki/html/T_PKI_CertificateServices_Flags_InterfaceFlagEnum.htm
uint? interfaceFlags = RegistryHelper.GetDwordValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\{caName}", "InterfaceFlags");
if (!interfaceFlags.HasValue || (interfaceFlags & 512) == 0)
Beaprint.BadPrint(" IF_ENFORCEENCRYPTICERTREQUEST not set in InterfaceFlags — vulnerable to ESC11.");
else
Beaprint.GoodPrint(" IF_ENFORCEENCRYPTICERTREQUEST set in InterfaceFlags — not vulnerable to ESC11.");
string policyModule = RegistryHelper.GetRegValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\{caName}\PolicyModules", "Active");
if (!string.IsNullOrWhiteSpace(policyModule))
{
string disableExtensionList = RegistryHelper.GetRegValue("HKLM", $@"SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\{caName}\PolicyModules\{policyModule}", "DisableExtensionList");
// zOID_NTDS_CA_SECURITY_EXT (OID 1.3.6.1.4.1.311.25.2)
if (disableExtensionList?.Contains("1.3.6.1.4.1.311.25.2") == true)
Beaprint.BadPrint(" szOID_NTDS_CA_SECURITY_EXT disabled for the entire CA — vulnerable to ESC16.");
else
Beaprint.GoodPrint(" szOID_NTDS_CA_SECURITY_EXT not disabled for the CA — not vulnerable to ESC16.");
}
else
{
Beaprint.GrayPrint(" [-] Policy Module not found. Skipping.");
}
}
else
{
Beaprint.GrayPrint(" [-] Certificate Authority not found. Skipping.");
}
}
else
{
Beaprint.GrayPrint(" [-] Host is not a domain controller. Skipping ADCS Registry check");
}
// Detect AD CS certificate templates where current principal has dangerous control rights(ESC4 - style)
Beaprint.InfoPrint("\nIf you can modify a template (WriteDacl/WriteOwner/GenericAll), you can abuse ESC4");
var configNC = GetRootDseProp("configurationNamingContext");
if (string.IsNullOrEmpty(configNC))
{