用途限制声明,本文仅用于网络安全技术研究、教育与知识分享。文中涉及的渗透测试方法与工具,严禁用于未经授权的网络攻击、数据窃取或任何违法活动。任何因不当使用本文内容导致的法律后果,作者及发布平台不承担任何责任。渗透测试涉及复杂技术操作,可能对目标系统造成数据损坏、服务中断等风险。读者需充分评估技术能力与潜在后果,在合法合规前提下谨慎实践。

这次我们使用python来编写一个跨平台的批量用户密码修改工具,支持 Windows 和 Linux 系统。它能够根据配置批量修改符合条件的用户密码,同时具备权限检查、用户信息备份、操作日志记录和模拟运行(不实际修改密码)等功能,适用于系统管理员批量管理用户密码的场景。

import platform
import logging
import subprocess
import ctypes
import os
from typing import List, Dict, Optional

# 配置日志:记录操作详情(成功/失败/时间),支持文件和控制台输出
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("password_change.log"),  # 日志文件备份
        logging.StreamHandler()  # 控制台输出
    ]
)
logger = logging.getLogger(__name__)


class PasswordChanger:
    def __init__(self, config: Dict):
        """初始化配置参数"""
        self.target_users = config.get("target_users", [])  # 目标用户列表(支持正则)
        self.new_password = config.get("new_password")  # 新密码
        self.backup_enabled = config.get("backup_enabled", True)  # 是否备份用户信息
        self.dry_run = config.get("dry_run", False)  # 模拟运行(不实际修改)
        self._validate_config()  # 校验配置合法性

    def _validate_config(self) -> None:
        """校验配置参数合法性"""
        if not self.target_users:
            raise ValueError("目标用户列表不能为空,请配置target_users")
        if not self.new_password:
            raise ValueError("新密码不能为空,请配置new_password")
        if len(self.new_password) < 8:
            logger.warning("密码长度小于8位,可能不符合安全策略")

    def _is_admin(self) -> bool:
        """检查当前用户是否有管理员/root权限(修改密码必需)"""
        try:
            if platform.system() == "Windows":
                return ctypes.windll.shell32.IsUserAnAdmin() != 0
            else:  # Linux/Unix
                return os.geteuid() == 0
        except Exception as e:
            logger.error(f"权限检查失败: {str(e)}")
            return False

    def _backup_user_info(self, username: str) -> None:
        """备份用户信息(用于回滚)"""
        if not self.backup_enabled:
            return
        try:
            if platform.system() == "Windows":
                # Windows: 备份用户基本信息到文件
                with open(f"user_backup_{username}.txt", "w") as f:
                    f.write(f"Backup for {username} at {platform.system()}\n")
                    # 可扩展:通过WMI获取更多用户属性(如SID、创建时间等)
            else:
                # Linux: 备份/etc/shadow中该用户的记录
                with open("/etc/shadow", "r") as shadow, \
                     open(f"shadow_backup_{username}.txt", "w") as f:
                    for line in shadow:
                        if line.startswith(f"{username}:"):
                            f.write(line)
                            break
            logger.info(f"已备份用户 {username} 信息")
        except Exception as e:
            logger.warning(f"备份用户 {username} 信息失败: {str(e)}")

    def _match_user(self, username: str) -> bool:
        """判断用户是否符合修改条件(支持精确匹配和正则)"""
        import re
        for pattern in self.target_users:
            if re.fullmatch(pattern, username):  # 支持正则表达式匹配
                return True
        return False

    def set_windows_password(self, username: str) -> bool:
        """Windows系统修改密码(优化错误处理)"""
        try:
            from win32com import adsi
            from pywintypes import com_error  # 捕获ADSI相关异常

            ads_path = f"WinNT://localhost/{username},user"
            ads_obj = adsi.ADsGetObject(ads_path)
            ads_obj.Getinfo()
            if not self.dry_run:
                ads_obj.SetPassword(self.new_password)
            logger.info(f"[Windows] 用户 {username} 密码修改成功({'模拟' if self.dry_run else '实际'}操作)")
            return True
        except com_error as e:
            error_msg = f"[Windows] 用户 {username} 密码修改失败: {e.excepinfo[2]}"
            logger.error(error_msg)
            return False
        except Exception as e:
            logger.error(f"[Windows] 处理用户 {username} 时发生未知错误: {str(e)}")
            return False

    def set_linux_password(self, username: str) -> bool:
        """Linux系统修改密码(使用subprocess增强安全性,避免shell注入)"""
        try:
            # 构造密码输入(两次输入新密码)
            input_data = f"{self.new_password}\n{self.new_password}\n".encode()
            # 使用subprocess避免os.system的安全风险,不启用shell
            result = subprocess.run(
                ["passwd", username],  # 命令参数列表化,防止注入
                input=input_data,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=False  # 二进制输入输出
            )
            if result.returncode == 0:
                logger.info(f"[Linux] 用户 {username} 密码修改成功({'模拟' if self.dry_run else '实际'}操作)")
                return True
            else:
                error_msg = result.stderr.decode(errors="ignore")
                logger.error(f"[Linux] 用户 {username} 密码修改失败: {error_msg}")
                return False
        except Exception as e:
            logger.error(f"[Linux] 处理用户 {username} 时发生未知错误: {str(e)}")
            return False

    def run(self) -> None:
        """主执行逻辑"""
        # 权限检查
        if not self._is_admin():
            logger.error("错误:修改用户密码需要管理员/root权限,请以管理员身份运行")
            return

        # 按系统类型处理
        if platform.system() == "Windows":
            self._process_windows_users()
        else:
            self._process_linux_users()

    def _process_windows_users(self) -> None:
        """处理Windows用户"""
        try:
            import wmi
            w = wmi.WMI()
            for user in w.Win32_UserAccount():
                username = user.Name
                if self._match_user(username):
                    logger.info(f"发现符合条件的用户: {username}")
                    self._backup_user_info(username)
                    self.set_windows_password(username)
        except ImportError:
            logger.error("处理Windows用户失败:请安装wmi模块(pip install wmi)")
        except Exception as e:
            logger.error(f"Windows用户处理逻辑出错: {str(e)}")

    def _process_linux_users(self) -> None:
        """处理Linux用户(优化用户筛选逻辑)"""
        try:
            import pwd
            # 筛选有效用户(排除系统用户,可配置uid范围)
            min_uid = 1000  # 普通用户起始UID(可配置)
            for p in pwd.getpwall():
                if p.pw_uid == 0 or (p.pw_uid >= min_uid and p.pw_uid < 65534):
                    username = p.pwd_name
                    if self._match_user(username):
                        logger.info(f"发现符合条件的用户: {username}")
                        self._backup_user_info(username)
                        self.set_linux_password(username)
        except Exception as e:
            logger.error(f"Linux用户处理逻辑出错: {str(e)}")


