1. 从一次内网扫描说起:为什么rsync会成为突破口
先讲个我早年间经历的事儿。某次给一家企业做内网安全评估,扫了一圈发现一台备份服务器上开了873端口,banner显示是rsync。当时同行的人没太在意,觉得rsync就是个文件同步工具,能有什么风险。结果我用一条命令直接列了共享模块,发现整个备份目录完全开放,连匿名用户都能读写。
那次经历让我意识到,rsync作为Linux世界最常用的文件同步工具之一,它的访问控制机制如果配置不当,造成的影响远比大多数人想象的要严重。它不是那种花里胡哨的漏洞利用,而是“配置即漏洞”的典型代表——你不需要精心构造数据包,不需要绕过复杂的防护,只要服务端把门敞开了,任何人都能直接走进去拿东西。
这篇文章我不会去教你如何对某个目标发起攻击,那是违法的事儿。但我会把rsync未授权访问的原理、检测思路、加固方法完整讲清楚。对于负责运维和安全的同学来说,理解攻击者是怎么想的、是怎么摸到你的服务器的,你才知道该在哪些地方堵窟窿。
如果你是刚接触Linux安全的新手,这篇文章同样适用。我会把涉及的基础知识一并讲透,比如rsync的模块机制、权限模型、日志审计,让你不光是“知道有这个坑”,而是真正理解“这个坑是怎么形成的”,以后碰到类似的服务(比如NFS、FTP、Redis)也能举一反三。
2. 洞悉本质:rsync的模块机制与匿名访问模型
2.1 rsync的守护进程模式是怎么工作的
很多人日常用rsync都是命令行模式,比如rsync -avz ./data user@host:/backup,这种用法走的是SSH通道,安全性和认证都由SSH负责。但还有另一种用法,就是让rsync以守护进程(daemon)方式运行,监听在873端口,由rsync自己来处理连接和文件传输。
守护进程模式的核心概念是“模块”(module)。你可以把模块理解成一块对外暴露的目录区域,配置文件/etc/rsyncd.conf里每一个方括号段落都是一个模块,比如:
[backup] path = /data/backup comment = Backup Storage read only = yes当客户端执行rsync rsync://192.168.1.10/backup的时候,实际上就是在请求这个名为backup的模块。服务端收到请求后,会把path指定的目录映射给客户端。
关键问题来了:这个模块允许谁来访问?谁能读写?这就是auth users、secrets file、read only、hosts allow这几个参数决定的。
2.2 未授权访问的本质是“没人管门”
如果你在配置模块的时候,既没有设置auth users,也没有设置hosts allow,那么这个模块对全网所有IP都是开放的。任何人在任何地方,只要网络能到达你的873端口,就能执行:
rsync rsync://目标IP/模块名这条命令会直接列出模块下的文件列表。如果是只读模块,攻击者可以批量拉取所有文件;如果没有设置只读,攻击者还能往里面写文件。
为什么会出现这种配置?我总结了几类常见原因:
- 图省事:内网环境觉得“反正外面进不来”,干脆不做认证
- 不懂参数:把
auth users当成可选项,以为配置了path就完事了 - 遗留配置:之前临时测试用的配置,后来忘了清理
- 只设了IP白名单但没设认证:某些场景下IP可以伪造或绕过,单一防护不够
这里要特别强调一个认知误区:很多人觉得“rsync是内网服务,外网访问不到就安全了”。但内网不等于安全区,内网里同样有被攻陷的跳板机、恶意员工、中了马的办公终端。一旦攻击者通过钓鱼或者漏洞打进内网,第一件事就是横向扫描开放端口,而873端口几乎是一扫一个准的高价值目标。
2.3 访客模式下能做什么
当rsync模块被匿名访问时,攻击者能干的事儿大致分三档:
第一档:信息泄露。用rsync rsync://IP/模块名列出文件列表,分析目录结构、文件名、备份策略,从中推断业务类型、系统版本、内部网络结构。备份文件里往往藏着数据库导出、配置文件、密钥、源码压缩包,这些都属于高敏数据。
第二档:数据篡改。如果模块不是只读的,攻击者可以上传恶意文件。典型手法是在网站根目录模块里放webshell,或者替换应用配置文件,甚至直接覆盖二进制文件植入后门。由于rsync同步的特性,被篡改的文件还会随着下一次同步扩散到其他服务器。
第三档:利用rsync本身拿权限。这个我会在后面章节细讲,思路是利用rsync的写权限修改目标机器的定时任务、SSH公钥等文件,实现远程命令执行。
所以,“未授权访问”四个字听上去不痛不痒,实际危害可以类比成“你家大门没锁,快递员、小偷、推销员都能进来翻东西”——区别只是来的人想干什么。
3. 写权限等于命令执行:从文件上传到RCE的路径推演
3.1 为什么说“能写文件”就是“能执行命令”
这是rsync安全里最重要也最容易被低估的一点。很多人觉得“我只是开了文件写入,他又不能执行命令”。但在一台Linux服务器上,文件写入权限和命令执行权限之间往往只隔着一层窗户纸。
举几个最常见的利用路径:
路径一:写入定时任务。Linux的cron任务目录是/var/spool/cron/或/etc/cron.d/,这些目录里的文件如果内容合法,就会被cron服务读取并执行。如果rsync模块暴露的path刚好包含这些目录(很多备份策略喜欢备份/etc、/var/spool这类系统路径),攻击者就能写一个定时任务文件进去,内容指定一个反弹shell命令,等cron执行即可。
路径二:写入SSH公钥。如果模块的path指向某个用户的家目录(比如备份/home),并且该用户开启了SSH登录且目录下有.ssh/authorized_keys,攻击者可以向里面追加自己的公钥,然后直接用SSH登录该用户。
路径三:覆盖启动脚本或服务配置。systemd服务单元文件、init.d脚本、环境变量文件(如/etc/profile.d/、~/.bashrc)如果被篡改,等效于直接控制了该用户或服务的运行环境。
路径四:替换Web目录文件。如果模块暴露的是Web根目录,写入一个JSP/PHP脚本就能获得Webshell,后续顺着Webshell再提权。
需要说明的是,上面这些路径的可行性取决于rsync模块的具体配置。如果模块配置了read only = yes,那么只有读取权限,以上都不可行。但很多备份模块为了让客户端能推送数据,会设置成可写,这就在无意中打开了潘多拉魔盒。
3.2 rsync同步机制在攻击中扮演的角色
rsync的同步机制和普通的上传下载有个明显区别:它支持增量传输和删除操作。如果配置了delete参数,客户端同步时会删除服务端多余的文件。
这意味着攻击者不只是能“多写几个文件”,甚至可以批量删除目标机器上的文件。配合定时任务等目录,直接让目标机器起不来服务,或者把业务数据删光。这种破坏力比单纯的命令执行更野蛮,但造成的业务影响却更直接。
所以我在评估一个rsync暴露风险的时候,从来不会只看“能不能读”,而是把“能不能写”“能不能删”“覆盖了哪些目录”三个维度一起评估。信息泄露、数据破坏、远程控制,三条线的危害程度完全不同。
3.3 攻击者视角的操作逻辑
以攻击者的视角来看,拿到一个开放873端口的目标后,大致的判断流程是这样的:
- 用
rsync rsync://IP枚举有哪些模块 - 对每个模块观察配置信息里的只读/可写提示、路径特征
- 如果是可写模块,先拉取文件列表确认path名称(配置里不一定显示实际路径,但文件结构能透露不少信息)
- 判断能否把文件写到关键目录(cron、ssh、web目录等)
- 如果无法判断实际路径,就通过拉取
/etc/passwd等标志性文件来反推path的根目录位置
这个过程提醒我们一个事实:rsync未授权访问很少是“单一漏洞利用”,它往往是一个侦察和横向移动的入口点。攻击者在这个入口上能待多久、能摸多深,完全取决于服务端配置开放了多大面。
4. 从配置到加固:手把手堵住rsync的每个风险点
4.1 安全的rsyncd.conf应该长什么样
下面这份配置是我在实际生产环境中长期使用的模板,你可以根据业务情况做调整,但核心安全项不要删:
# /etc/rsyncd.conf uid = rsync gid = rsync use chroot = yes max connections = 10 lock file = /var/run/rsyncd.lock log file = /var/log/rsync.log transfer logging = yes timeout = 300 read only = no list = no [backup] path = /data/rsync/backup comment = Internal Backup Module auth users = backupuser secrets file = /etc/rsyncd.secrets hosts allow = 192.168.10.0/24 hosts deny = * read only = no逐行拆解关键点:
uid和gid:指定rsync进程以专用系统账号运行,不要用root。这是最重要的一条,即使被入侵,权限也被限制在普通用户级别。可以先用useradd -r rsync创建专用账号。use chroot = yes:在传输文件前把进程禁锢到模块的path目录中,防止客户端借助符号链接或路径穿越访问系统其他位置。list = no:禁止客户端枚举模块列表。这样即使对方知道IP,也得先猜模块名。auth users+secrets file:启用账号密码认证。secrets文件格式是“用户名:密码”,权限必须设置为600。hosts allow/hosts deny:只允许内网指定网段访问。注意这不能替代认证,只是缩小了暴露面。
read only的值需要根据业务定:如果只是服务端给客户端分发文件,建议read only = yes;如果允许客户端推送备份数据,那至少要把uid设置为非root的专用账号,并且path目录的所有者不要设置为该目录的上级敏感路径。
4.2 创建密钥文件的细节
secrets文件是个容易踩坑的地方。我见过好几次配置完rsync总是认证失败的,最后发现是secrets文件权限不对。rsync守护进程对密码文件要求非常严格,权限必须是600或更严格,属主必须是运行rsync进程的用户,否则直接拒绝读取。
正确的创建方式:
# 创建专用账号 useradd -r -s /sbin/nologin rsync # 创建密码文件 echo "backupuser:Str0ngPassw0rd" > /etc/rsyncd.secrets chown root:rsync /etc/rsyncd.secrets chmod 600 /etc/rsyncd.secrets另外,客户端连接时如果要免交互输入密码,可以用--password-file=/etc/rsync.pwd参数指定密码文件。这个文件在客户端也建议设置600权限,否则本机其他用户能通过/proc或直接读文件拿到明文密码。
4.3 网络层的额外防线
除了rsync自身的配置,我还建议在网络层做三层防护,这属于纵深防御:
- iptables/firewalld:只放行特定来源IP访问873端口。比如只允许备份服务器IP访问,其他全部DROP。
- fail2ban:监控rsync日志,对多次认证失败的IP做临时封禁。虽然rsync未授权访问不涉及“爆破密码”,但这个机制能防住针对secrets文件的暴力猜测。
- 不监听公网:如果是纯内网使用,让rsync只监听内网IP,不要用
0.0.0.0。在配置里用address = 内网IP指定监听地址,减小暴露面。
4.4 安全基线自检脚本
我把自己常用的rsync安全基线检查写成了一个脚本,跑一遍就能看到风险项。你可以参照这个思路做成自动化巡检:
#!/bin/bash # rsync安全巡检脚本 CONF=/etc/rsyncd.conf echo "=== 检查rsync配置 ===" # 1. 检查是否启用匿名模块 if grep -vE "^\s*#|^\s*$" $CONF | grep -q "auth users"; then echo "[OK] 已配置认证" else echo "[FAIL] 存在未配置认证的模块" fi # 2. 检查是否启用chroot if grep -q "use chroot = yes" $CONF; then echo "[OK] chroot已启用" else echo "[WARN] 未启用chroot" fi # 3. 检查是否允许list if grep -q "list = no" $CONF; then echo "[OK] 已禁止列表枚举" else echo "[FAIL] 模块可被匿名枚举" fi # 4. 检查uid是否root if grep -qE "^(uid|gid) = root" $CONF; then echo "[FAIL] rsync以root运行" else echo "[OK] rsync非root运行" fi # 5. 检查密码文件权限 if [ -f /etc/rsyncd.secrets ]; then PERM=$(stat -c "%a" /etc/rsyncd.secrets) if [ "$PERM" = "600" ]; then echo "[OK] 密码文件权限正确" else echo "[FAIL] 密码文件权限为$PERM,应设为600" fi else echo "[WARN] 未找到密码文件" fi # 6. 检查网络暴露情况 echo "=== 端口监听检查 ===" ss -tlnp | grep 873 || echo "[INFO] 873端口未监听"这套脚本不需要额外依赖,放到cron里每周跑一次,或者接入监控系统,至少能保证关键项不失控。
5. 检测发现与日志审计:如何知道自己有没有中招
5.1 主动检测:在攻击者之前发现问题
与其等被入侵后才去排查,不如站在攻击者的角度主动探测自己的服务。这里提供一个完全合规的自检思路:用自己的服务器模拟一次“未授权访问”,看能不能枚举模块、能不能匿名拉取文件。
首先要明确一点:对自己的服务器做检测是合法的安全运维行为,但不要对未经授权的目标做同样的事。这个边界是红线,绝对不能越。
检测命令:
# 检查873端口是否对外开放 nmap -p873 127.0.0.1 # 枚举模块(如果配置了list=no,这里可能不显示模块名) rsync rsync://127.0.0.1/ # 尝试直接访问常见模块名 rsync rsync://127.0.0.1/backup rsync rsync://127.0.0.1/data注意rsync rsync://IP/尾部没有模块名,这是枚举模式。如果服务端配置了list = yes,这一步会直接列举出所有模块名;如果配置了list = no,则只会返回连接成功的消息,不会暴露模块名。
从攻击者的角度,list = no只能提高枚举门槛,并不能完全阻止。常见的模块名其实非常容易猜测,backup、data、www、upload、files、public等等,字典枚举一轮下来总能命中几个。这也是为什么认证配置才是安全的核心,list = no只是辅助手段。
5.2 日志审计:连接记录里的蛛丝马迹
rsync的守护进程会在日志里记录每一次连接和传输行为。如果你的rsyncd.conf里配置了transfer logging = yes,日志里会记录客户端的IP、请求的模块、传输的文件数量等信息。默认情况下日志写到syslog或rsyncd.log,具体看配置文件里的log file参数。
我整理了一份异常特征速查表,你可以在审计日志时重点关注这些模式:
| 特征 | 说明 | 风险等级 |
|---|---|---|
| 同一IP短时间内大量连接 | 可能在进行模块名暴力枚举 | 中 |
| 连接后长时间无文件传输 | 攻击者可能在探索目录结构 | 中 |
| 来自非预期网段的连接 | 说明hosts allow可能未生效或网络边界被突破 | 高 |
| 向非预期模块写入文件 | 备份目录出现了非业务的写操作 | 高 |
| 连接发生在凌晨业务低峰期 | 自动攻击脚本常选这个时段 | 中 |
手工翻日志的效率太低,我更推荐用grep加awk配合监控。比如查看最近谁连过873端口:
# 查看最近rsync连接记录 grep rsync /var/log/rsyncd.log | tail -100 # 统计各IP连接次数 awk '{print $NF}' /var/log/rsyncd.log | sort | uniq -c | sort -rn | head -205.3 中招后的应急处理顺序
如果不幸发现服务器上的文件被异常访问或篡改,处理顺序非常关键。我踩过慌乱的坑,也总结过靠谱的流程,分享给你:
- 立即断网或收紧防火墙:先从网络层面阻断可疑IP,但不要急着关rsync服务,保留现场,进程还在便于排查日志。
- 备份日志和配置:把
/var/log/rsyncd.log、/etc/rsyncd.conf、/etc/rsyncd.secrets原样拷贝一份,用stat记录文件的修改时间。 - 检查关键文件篡改痕迹:重点看
/var/spool/cron/、/etc/cron.d/、.ssh目录、Web目录、系统二进制文件的时间戳。 - 定位webshell和后门:如果发现新增的可执行文件,不要直接删除,先保留样本分析。Web目录里可以用
find查找最近修改的脚本文件:
find /var/www/html -type f -mtime -7 -name "*.php" -o -name "*.jsp" | xargs ls -la修改所有相关密码和密钥:rsync的认证密码、服务器上Linux用户的密码、SSH密钥全部重新生成。
修补配置并部署监控:按第四节的安全基线把rsync重新加固,同时部署文件完整性监控。
6. 扩展思考:同类“信任边界”风险与常态化防御
研究rsync未授权访问给我最大的启发,是它代表了运维中特别常见的一类问题:服务本身不是高危漏洞,但信任边界画错了。rsync默认信任网络里所有能连到它的人(如果没有配置认证的话),这类问题不止rsync有。
NFS(网络文件系统)如果exports里写了*(rw),效果和rsync匿名可写模块如出一辙。Redis如果没设密码且监听在0.0.0.0,攻击者用一条config set dir就能写cron目录。FTP配置成匿名可写也有类似问题。它们的共同特征是:默认配置优先考虑可用性,而安全需要使用者主动配置。
所以我在评估一个系统安全的时候,不再把某个单独服务拎出来看,而是看整个系统的“信任模型”:
- 这个服务信任谁?(有没有认证)
- 信任的范围是什么?(允许哪些IP)
- 被信任的人能做什么?(只读还是可写)
- 如果信任被滥用,影响边界在哪?(是不是root权限、能不能影响其他服务)
顺着这几个问题往下排查,比单个服务逐个找漏洞高效得多。
防御层面我给三条长期建议:
- 定期做自我渗透测试:不需要搞得很复杂,每季度末用扫描器扫一遍内网,主动发现873、2049(NFS)、6379(Redis)这类常见服务是否暴露在过大的范围。
- 建立配置变更审批机制:rsyncd.conf这类文件不能谁想改就改。改动前先review,改完自动跑一遍安全基线脚本。
- 接入统一的日志监控平台:单机日志很容易被忽略,把rsync日志接入ELK这类平台后,配合规则引擎做异常检测,效率会高得多。
最后分享一个经验:安全加固这件事,最怕的不是技术不够,而是“觉得内网里没事”。我见过的几起rsync导致的事故,无一例外都是管理员认为“内网嘛,没事”,结果等真出了事才追悔莫及。所以我的习惯是把每台服务器的对外暴露面当成公网来审视,宁可多绑一层认证,多限制一段来源IP,也不给入侵者留一条捷径。你可以在自己的环境里试试我给的巡检脚本,看到输出里有FAIL或者WARN的地方,就顺着去改,改完再跑一遍。