|
| 1 | +# Title: Processes & Cron & Services & Timers - Crontab UI (root) Misconfiguration |
| 2 | +# ID: PR_Crontab_UI_misconfig |
| 3 | +# Author: HT Bot |
| 4 | +# Last Update: 2025-09-13 |
| 5 | +# Description: Detect Crontab UI service and risky configurations that can lead to privesc: |
| 6 | +# - Root-run Crontab UI exposed on localhost |
| 7 | +# - Basic-Auth credentials in systemd Environment= (BASIC_AUTH_USER/PWD) |
| 8 | +# - Cron DB path (CRON_DB_PATH) and weak permissions / embedded secrets in jobs |
| 9 | +# License: GNU GPL |
| 10 | +# Version: 1.0 |
| 11 | +# Functions Used: print_2title, print_info, print_list, echo_not_found |
| 12 | +# Global Variables: $SEARCH_IN_FOLDER, $SED_RED, $SED_RED_YELLOW, $NC |
| 13 | +# Initial Functions: |
| 14 | +# Generated Global Variables: $svc, $state, $user, $envvals, $port, $dbpath, $dbfile, $candidates, $procs, $perms, $basic_user, $basic_pwd, $uprint, $pprint, $dir, $found |
| 15 | +# Fat linpeas: 0 |
| 16 | +# Small linpeas: 1 |
| 17 | + |
| 18 | +if ! [ "$SEARCH_IN_FOLDER" ]; then |
| 19 | + print_2title "Crontab UI (root) misconfiguration checks" |
| 20 | + print_info "https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#scheduledcron-jobs" |
| 21 | + |
| 22 | + # Collect candidate services referencing crontab-ui |
| 23 | + candidates="" |
| 24 | + if command -v systemctl >/dev/null 2>&1; then |
| 25 | + candidates=$(systemctl list-units --type=service --all 2>/dev/null | awk '{print $1}' | grep -Ei '^crontab-ui\.service$' 2>/dev/null) |
| 26 | + fi |
| 27 | + |
| 28 | + # Fallback: grep service files for ExecStart containing crontab-ui |
| 29 | + if [ -z "$candidates" ]; then |
| 30 | + for dir in /etc/systemd/system /lib/systemd/system; do |
| 31 | + [ -d "$dir" ] || continue |
| 32 | + found=$(grep -RIl "^Exec(Start|StartPre|StartPost)=.*crontab-ui" "$dir" 2>/dev/null | xargs -r -I{} basename {} 2>/dev/null) |
| 33 | + if [ -n "$found" ]; then |
| 34 | + candidates=$(printf "%s\n%s" "$candidates" "$found" | sort -u) |
| 35 | + fi |
| 36 | + done |
| 37 | + fi |
| 38 | + |
| 39 | + # Also flag if the binary exists or a process seems to be running |
| 40 | + if command -v crontab-ui >/dev/null 2>&1; then |
| 41 | + print_list "crontab-ui binary found at: $(command -v crontab-ui)"$NC |
| 42 | + else |
| 43 | + echo_not_found "crontab-ui" |
| 44 | + fi |
| 45 | + |
| 46 | + procs=$(ps aux 2>/dev/null | grep -E "(crontab-ui|node .*crontab-ui)" | grep -v grep) |
| 47 | + if [ -n "$procs" ]; then |
| 48 | + print_list "Processes matching crontab-ui? ..................... "$NC |
| 49 | + printf "%s\n" "$procs" |
| 50 | + echo "" |
| 51 | + fi |
| 52 | + |
| 53 | + # If no candidates detected, exit quietly |
| 54 | + if [ -z "$candidates" ]; then |
| 55 | + exit 0 |
| 56 | + fi |
| 57 | + |
| 58 | + # Iterate candidates and extract interesting data |
| 59 | + printf "%s\n" "$candidates" | while read -r svc; do |
| 60 | + [ -n "$svc" ] || continue |
| 61 | + # Ensure suffix .service if missing |
| 62 | + case "$svc" in |
| 63 | + *.service) : ;; |
| 64 | + *) svc="$svc.service" ;; |
| 65 | + esac |
| 66 | + |
| 67 | + state="" |
| 68 | + user="" |
| 69 | + if command -v systemctl >/dev/null 2>&1; then |
| 70 | + state=$(systemctl is-active "$svc" 2>/dev/null) |
| 71 | + user=$(systemctl show "$svc" -p User 2>/dev/null | cut -d= -f2) |
| 72 | + fi |
| 73 | + |
| 74 | + [ -z "$state" ] && state="unknown" |
| 75 | + [ -z "$user" ] && user="unknown" |
| 76 | + |
| 77 | + echo "Service: $svc (state: $state, User: $user)" | sed -${E} "s,root,${SED_RED},g" |
| 78 | + |
| 79 | + # Read Environment from systemd (works even if file unreadable in many setups) |
| 80 | + envvals=$(systemctl show "$svc" -p Environment 2>/dev/null | cut -d= -f2-) |
| 81 | + if [ -n "$envvals" ]; then |
| 82 | + basic_user=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^BASIC_AUTH_USER=' | head -n1 | cut -d= -f2-) |
| 83 | + basic_pwd=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^BASIC_AUTH_PWD=' | head -n1 | cut -d= -f2-) |
| 84 | + dbpath=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^CRON_DB_PATH=' | head -n1 | cut -d= -f2-) |
| 85 | + port=$(printf "%s\n" "$envvals" | tr ' ' '\n' | grep -E '^PORT=' | head -n1 | cut -d= -f2-) |
| 86 | + |
| 87 | + if [ -n "$basic_user" ] || [ -n "$basic_pwd" ]; then |
| 88 | + uprint="$basic_user" |
| 89 | + pprint="$basic_pwd" |
| 90 | + [ -n "$basic_pwd" ] && pprint="$basic_pwd" |
| 91 | + echo " └─ Basic-Auth credentials in Environment: user='${uprint}' pwd='${pprint}'" | sed -${E} "s,pwd='[^']*',${SED_RED_YELLOW},g" |
| 92 | + fi |
| 93 | + |
| 94 | + if [ -n "$dbpath" ]; then |
| 95 | + echo " └─ CRON_DB_PATH: $dbpath" |
| 96 | + fi |
| 97 | + |
| 98 | + # Check listener bound to localhost |
| 99 | + [ -z "$port" ] && port=8000 |
| 100 | + if command -v ss >/dev/null 2>&1; then |
| 101 | + if ss -ltn 2>/dev/null | grep -qE "127\.0\.0\.1:${port}[[:space:]]"; then |
| 102 | + echo " └─ Listener detected on 127.0.0.1:${port} (likely Crontab UI)." |
| 103 | + fi |
| 104 | + else |
| 105 | + if netstat -tnl 2>/dev/null | grep -qE "127\.0\.0\.1:${port}[[:space:]]"; then |
| 106 | + echo " └─ Listener detected on 127.0.0.1:${port} (likely Crontab UI)." |
| 107 | + fi |
| 108 | + fi |
| 109 | + |
| 110 | + # If we know DB path, try to read crontab.db for obvious secrets and check perms |
| 111 | + if [ -n "$dbpath" ] && [ -d "$dbpath" ] && [ -r "$dbpath" ]; then |
| 112 | + dbfile="$dbpath/crontab.db" |
| 113 | + if [ -f "$dbfile" ]; then |
| 114 | + perms=$(ls -ld "$dbpath" 2>/dev/null | awk '{print $1, $3, $4}') |
| 115 | + echo " └─ DB dir perms: $perms" |
| 116 | + if [ -w "$dbpath" ] || [ -w "$dbfile" ]; then |
| 117 | + echo " └─ Writable by current user -> potential job injection!" | sed -${E} "s,.*,${SED_RED},g" |
| 118 | + fi |
| 119 | + echo " └─ Inspecting $dbfile for embedded secrets in commands (zip -P / --password / pass/token/secret)..." |
| 120 | + 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" |
| 121 | + fi |
| 122 | + fi |
| 123 | + fi |
| 124 | + echo "" |
| 125 | + done |
| 126 | +fi |
| 127 | + |
0 commit comments