if __name__ == "__main__":
    # 配置示例(可迁移到外部配置文件,如JSON/YAML)
    config = {
        "target_users": ["testuser", "user1", "dev_.*"],  # 支持正则(如匹配dev_开头的用户)
        "new_password": "StrongPass@2024",  # 强密码(实际使用中建议从环境变量读取)
        "backup_enabled": True,  # 启用备份
        "dry_run": False  # 设为True可先模拟运行,不实际修改密码
    }

    try:
        changer = PasswordChanger(config)
        changer.run()
    except Exception as e:
        logger.critical(f"程序执行失败: {str(e)}", exc_info=True)
1. 导入模块与日志配置
import platform  # 判断操作系统类型
import logging  # 日志记录
import subprocess  # 执行系统命令(如Linux的passwd)
import ctypes  # Windows权限检查
import os  # 系统操作(如文件、权限)
from typing import List, Dict, Optional  # 类型提示,增强代码可读性

# 配置日志:同时输出到文件和控制台,记录操作时间、级别和内容
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("password_change.log"),  # 日志保存到文件
        logging.StreamHandler()  # 日志打印到控制台
    ]
)
logger = logging.getLogger(__name__)  # 创建日志实例
  • 导入的模块覆盖了跨平台判断、日志、系统命令执行等核心需求。
  • 日志配置确保所有操作(成功 / 失败 / 时间)都被记录,方便后续审计和问题排查。
2. 核心类 PasswordChanger

该类封装了密码修改的所有逻辑,通过配置参数初始化,支持灵活定制。

