T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/bootstrap_restic_home.sh:97
- Finding
- Root Command Injection Through Shell-Sourced Configuration Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bootstrap_restic_home.sh:29-32, 97-102, 108-130, 136-198, 214-215` **Vulnerability Type**: Shell command injection through unsafe configuration generation **Risk Level**: High ### Vulnerable Code User-controlled arguments are accepted without validation: ```bash --user) USER_NAME="$2"; shift 2 ;; --repo) REPO="$2"; shift 2 ;; --password-file) PASS_FILE="$2"; shift 2 ;; --timezone) TIMEZONE="$2"; shift 2 ;; ``` The values are written verbatim into a file that is subsequently interpreted as shell code: ```bash cat >/etc/restic-home.env <<EOF RESTIC_REPOSITORY=${REPO} RESTIC_PASSWORD_FILE=${PASS_FILE} BACKUP_SOURCE=${HOME_DIR} EXCLUDES_FILE=/etc/restic-home/excludes.txt EOF chmod 600 /etc/restic-home.env ``` Every generated operational script sources that file: ```bash cat >/usr/local/bin/restic-home-backup.sh <<'EOF' #!/usr/bin/env bash set -euo pipefail source /etc/restic-home.env exec /usr/bin/restic backup "$BACKUP_SOURCE" --exclude-file "$EXCLUDES_FILE" EOF chmod 755 /usr/local/bin/restic-home-backup.sh cat >/usr/local/bin/restic-home-prune.sh <<'EOF' #!/usr/bin/env bash set -euo pipefail source /etc/restic-home.env exec /usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune EOF chmod 755 /usr/local/bin/restic-home-prune.sh cat >/usr/local/bin/restic-home-check.sh <<'EOF' #!/usr/bin/env bash set -euo pipefail source /etc/restic-home.env exec /usr/bin/restic check EOF chmod 755 /usr/local/bin/restic-home-check.sh ``` Repository initialization also directly sources the generated file: ```bash if [[ "$INIT_REPO" == "yes" ]]; then source /etc/restic-home.env if ! /usr/bin/restic snapshots >/dev/null 2>&1; then /usr/bin/restic init fi fi ``` ### Technical Analysis `REPO` and `PASS_FILE` are attacker-influenced command-line values. They are inserted into `/etc/restic-home.env` without shell escaping, quoting, newline rejection, or format validation. Becau ...[truncated 3160 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Do not source data files as shell code.** Replace `/etc/restic-home.env` with a configuration format parsed as data. For example, place fixed environment assignments directly in systemd unit definitions using properly escaped values, or use a dedicated parser that rejects shell syntax. 2. **Apply strict input validation.** - Reject newlines, carriage returns, NUL bytes, command substitutions, and shell control characters. - Validate repository values against explicitly supported restic repository formats and schemes. - Require `PASS_FILE` to be an absolute path under an approved directory such as `/etc/restic-home/`. - Validate `USER_NAME` using the operating system account database rather than constructing `/home/${USER_NAME}` directly. - Validate `TIMEZONE` against installed IANA timezone identifiers. 3. **If shell assignments cannot be avoided, serialize values safely.** Generate assignments with Bash-safe escaping, such as: ```bash { printf 'RESTIC_REPOSITORY=%q\n' "$REPO" printf 'RESTIC_PASSWORD_FILE=%q\n' "$PASS_FILE" printf 'BACKUP_SOURCE=%q\n' "$HOME_DIR" printf 'EXCLUDES_FILE=%q\n' '/etc/restic-home/excludes.txt' } > /etc/restic-home.env ``` Strict validation should still be performed before serialization. 4. **Remove duplicate shell interpretation.** The systemd units already specify `EnvironmentFile=/etc/restic-home.env`; generated scripts should not source the same file. Prefer fixed executables that consume environment variables supplied by systemd. 5. **Reduce service privileges.** Run backup operations under a dedicated restricted account where repository and source permissions permit. If root access is required to read the entire home directory, add systemd hardening controls appropriate for restic, including: - `NoNewPrivileges=true` - `PrivateTmp=true` - `ProtectSystem=strict` - Explicit `ReadOnlyPaths=` for backup sources - Explicit `R ...[truncated 526 chars]
