首页 > 系统相关 >【应急响应+Linux】常见的rootkit隐藏手段:通过用户层劫持加载器/连接器隐藏进程pld(用户层rootkit)

【应急响应+Linux】常见的rootkit隐藏手段:通过用户层劫持加载器/连接器隐藏进程pld(用户层rootkit)

时间:2024-10-12 19:11:30浏览次数:7  
标签:preload 文件 劫持 用户 rootkit so ld 隐藏 加载

原理

linux在进程启动后,和windows加载dll一样会按照一定的顺序加载动态链接库,相关顺序如下:

加载环境变量LD_PRELOAD指定的动态库

加载文件/etc/ld.so.preload指定的动态库

搜索环境变量LD_LIBRARY_PATH指定的动态库搜索路径

搜索路径/lib64下的动态库文件

攻击者常见使用的劫持方式大致存在以下三种:

1、可以通过LD_PRELOAD 最先被加载的特征,攻击者写一些so文件,在这个so文件里面实现一些本来对应命令要使用的函数,运行相应命令会先从该环境变量中加载我们自定义的so文件,从而劫持相应命令对应的函数,实现恶意的逻辑;

2、利用/etc/ld.so.preload是系统的默认ld预加载路径,攻击者可以写一些so文件,在这个so文件里面实现一些本来对应命令要使用的函数,然后把恶意so文件的路径写入该文件内容中,从而劫持相应命令对应的函数,实现恶意的逻辑;

3、利用linux基本都是基于glibc的特征,大部分的动态连接的基础文件都是基于几个常见的so文件,比如libc.so.6,Linux下命令的动态链接中基本上都会使用这个so文件,因为这个so文件实现了各种标准C的各种函数。对于GCC而言,默认情况下,所编译的程序中对标准C函数的链接,都是通过动态链接方式来链接libc.so.6这个函数库的;所以这里攻击者可以替换劫持该so文件,从而实现对linux的几乎所有依靠动态连接的命令的劫持;

拿第二种方式举例:

回到这个进程pid的隐藏,ps\top\netstat等命令是通过读取遍历/proc/pid内容来返回对应的pid等相关值的,读取文件目录底层是通过 readdir/readdir64 实现,这里我们可以利用ld预加载特性,编写恶意的so文件,在相关文件里面重写上面两个函数,在相关函数中当特殊名称的进程出现的时候,相关函数不做返回,或者返回为空跳过即可,并将路径添加到/etc/ld.so.preload中;

该操作对ps的隐藏效果最好,因为ps的所有结果都是完全依赖于 /proc/pid 来获取内容;netstat的话是部分依赖,仅仅获取不到pid和pname(这也是一般netstat能看到网络连接,但是看不到对应的pid和进程名的原因),其他的能拿到的;

