阿布云

你所需要的,不仅仅是一个好用的代理。

DNS Rebinding技术绕过SSRF/代理IP限制

阿布云 发表于

1 传统SSRF过滤
   传统SSRF过滤器的方式大致是以下几个步骤:
   获取到输入的URL,从该URL中提取host
   对该host进行DNS解析,获取到解析的IP
   检测该IP是否是合法的,比如是否是私有IP等
   如果IP检测为合法的,则进入curl的阶段发包
乍一看,这种过滤方式似乎没有什么问题.我们从DNS解析的角度看,该检测方式一共有两次,第一次是步骤2中对该host进行DNS解析,第二次是使用CURL发包的时候进行解析.这两次DNS解析是有时间差的,我们可以使用这个时间差进行绕过.
2 DNS Rebinding绕过
DNS Rebinding不是啥新技术了,之前有人用这个技术来做SOP绕过,我们先来看一下具体流程.
攻击者需要自己持有一个域名,然后将这个域名解析指向自己的DNS Server,在该server上写个解析服务,每次返回不同的解析结果.
比如:
1.png

你可以看到,这个解析服务每次返回的结果不同,第一次请求DNS查询,结果返回的是101.191.60.117,是一个合法的公网IP,但是第二次请求时,变成了私有IP 10.36.5.215.注意到,这两条记录的ttl都是0,这是为了防止有DNS服务器对解析结果进行缓存.
说到这里,基本就可以道出绕过原理了.从传统SSRF过滤思路来看,DNS解析一共分两次,其中第一次是至关重要的有效性检测,第二次则是具体发起的请求.我们利用DNS Rebinding技术,在第一次校验IP的时候返回一个合法的IP,在真实发起请求的时候,返回我们真正想要访问的内网IP即可.
该脚本的代码片段如下:

2.png
3 IP双重绑定绕过
深入探索一下,一般PHP在获取IP的时候通常是使用gethostname或者dns_get_record这俩函数.在一个域名下,同时绑定两个IP,来看看这两个函数是怎么处理的.

3.png 

然后执行一下 dns_get_record看看:

4.png

如果使用的是gethostname来获取IP,则只会返回一个,返回哪个IP是随机的.
但是Curl在访问这种域名的时候,由于绑定的是两个IP,curl会尝试访问每一个IP,最终返回有效的那个.比如我一个域名绑定了两个IP,一个是1.1.1.1(80端口关闭),一个是2.2.2.2(80端口开放),在curl这个域名的时候,会返回2.2.2.2的请求结果.如果SSRF过滤逻辑使用的是gethostname或者只获取了dns_get_record返回数组的第一个元素,那么就会存在被绕过的风险.
4 实战中的问题
事实上,基于DNS Rebinding的绕过方式在实战中可能会遇到一些问题.
问题一是DNS缓存的问题,即使我们在前面实现的时候设置了TTL为0,但是有些公共DNS服务器,比如114.114.114.114还是会把记录进行缓存,完全不按照标准协议来,遇到这种情况是无解的.但是8.8.8.8是严格按照DNS协议去管理缓存的,如果设置TTL为0,则不会进行缓存,从效果上来看,每次dig都会跑去我们的NS服务器上去查询一遍.
问题二是DNS迭代查询和递归查询的问题,往往这边发起攻击,DNS服务器会收到很多不同IP的查询请求,无法确定与受害服务器相关的来源IP是哪个.为此我一共实现了3版解析脚本,第一版很容易想到,首先对来源IP进行搜集,保存在文件中,然后真实发起请求的时候基于IP列表进行解析,但是后来发现还是很多莫名其妙的来源IP过来.但是仔细查看这些IP,发现都是某个B段或者C段的,很固定,因此第二版是基于IP段过滤,但是又有这种解析flag标志位交替不准确的问题.
最终,我实现一个时间窗口,用这个时间窗口去返回解析内容,比如前5s返回结果1,后5s返回结果2,对于时间窗口的具体值,需要探测阶段进行统计和尝试.
相关的代码不公开,有兴趣的可以自己实现以下,不是很难.