You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C语言编程修改shadow文件哈希段?现有代码问题求优化

解决Linux Shadow文件哈希替换的两个核心问题

问题根源分析

  1. 短哈希残留问题:原代码若采用原地字节覆盖的方式修改哈希段,当新哈希长度短于原哈希时,原哈希未被覆盖的剩余字符会留在文件中,导致用户条目格式失效。
  2. 代码通用性不足:若代码硬编码了哈希位置、长度或用户条目格式,会无法适配不同哈希算法(如MD5/SHA-256/SHA-512)或shadow文件的字段变化。

规范通用的解决方案

正确的做法是采用**「读取-解析-修改-原子写入」**的安全流程,彻底避免原地修改的弊端,同时适配所有符合POSIX规范的shadow文件格式。

核心思路

  1. 以root权限读取shadow文件(仅root可访问)。
  2. 逐行解析用户条目,格式为:用户名:哈希:last_change:min:max:warn:inactive:expire:reserved。
  3. 定位目标用户后,重新构建完整的用户条目行(替换哈希段)。
  4. 将所有处理后的行写入临时文件(权限与shadow保持一致)。
  5. 用原子操作rename替换原shadow文件(避免中途崩溃导致文件损坏)。

完整可运行代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define SHADOW_PATH "/etc/shadow"
#define TEMP_SHADOW_PATH "/etc/shadow.tmp"
#define BUFFER_SIZE 1024

// 替换指定用户的密码哈希
int update_shadow_hash(const char *username, const char *new_hash) {
    // 强制检查root权限
    if (getuid() != 0) {
        fprintf(stderr, "错误:必须以root身份运行\n");
        return -1;
    }

    // 打开原shadow文件
    FILE *shadow_fp = fopen(SHADOW_PATH, "r");
    if (!shadow_fp) {
        perror("打开shadow文件失败");
        return -1;
    }

    // 创建临时文件,权限与shadow保持一致(0600)
    int temp_fd = open(TEMP_SHADOW_PATH, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
    if (temp_fd == -1) {
        perror("创建临时shadow文件失败");
        fclose(shadow_fp);
        return -1;
    }
    FILE *temp_fp = fdopen(temp_fd, "w");
    if (!temp_fp) {
        perror("打开临时文件流失败");
        close(temp_fd);
        unlink(TEMP_SHADOW_PATH);
        fclose(shadow_fp);
        return -1;
    }

    char buffer[BUFFER_SIZE];
    int user_found = 0;

    // 逐行处理shadow文件
    while (fgets(buffer, BUFFER_SIZE, shadow_fp) != NULL) {
        // 去除换行符
        size_t line_len = strlen(buffer);
        if (line_len > 0 && buffer[line_len - 1] == '\n') {
            buffer[line_len - 1] = '\0';
        }

        // 分割用户名(第一个冒号前的内容)
        char *current_user = strtok(buffer, ":");
        if (!current_user) {
            // 无效行直接写入临时文件
            fprintf(temp_fp, "%s\n", buffer);
            continue;
        }

        if (strcmp(current_user, username) == 0) {
            user_found = 1;
            // 跳过原哈希字段,获取后续所有字段
            strtok(NULL, ":"); // 跳过旧哈希
            char *remaining_fields = strtok(NULL, "\0"); // 从第三个字段开始的所有内容

            if (remaining_fields) {
                fprintf(temp_fp, "%s:%s:%s\n", username, new_hash, remaining_fields);
            } else {
                // 极端情况:无后续字段
                fprintf(temp_fp, "%s:%s:\n", username, new_hash);
            }
        } else {
            // 非目标用户,直接写入原行
            fprintf(temp_fp, "%s\n", buffer);
        }
    }

    if (!user_found) {
        fprintf(stderr, "错误:未找到用户 '%s'\n", username);
        fclose(temp_fp);
        unlink(TEMP_SHADOW_PATH);
        fclose(shadow_fp);
        return -1;
    }

    // 关闭文件流
    if (fclose(temp_fp) != 0 || fclose(shadow_fp) != 0) {
        perror("关闭文件失败");
        unlink(TEMP_SHADOW_PATH);
        return -1;
    }

    // 原子替换原shadow文件(避免中途崩溃导致文件损坏)
    if (rename(TEMP_SHADOW_PATH, SHADOW_PATH) == -1) {
        perror("替换shadow文件失败");
        unlink(TEMP_SHADOW_PATH);
        return -1;
    }

    printf("成功更新用户 '%s' 的哈希\n", username);
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "用法:%s <用户名> <新哈希>\n", argv[0]);
        fprintf(stderr, "示例:%s myuser '$6$rounds=1000$abc$def...'\n", argv[0]);
        return EXIT_FAILURE;
    }

    return update_shadow_hash(argv[1], argv[2]) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

代码优势说明

  1. 解决短哈希残留:通过重新构建完整用户条目,彻底替换原行,不会残留旧哈希的多余字符。
  2. 通用性强:适配所有Linux哈希算法(MD5/SHA-256/SHA-512等),兼容标准shadow文件的所有字段格式。
  3. 安全性高:
    • 临时文件权限严格设置为0600,避免权限泄露。
    • rename是原子操作,确保shadow文件始终处于有效状态。
    • 强制检查root权限,符合系统安全规范。
  4. 错误处理完善:所有系统调用(文件操作、权限检查等)均包含错误处理,便于排查问题。

注意事项

  • 编译命令:gcc -o shadow_update shadow_update.c
  • 运行方式:sudo ./shadow_update 用户名 新哈希
  • 新哈希必须符合Linux密码格式(如$6$rounds=1000$salt$hash,6代表SHA-512算法)。
  • 操作前建议备份shadow文件:sudo cp /etc/shadow /etc/shadow.bak

内容的提问来源于stack exchange,提问作者peacism

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 14:34:52