403Webshell
Server IP : 156.238.232.47  /  Your IP : 216.73.216.150
Web Server : nginx/1.25.3
System : Linux C202504152095410 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64
User : www ( 1000)
PHP Version : 8.3.25
Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /www/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /www/clean_pbootcms.sh
#!/usr/bin/env bash
set -eo pipefail

SCRIPT_NAME="$(basename "$0")"
KEEP_CONFIG_FILES=("config.php" "database.php" "route.php")

usage() {
  cat <<EOF
用法:
  单站点:
    bash $SCRIPT_NAME --site /www/wwwroot/site1 --apps-zip /root/apps.zip --core-zip /root/core.zip

  多站点:
    bash $SCRIPT_NAME --scan-root /www/wwwroot --apps-zip /root/apps.zip --core-zip /root/core.zip

说明:
  - 多站点模式只扫描 scan-root 下一层目录
  - 执行前会统一列出待处理站点和待删除文件,输入 YES 才继续
EOF
}

die() {
  echo "[ERR] $*" >&2
  exit 1
}

log() {
  echo "[INFO] $*"
}

warn() {
  echo "[WARN] $*" >&2
}

safe_chmod() {
  local mode="$1"
  local path="$2"
  if [[ "$(basename "$path")" == ".user.ini" ]]; then
    return 0
  fi
  if ! chmod "$mode" "$path" 2>/dev/null; then
    warn "chmod $mode 失败,已跳过: $path"
  fi
}

resolve_path() {
  local p="$1"
  if command -v realpath >/dev/null 2>&1; then
    realpath "$p"
  elif command -v readlink >/dev/null 2>&1; then
    readlink -f "$p"
  else
    (cd "$(dirname "$p")" && printf '%s/%s\n' "$(pwd -P)" "$(basename "$p")")
  fi
}

check_prerequisites() {
  local cmd
  for cmd in unzip find chmod cp rm mkdir mktemp mv; do
    command -v "$cmd" >/dev/null 2>&1 || die "缺少必要命令: $cmd"
  done
}

is_pbootcms_site() {
  local site="$1"
  [[ -f "$site/apps/common/version.php" ]] &&
    [[ -f "$site/core/start.php" ]] &&
    [[ -f "$site/index.php" ]] &&
    [[ -f "$site/admin.php" ]]
}

normalize_extract_root() {
  local extracted="$1"
  local top_count
  local -a top_entries=()
  mapfile -t top_entries < <(find "$extracted" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort)
  top_count="${#top_entries[@]}"
  if [[ "$top_count" -eq 1 ]] && [[ -d "$extracted/${top_entries[0]}" ]]; then
    printf '%s\n' "$extracted/${top_entries[0]}"
  else
    printf '%s\n' "$extracted"
  fi
}

collect_php_files() {
  local site="$1"
  local -a paths=()
  local dir

  for dir in data rewrite static template skin; do
    if [[ -d "$site/$dir" ]]; then
      while IFS= read -r -d '' f; do
        paths+=("$f")
      done < <(find "$site/$dir" -type f \( -iname '*.php' -o -iname '*.phtml' -o -iname '*.php3' -o -iname '*.php4' -o -iname '*.php5' -o -iname '*.php7' -o -iname '*.phar' \) -print0)
    fi
  done

  if [[ -d "$site/config" ]]; then
    while IFS= read -r -d '' f; do
      local base
      base="$(basename "$f")"
      local keep=false
      local k
      for k in "${KEEP_CONFIG_FILES[@]}"; do
        if [[ "$base" == "$k" ]]; then
          keep=true
          break
        fi
      done
      if [[ "$keep" == false ]]; then
        paths+=("$f")
      fi
    done < <(find "$site/config" -maxdepth 1 -type f \( -iname '*.php' -o -iname '*.phtml' -o -iname '*.php3' -o -iname '*.php4' -o -iname '*.php5' -o -iname '*.php7' -o -iname '*.phar' \) -print0)
  fi

  if [[ "${#paths[@]}" -gt 0 ]]; then
    printf '%s\0' "${paths[@]}"
  fi
}

collect_sites_from_scanroot() {
  local root="$1"
  local -a sites=()

  if is_pbootcms_site "$root"; then
    sites+=("$root")
  fi

  while IFS= read -r -d '' d; do
    if is_pbootcms_site "$d"; then
      sites+=("$d")
    fi
  done < <(find "$root" -mindepth 1 -maxdepth 1 -type d -print0)

  if [[ "${#sites[@]}" -gt 0 ]]; then
    printf '%s\0' "${sites[@]}"
  fi
}

collect_sites() {
  local mode="$1"
  local target="$2"
  local -a sites=()

  if [[ "$mode" == "site" ]]; then
    is_pbootcms_site "$target" || die "目标目录不是 PbootCMS 站点: $target"
    sites+=("$target")
  else
    while IFS= read -r -d '' s; do
      sites+=("$s")
    done < <(collect_sites_from_scanroot "$target")
    [[ "${#sites[@]}" -gt 0 ]] || die "未在扫描目录中找到符合条件的 PbootCMS 站点: $target"
  fi

  if [[ "${#sites[@]}" -gt 0 ]]; then
    printf '%s\0' "${sites[@]}"
  fi
}