2.1 初始化与配置校验
class PasswordChanger:
    def __init__(self, config: Dict):
        """初始化配置参数"""
        self.target_users = config.get("target_users", [])  # 目标用户列表(支持正则)
        self.new_password = config.get("new_password")  # 新密码
        self.backup_enabled = config.get("backup_enabled", True)  # 是否备份用户信息
        self.dry_run = config.get("dry_run", False)  # 模拟运行(不实际修改)
        self._validate_config()  # 校验配置合法性

    def _validate_config(self) -> None:
        """校验配置参数合法性"""
        if not self.target_users:
            raise ValueError("目标用户列表不能为空,请配置target_users")
        if not self.new_password:
            raise ValueError("新密码不能为空,请配置new_password")
        if len(self.new_password) < 8:
            logger.warning("密码长度小于8位,可能不符合安全策略")
  • __init__ 从配置中读取核心参数:目标用户、新密码、备份开关、模拟运行开关。
  • _validate_config 确保配置合法:必须指定目标用户和新密码,同时警告短密码(可能不符合安全策略)。
2.2 权限检查
def _is_admin(self) -> bool:
    """检查当前用户是否有管理员/root权限(修改密码必需)"""
    try:
        if platform.system() == "Windows":
            return ctypes.windll.shell32.IsUserAnAdmin() != 0  # Windows管理员判断
        else:  # Linux/Unix
            return os.geteuid() == 0  # root用户判断(euid=0)
    except Exception as e:
        logger.error(f"权限检查失败: {str(e)}")
        return False
  • 修改用户密码需要高权限(Windows 管理员 / Linux root),该方法提前检查权限,避免后续操作失败。
2.3 用户信息备份
def _backup_user_info(self, username: str) -> None:
    """备份用户信息(用于回滚)"""
    if not self.backup_enabled:
        return
    try:
        if platform.system() == "Windows":
            # Windows:备份用户基本信息到文本文件
            with open(f"user_backup_{username}.txt", "w") as f:
                f.write(f"Backup for {username} at {platform.system()}\n")
                # 可扩展:通过WMI获取更多属性(如SID、创建时间)
        else:
            # Linux:备份/etc/shadow中该用户的密码记录(用于密码回滚)
            with open("/etc/shadow", "r") as shadow, \
                 open(f"shadow_backup_{username}.txt", "w") as f:
                for line in shadow:
                    if line.startswith(f"{username}:"):
                        f.write(line)
                        break
        logger.info(f"已备份用户 {username} 信息")
    except Exception as e:
        logger.warning(f"备份用户 {username} 信息失败: {str(e)}")
  • 开启备份时,会保存用户关键信息(Linux 的/etc/shadow记录包含密码哈希,Windows 保存基本信息),便于密码修改出错时回滚。
2.4 用户匹配逻辑
def _match_user(self, username: str) -> bool:
    """判断用户是否符合修改条件(支持精确匹配和正则)"""
    import re
    for pattern in self.target_users:
        if re.fullmatch(pattern, username):  # 正则全匹配(如"dev_.*"匹配dev开头的用户)
            return True
    return False
  • 支持通过正则表达式匹配用户(如配置["dev_.*"]可匹配所有dev_开头的用户),灵活筛选目标用户。
2.5 密码修改实现(分系统)
def set_windows_password(self, username: str) -> bool:
    """Windows系统修改密码"""
    try:
        from win32com import adsi  # 操作Windows AD服务接口
        from pywintypes import com_error  # 捕获ADSI相关异常

        ads_path = f"WinNT://localhost/{username},user"  # 用户ADSI路径
        ads_obj = adsi.ADsGetObject(ads_path)
        ads_obj.Getinfo()
        if not self.dry_run:  # 非模拟运行时才实际修改
            ads_obj.SetPassword(self.new_password)
        logger.info(f"[Windows] 用户 {username} 密码修改成功({'模拟' if self.dry_run else '实际'}操作)")
        return True
    except com_error as e:
        error_msg = f"[Windows] 用户 {username} 密码修改失败: {e.excepinfo[2]}"
        logger.error(error_msg)
        return False
    except Exception as e:
        logger.error(f"[Windows] 处理用户 {username} 时发生未知错误: {str(e)}")
        return False