参考项目:(https://github.com/gianlucaborello/libprocesshider)

实现:

拿第二种方式举例:

使用上面项目编译生成的.so文件放入受害机器;

修改processhider.c文件里面的process_to_filter 参数,后面修改成要隐藏的进程:

这个想要通过preload加载,也有好几种方式(1、修改$LD_PRELOAD 环境变量,添加so文件路径;2、创建/etc/ld.so.preload文件并写如对应so文件路径;3、修改动态链接器,一般来说动态链接器里面默认使用的路径是/etc/ld.so.preload,这里可以通过篡改动态连接器,修改文件路径,从而系统就会去新文件路径里面去找so文件加载),这里我们选择在受害机器的/etx/ld.so.preload文件中添加对应路径,如下:

然后这里我们模拟一个叫1234567.py的进程(这个在编译上面.so文件的时候要把这个名称写入),该进程源码如下:就是发起socket连接101.1.1.1:43端口:

import socket  
def send\_tcp\_request(ip, port, message):  
    try:  
        \# 创建一个TCP/IP socket  
        sock = socket.socket(socket.AF\_INET, socket.SOCK\_STREAM)  
  
        \# 连接到指定的IP和端口  
        sock.connect((ip, port))  
        sock.settimeout(100)  
  
        sock.sendall(message.encode())  
  
        \# 接收服务器返回的数据  
        received\_data = sock.recv(1024)  
        print("Received:", received\_data.decode())  
  
    except socket.error as e:  
        print("Socket error:", e)  
    finally:  
        \# 关闭socket连接  
        sock.close()  
  
  
\# 测试代码  
if \_\_name\_\_ == "\_\_main\_\_":  
    ip = '101.1.1.1'  \# 要连接的IP地址  
    port = 43  \# 要连接的端口号  
    message = "Hello, server!"  \# 要发送的消息  
    send\_tcp\_request(ip, port, message)  

运行进程:

现象

使用netstat -pantu,如下可以看到这里是发现了网络连接,但是没有pid和pname:

使用ps命令,即使是在知道了被隐藏了进程的名称情况下,也查不到对应的进程:

针对原理的排查方法

1、echo $LD_PRELOAD(排查上述原理中第一种实现方式)

查看环境变量是否被劫持,如果存在劫持情况,unset LD_PRELOAD,并且删除查出来的对应so文件

2、cat /etc/ld.so.preload文件,一般情况下是没有这个文件,或者是有这个文件但是文件内容为空,如果出现内容要重点排查;(排查上述原理中第二种实现方式)

如下图是被劫持的情况:

3、排查系统的动态连接器是否被劫持,也就是最后不会去/etc/ld.so.preload加载,而是去指定的地方加载(排查上述原理中类似第三种实现方式,替换libc.so.6,但是仅仅是修改了里面默认的内置ld连接文件的位置(这点笔者没有去核实,该路径可能不是在libc.so.6里面的,是其他通用so里面,但是排查方式都是校验完整性和hash))

下图先找到命令的二进制文件,然后通过readelf读取其文件头中设置的链接器,然后判断链接器是否被改动

通过时间判断是否动态连接器是否有问题:

通过rpm来校验是否有问题,这个就是通过hash去判断的,如果前面几个字符中没有出现5 就说明md5没有变动,如下图是未发现 ld-2.17.so文件发生了变动

快速排查的思路:

当发现有问题,进程被隐藏了,建议可以直接使用如下方法快速排查:

1、排查指定命令的动态链接库依赖,从上到下逐一排查so文件是否有问题

ldd /usr/bin/ps 如下:

2、直接使用 strace 命令 追踪 相关命令对文件的操作,ps进程执行的所有操作都会被记录,然后再去看是否存在可疑操作,打开了可疑的so文件等,从而判断问题出在哪个so文件

strace -o result.txt -e trace=file -f ps

效果如下:

解除方法

1、如果是环境变量劫持LD_PRELOAD 那就清空LD_PRELOAD,删除劫持的恶意so文件;

unset LD_PRELOAD

2、如果是ld.so.preload劫持,删除 /etc/ld.so.preload里面的劫持内容,删除劫持的恶意so文件;

直接删除ld.so.preload文件也可以

3、如果链接器被篡改了,那就重下载,替换回来;

快速排查获取隐藏的pid方法:

1、以其人之道还治其人之身,劫持攻击者的劫持,那么程序就会调用我们的劫持,通过强行指定一个LD_PRELOAD环境变量 去执行对应的命令,如果我们怀疑readdir这个函数被劫持了,那么只要我们指定的LD_PRELOAD实现了readdir这个函数那么就能解除劫持,需要注意的是先要校验我们指定的so有没有被劫持,

上面使用的是/lib64/libc.so.6,如下在其导出表里面可以看到其实现了readdir64,所以可以解除劫持

2、上传一个busybox,busybox是使用静态连接编译而成的,不会被动态链接相关机制劫持;所以直接使用busybox,可以绕过动态链接机制,拿到pid和pname;

标签:preload,文件,劫持,用户,rootkit,so,ld,隐藏,加载
From: https://www.cnblogs.com/o-O-oO/p/18461280

相关文章

  • 【应急响应+Linux】常见的rootkit隐藏手段:通过劫持shell环境,实现文件、进程名隐藏等操
    原理修改或构造/etc/profile.d/下sh文件,劫持环境变量,从而实现覆盖常见的命令,如:ps、ls、lsof等;实现:1、配置环境变量shell脚本:重新登录用户之后;或者使用命令source/etc/profile更新配置,使生效;2、根目录下存在的myshell.sh文件被隐藏:执行ls命令效果:排查方法:使用strace......
  • 【应急响应+Linux】常见的rootkit隐藏手段:通过挂载/proc/pid实现pid隐藏
    原理ps、netstat是遍历/proc来显示pid的原理,通过隐藏相关/proc/pid文件夹来实现pid隐藏实现运行如下命令,将pid对应文件夹挂载到隐藏目录上面mount-obind/home/.hidden/proc/9212现象:如下图,使用root权限调用netstat发现PID和Programname都是空:排查方法1、ca......
  • 安全:列出linux中可登录shell/ssh/sudo的用户
    一,列出可登录shell的用户root@lhdpc:~#grepbash/etc/passwdroot:x:0:0:root:/root:/bin/bashliuhongdi:x:1000:1000:liuhongdi,,,:/home/liuhongdi:/bin/bash只显示用户名:root@lhdpc:~#grepbash/etc/passwd|cut-d:-f1rootliuhongdi也可以把常见的3个不允许登录......
  • 隐藏zsh前面用户名或者主机名
    1.zshrc修改vim~/.zshrc文件,在文件底部增加:prompt_context(){}只保留用户名,隐藏主机名prompt_context(){if[["$USER"!="$DEFAULT_USER"||-n"$SSH_CLIENT"]];thenprompt_segmentblackdefault"%(!.%{%F{yellow}%}.)$USER"......
  • NSIS安装包设计为当前用户和所有用户安装
    一、在windows系统中为当前用户安装和所有用户安装有什么差异 最直观、简单的区别就是如果选择“仅为我安装”,那么安装完成后,软件只能安装软件的这个用户使用,其他用户的开始菜单和桌面上没有这个应用;而如果选择“为使用这台电脑的任何人安装”,那么安装完成后,所有用户的开始菜单......
  • 使用C#获取系统关键信息:CPU、内存、硬盘、用户与网络状态
    在C#中,获取系统信息如CPU、内存、硬盘、用户以及网络状态等,可以通过多种方式实现,包括使用System.Management命名空间中的类来查询WMI(WindowsManagementInstrumentation)信息,或者使用.NETFramework自带的类库。以下是一些基本示例来展示如何获取这些信息。1.引入必要的命......
  • 【Linux探索学习】第四弹——Linux权限管理详解:理解用户、组和权限之间的关系
    前言:在前面我们已经学习了Linux的基础指令,相信大家对Linux已经有了一定的认识,今天我们来学习Linux权限的相关知识点,Linux权限是Linux初学者必须要掌握的内容目录一、Linux下用户类型二、权限基本概念三、权限的表示四、文件访问权限的相关设置方法1.chmod指令2.ch......
  • 在Kubernetes中实现灰度发布(Canary Release)是一种有效的策略,它允许逐步将新版本的应用
    在Kubernetes中实现灰度发布(CanaryRelease)是一种有效的策略,它允许逐步将新版本的应用推送给部分用户群体,以收集反馈并监控新版本的表现。这种方法可以显著降低新版本上线的风险,并在问题发生时快速回滚。灰度发布通常比滚动更新(RollingUpdate)提供更细粒度的控制,因为它可以精确......
  • WPF开发用户控件、用户控件属性依赖DependencyProperty实现双向绑定、以及自定义实现C
    1、新建了一个用户控件,里面画了一个实心圆,以及一个文本控件的组合,当作我要实验使用的用户控件(TestUserControl)。 2、在主窗体里面进行引用,可以看到引用以后,会在工具箱上显示新增的用户控件3、为了测试方便,我直接在先前的Lo'gin页面直接进行添加该用户控件,效果如下。 4、......
  • CSS 隐藏元素的方法
    display:none;通过设置元素CSS的displays属性为none来使元素消失,该方法隐藏的元素不占据页面空间oapcity:0;oapcity属性的作用是控制元素的透明度,当值为0的时候该元素完全不可见,但仍然存在页面上,占据页面空间visibility:hidden;visibility属性旨在控制元素的可见性,设置为hid......