replace_tree_from_zip() {
  local site="$1"
  local zip_path="$2"
  local dest_dir="$3"
  local tmp_root="$4"
  local new_dir="$site/.${dest_dir}.clean.$$"

  [[ "$dest_dir" == "apps" || "$dest_dir" == "core" ]] || die "非法替换目录: $dest_dir"
  [[ -n "$site" && "$site" != "/" ]] || die "拒绝处理危险站点路径: $site"

  unzip -oq "$zip_path" -d "$tmp_root" >/dev/null

  local src_dir
  src_dir="$(normalize_extract_root "$tmp_root")"

  rm -rf "$new_dir"
  mkdir -p "$new_dir"
  cp -a "$src_dir"/. "$new_dir"/

  rm -rf "$site/$dest_dir"
  mv "$new_dir" "$site/$dest_dir"
}

cleanup_php_files() {
  local site="$1"
  local -a files=()
  local f

  while IFS= read -r -d '' f; do
    files+=("$f")
  done < <(collect_php_files "$site")

  for f in "${files[@]}"; do
    rm -f "$f"
  done
}

apply_permissions() {
  local site="$1"
  local path

  while IFS= read -r -d '' path; do
    safe_chmod 555 "$path"
  done < <(find "$site" -type d -print0)

  while IFS= read -r -d '' path; do
    safe_chmod 555 "$path"
  done < <(find "$site" -type f -print0)

  for dir in runtime static data; do
    if [[ -e "$site/$dir" ]]; then
      while IFS= read -r -d '' path; do
        safe_chmod 755 "$path"
      done < <(find "$site/$dir" -print0)
    fi
  done
}

print_plan() {
  local site="$1"
  local apps_zip="$2"
  local core_zip="$3"

  echo "站点: $site"
  echo "  替换: apps <- $apps_zip"
  echo "  替换: core <- $core_zip"
  echo "  删除:"

  local -a files=()
  local f
  while IFS= read -r -d '' f; do
    files+=("$f")
  done < <(collect_php_files "$site")

  if [[ "${#files[@]}" -eq 0 ]]; then
    echo "    (无)"
  else
    for f in "${files[@]}"; do
      echo "    $f"
    done
  fi
}

main() {
  local mode=""
  local site_arg=""
  local scan_root_arg=""
  local apps_zip_arg=""
  local core_zip_arg=""

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --site)
        [[ -z "$mode" ]] || die "不能同时指定 --site 和 --scan-root"
        [[ $# -ge 2 ]] || die "--site 缺少参数"
        mode="site"
        site_arg="${2:-}"
        shift 2
        ;;
      --scan-root)
        [[ -z "$mode" ]] || die "不能同时指定 --site 和 --scan-root"
        [[ $# -ge 2 ]] || die "--scan-root 缺少参数"
        mode="scan"
        scan_root_arg="${2:-}"
        shift 2
        ;;
      --apps-zip)
        [[ $# -ge 2 ]] || die "--apps-zip 缺少参数"
        apps_zip_arg="${2:-}"
        shift 2
        ;;
      --core-zip)
        [[ $# -ge 2 ]] || die "--core-zip 缺少参数"
        core_zip_arg="${2:-}"
        shift 2
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        die "未知参数: $1"
        ;;
    esac
  done

  [[ -n "$mode" ]] || { usage; die "请指定 --site 或 --scan-root"; }
  [[ -n "$apps_zip_arg" ]] || die "缺少 --apps-zip"
  [[ -n "$core_zip_arg" ]] || die "缺少 --core-zip"

  check_prerequisites

  [[ -f "$apps_zip_arg" ]] || die "apps.zip 不存在: $apps_zip_arg"
  [[ -f "$core_zip_arg" ]] || die "core.zip 不存在: $core_zip_arg"

  local apps_zip core_zip target_root
  apps_zip="$(resolve_path "$apps_zip_arg")"
  core_zip="$(resolve_path "$core_zip_arg")"

  unzip -tq "$apps_zip" >/dev/null || die "apps.zip 校验失败: $apps_zip"
  unzip -tq "$core_zip" >/dev/null || die "core.zip 校验失败: $core_zip"

  if [[ "$mode" == "site" ]]; then
    [[ -n "$site_arg" ]] || die "缺少 --site 参数"
    [[ -d "$site_arg" ]] || die "站点目录不存在: $site_arg"
    target_root="$(resolve_path "$site_arg")"
  else
    [[ -n "$scan_root_arg" ]] || die "缺少 --scan-root 参数"
    [[ -d "$scan_root_arg" ]] || die "扫描目录不存在: $scan_root_arg"
    target_root="$(resolve_path "$scan_root_arg")"
  fi

  local -a sites=()
  while IFS= read -r -d '' s; do
    sites+=("$s")
  done < <(collect_sites "$mode" "$target_root")

  log "待处理站点数量: ${#sites[@]}"
  echo
  for site in "${sites[@]}"; do
    print_plan "$site" "$apps_zip" "$core_zip"
    echo
  done

  read -r -p "确认开始清理并替换吗?输入 YES 继续: " confirm
  [[ "$confirm" == "YES" ]] || { warn "已取消"; exit 0; }

  local site tmp_root apps_tmp core_tmp
  for site in "${sites[@]}"; do
    log "开始处理: $site"
    tmp_root="$(mktemp -d)"
    apps_tmp="$tmp_root/apps"
    core_tmp="$tmp_root/core"
    mkdir -p "$apps_tmp" "$core_tmp"

    replace_tree_from_zip "$site" "$apps_zip" "apps" "$apps_tmp"
    replace_tree_from_zip "$site" "$core_zip" "core" "$core_tmp"
    cleanup_php_files "$site"
    apply_permissions "$site"

    rm -rf "$tmp_root"
    log "完成: $site"
  done

  log "全部处理完成"
}

main "$@"

Youez - 2016 - github.com/yon3zu
LinuXploit