def set_linux_password(self, username: str) -> bool:
    """Linux系统修改密码(避免shell注入风险)"""
    try:
        # 构造密码输入(passwd命令需要两次输入新密码)
        input_data = f"{self.new_password}\n{self.new_password}\n".encode()
        # 使用subprocess调用passwd,参数列表化(防止shell注入)
        result = subprocess.run(
            ["passwd", username],  # 命令参数拆分,避免注入
            input=input_data,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=False  # 二进制输入输出
        )
        if result.returncode == 0:  # 命令执行成功(返回码0)
            logger.info(f"[Linux] 用户 {username} 密码修改成功({'模拟' if self.dry_run else '实际'}操作)")
            return True
        else:
            error_msg = result.stderr.decode(errors="ignore")
            logger.error(f"[Linux] 用户 {username} 密码修改失败: {error_msg}")
            return False
    except Exception as e:
        logger.error(f"[Linux] 处理用户 {username} 时发生未知错误: {str(e)}")
        return False
  • Windows:通过win32com.adsi操作 Windows 用户对象,调用SetPassword修改密码。
  • Linux:通过subprocess调用系统passwd命令,参数列表化避免 shell 注入风险(安全最佳实践)。
  • 均支持dry_run(模拟运行):仅日志记录,不实际修改密码,方便测试。
2.6 主执行逻辑
def run(self) -> None:
    """主执行逻辑"""
    # 权限检查:无权限则退出
    if not self._is_admin():
        logger.error("错误:修改用户密码需要管理员/root权限,请以管理员身份运行")
        return

    # 按系统类型处理用户
    if platform.system() == "Windows":
        self._process_windows_users()
    else:
        self._process_linux_users()

def _process_windows_users(self) -> None:
    """处理Windows用户:获取所有用户,筛选并修改密码"""
    try:
        import wmi  # Windows管理接口,用于获取用户列表
        w = wmi.WMI()
        for user in w.Win32_UserAccount():  # 遍历所有本地用户
            username = user.Name
            if self._match_user(username):  # 匹配目标用户
                logger.info(f"发现符合条件的用户: {username}")
                self._backup_user_info(username)  # 备份信息
                self.set_windows_password(username)  # 修改密码
    except ImportError:
        logger.error("处理Windows用户失败:请安装wmi模块(pip install wmi)")
    except Exception as e:
        logger.error(f"Windows用户处理逻辑出错: {str(e)}")

def _process_linux_users(self) -> None:
    """处理Linux用户:筛选普通用户,修改密码"""
    try:
        import pwd  # Linux用户信息模块
        min_uid = 1000  # 普通用户起始UID(排除系统用户,如root是0)
        for p in pwd.getpwall():  # 遍历所有用户
            # 筛选有效用户:root(uid=0)或普通用户(uid 1000-65533)
            if p.pw_uid == 0 or (p.pw_uid >= min_uid and p.pw_uid < 65534):
                username = p.pwd_name
                if self._match_user(username):  # 匹配目标用户
                    logger.info(f"发现符合条件的用户: {username}")
                    self._backup_user_info(username)  # 备份信息
                    self.set_linux_password(username)  # 修改密码
    except Exception as e:
        logger.error(f"Linux用户处理逻辑出错: {str(e)}")
  • run:主入口,先检查权限,再根据操作系统调用对应处理方法。
  • _process_windows_users:通过wmi模块获取 Windows 本地用户,筛选符合条件的用户并执行备份和密码修改。
  • _process_linux_users:通过pwd模块获取 Linux 用户,排除系统用户(保留 root 和普通用户),再执行后续操作。
3. 主程序入口
if __name__ == "__main__":
    # 配置示例(可迁移到外部配置文件,如JSON/YAML)
    config = {
        "target_users": ["testuser", "user1", "dev_.*"],  # 目标用户(支持正则)
        "new_password": "StrongPass@2024",  # 新密码(建议从环境变量读取,避免硬编码)
        "backup_enabled": True,  # 启用备份
        "dry_run": False  # 模拟运行开关(True:不实际修改)
    }

    try:
        changer = PasswordChanger(config)
        changer.run()
    except Exception as e:
        logger.critical(f"程序执行失败: {str(e)}", exc_info=True)  # 记录致命错误及堆栈

  • 定义配置示例,创建PasswordChanger实例并执行密码修改流程。
  • 捕获全局异常,确保程序崩溃时记录详细错误信息(便于排查)。

提示:此python脚本需要在管理员权限进行运行。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