Tuesday, March 5, 2013

NfSpy meets the real world

Recently, Rob Fuller a.k.a. mubix posted a how-to for using NfSpy through a Meterpreter pivot. It's always nice to see your tools being used in the real world, and mubix's writeup was detailed and straightforward. It was clear, however, that NfSpy is not as user-friendly as I had hoped. I'll try to address some shortfalls in this post, which may also serve as a roadmap for further development.


While [NfSpy's] original intent was aide in bypassing NFS security controls it has the right amount of options to make mounting NFS over Meterpreter possible.
One of the security controls that NfSpy can't bypass also makes using a proxy difficult, and it's turned on by default in almost every instance. The exports setting is called secure, and it explicitly rejects connections coming from source ports greater than 1024. In most operating systems, this means that the client process must be running with root privilege. It's no big deal to run NfSpy as root, but I don't know of a good way to convince a proxy to use a specific source port for outgoing connections.

I specifically put the mountport and nfsport options in to make bypassing network boundaries possible. Many organizations block port 111, because all the standard RPC tools use the portmapper to discover what ports to contact. But as anyone who has used a port scanner can tell you, the portmapper is not the only way to find open ports!
msf > use auxiliary/scanner/nfs/nfsmount
msf > use auxiliary/scanner/misc/sunrpc_portmapper
So mubix uses some nice Metasploit auxiliary modules to enumerate the exports and RPC ports on the remote system. For directly-connected systems, the standard showmount and rpcinfo commands work great, but they can't be easily forced to use TCP, which makes proxying difficult. There is another option, though. Nmap has a collection of NSE scripts that work great for discovering RPC information, and they can be proxied with proxychains. Here's how I would have done it:

# proxychains nmap -p 111 --script rpcinfo,nfs-showmount 192.168.1.50
<snip> 
PORT    STATE SERVICE
111/tcp open  rpcbind
| nfs-showmount:  
|   /home 192.168.1.0/255.255.255.0
|_  /volume/users 192.168.1.0/255.255.255.0 
| rpcinfo: 
|   program version   port/proto  service
|   100000  2,3,4        111/tcp  rpcbind
|   100000  2,3,4        111/udp  rpcbind
|   100003  3,4         2049/tcp  nfs
|   100003  3,4         2049/udp  nfs
|   100005  1,2,3      37719/tcp  mountd
|   100005  1,2,3      52569/udp  mountd
|   100021  1,3,4      54167/udp  nlockmgr
|   100021  1,3,4      38216/tcp  nlockmgr
|   100024  1          55731/tcp  status
|   100024  1          46797/udp  status
|   100227  3           2049/tcp  nfs_acl
|_  100227  3           2049/udp  nfs_acl

Note that this shows the versions of each service, too. For any given NFS version (NfSpy works with version 3), you must use the mountd service with a version 1 less (mountd version 2, in this case). I'm guessing that all the extra mountd ports in mubix's output represent different versions of the mount protocol.
proxychains nfspy -d -o server=192.168.1.50:/home,nfsport=2049/tcp,mountport=37719/tcp,rw /root/nfspy/mount
These options can be placed in any order. This command is equivalent:

proxychains nfspy /root/nfspy/mount -o server=192.168.1.50:/home,nfsport=2049/tcp,rw,mountport=37719/tcp -d

The trick of finding the proper nfs and mount ports is actually unnecessary in this case; since the proxy forwards all TCP traffic, all that is required is to tell NfSpy to use TCP for both mounting and NFS communication. It will then use TCP to connect to the RPC portmapper and pull the proper ports for you. So here's the modified command:

proxychains nfspy -d -o server=192.168.1.50:/home,nfsport=tcp,mountport=tcp,rw /root/nfspy/mount

NfSpy uses fuse which can be really silent when problems arise or give errors that tell you nothing meaningful. That's why I'm using the -d option that keeps nfspy in the foreground, just so I can detect any issues.
This issue has been a thorn in my side regarding FUSE (Filesystem in USErspace, a Linux kernel plugin and API). In reality, the problem is with the Python bindings, and perhaps someone has an answer to the problem of properly reporting fsinit failures. I know I could do a little better, but without a proper way of doing it, I just don't have the motivation.
Last note, you can't just CTRL-C an nfspy mount, you need to use `fusermount -u /root/nfspy/mount` to kill it. It's another fuse issue. If anyone has a better way to do this I'm all ears.
This is a byproduct of the -d flag. If that wasn't being used, then the process would background, and you'd just be left with a mounted directory. fusermount is the program used to unmount FUSE filesystems. Another bug here is that unmounting may not kill the python process either. After I've been testing for a while, I usually have to grep out the process ids and kill -9 them manually.

Given all the problems with FUSE, you might think it's a steaming pile. On the contrary, the library is great, and lets you create filesystem interfaces for just about anything. I was amazed at how fast NfSpy came together, first as a read-only tool to bypass UID-checking, then as a full-featured NFS client. But the next major milestone for its development will be completely divorcing it from FUSE so that it can be used on Windows, BSD, OS X, and anywhere Python can run. Already, the NfSpy class that handles most of the NFS protocol is separate from the Fuse class that provides the filesystem interface. All that is needed is a readline shell, and to implement some useful commands. I envision something like a FTP client interface: get, put, cd, ls. Suggestions and code contributions can be made on the project's Github page, where you can also get the program itself.

EDIT: I added nfspysh in 2013, which does not require FUSE. It works great on Windows!

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.