T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- scripts/git_sync_ctl.sh:249
- Finding
- Persistent systemd service runs a user-modifiable script as root<![CDATA[ ## Vulnerability Details **File Location**: `scripts/git_sync_ctl.sh`, lines 249-285 **Vulnerability Type**: `T05: Unauthorized Access and Privilege Escalation` **Risk Level**: High ### Vulnerable Code ```bash if [[ "$(id -u)" -ne 0 ]]; then echo "install-systemd requires root" >&2 exit 1 fi local unit_path unit_path="/etc/systemd/system/${SYSTEMD_UNIT}" cat > "$unit_path" <<EOF [Unit] Description=Git Sync Daemon After=network-online.target Wants=network-online.target [Service] Type=simple User=$(id -un) Environment=GIT_SYNC_INTERVAL=${INTERVAL} Environment=GIT_SYNC_REPO_FILE=${REPO_FILE} Environment=GIT_SYNC_LOG_FILE=${LOG_FILE} Environment=GIT_SYNC_GIT_TIMEOUT=${TIMEOUT_SECS} ExecStart=/bin/bash ${DAEMON_SCRIPT} Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now "$SYSTEMD_UNIT" ``` ### Technical Analysis The installation command requires execution as root. Consequently, `User=$(id -un)` resolves to `User=root` in the generated systemd unit. The service therefore performs all daemon operations with unrestricted root privileges. The `ExecStart` directive references `git_sync_daemon.sh` directly in the original project directory rather than copying it to a protected, root-owned installation directory. If an unprivileged user can modify that directory or script after installation, systemd will execute attacker-controlled shell code as root on the next service start, restart, or system reboot. The service uses `Restart=always` and is enabled for `multi-user.target`, making the privileged execution persistent. The daemon also invokes Git operations in registered repositories. Git hooks and related helpers reached during those operations inherit the service's root identity, expanding the trust boundary from the daemon script to repository-controlled executable content. Root privileges are not necessary for ordinary Git synchronization. This implementation there ...[truncated 1972 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Run the daemon as a dedicated unprivileged account** - Require an explicit service-user parameter. - Set `User` and `Group` to that account rather than deriving them from the root installer. - Reject `root` as the runtime identity unless the operator explicitly acknowledges a documented exceptional requirement. 2. **Install executable files into a protected location** - Copy the daemon to a root-owned location such as `/usr/local/libexec/git-sync-daemon/`. - Set ownership to `root:root`. - Remove group and world write permissions. - Ensure every parent directory in the executable path is not writable by the service user or other unprivileged users. 3. **Restrict filesystem access** - Grant the service account access only to the configured repositories, state directory, credential material, and log destination. - Do not expose root's home directory, SSH keys, or global Git configuration to the daemon. - Apply suitable systemd controls such as: ```ini NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true PrivateDevices=true RestrictSUIDSGID=true LockPersonality=true ReadWritePaths=/explicit/state/path /explicit/repository/path ``` 4. **Constrain repository-controlled execution** - Treat registered repositories as trusted executable content because Git hooks may run during synchronization. - Consider disabling hooks for unattended synchronization with a controlled `core.hooksPath`, where compatible with intended behavior. - Never synchronize repositories controlled by less-trusted users from a more-privileged service account. 5. **Use a safer installation workflow** - Have the privileged installation phase only create protected files and register the unit. - Run repository operations under the same unprivileged identity that owns the repositories and corresponding credentials. - Validate ownership and permissions of the daemon, its parent directories, re ...[truncated 342 chars]
