绕过安全设备的0day

释放双眼,带上耳机,听听看~!
1
介绍

DNS域名系统是互联网关键的基础设施之一,它是一个将域名与IP地址互相映射的全球分布数据库。对于恶意DNS的过滤、检测恶意网站域名、僵尸网络和网络隐秘通道发现是安全防护设备中必不可少的一种手段。

2
原理

RFC 1035规定了域名每个标签不超过63字节,域名总长不超过255字节。可以含有任意8bit值,通常情况下域名标签由英文字母、数字和连字符构成。RFC 2181进一步明确了,DNS本身不对域名所含字符内容进行限制。一些文献中验证了ISC BIND等常用DNS服务器软件对二进制域名的支持。尽管在RFC1 123 之中对于DNS软件支持无法转换为可打印格式的资源记录,内部存储不能使用文本格式。由于Letter Digit Hyphen规则的域名含有可打印字符,如此产生了两种问题,其一为大多数程序对于域名的处理采用字符串函数,可能会对于某些特定结束字符进行处理(例如C语言中对于\000进行处理),其二DNS服务器对于特殊字符进行处理后依然返回解析结果,某些程序过滤恶意DNS域名并未考虑。

3
测试方法

在对DNS服务器测试时,我们想被测的服务器发送正常和带有特殊字符的DNS两种请求,如果DNS服务器两种响应存区别则证明其二失败,否则成功。

01

序列名称

PYTHON socketserverstruct开发简单的DNS服务器进行测试,再使用DNSPython模块作为DNS请求的测试。DNS服务器脚本详情请见附录。

测试使用的版本:

Dnspython 1.16.0

Python 2.17.16

首先进行的是正常的测试,使用www.aa.com.www.bb.com能够正常的解析。如图1所示。

1

然后使用带有特殊字符的
www.aa.com\x09.www.bb.com
解析结果,如图
2
所示。

2

通过返回信息不难得出”\x09”等同于”.”。为了进一步分析,通过数据报文查看传输的请求。正常的DNS请求如图3所示。

3

带有特殊字符的请求如图4所示。

4

5
4
危害
一.隐藏恶意软件域名:
通过该方法可绕过基于DNS流量检测的流量分析软件、算法和相关安全设备及在线文件分析系统,通过构造加入特殊字符的DNS请求,既保证了域名成功解析,又保护了恶意域名难以被发现。
二.隐藏DNS隐蔽通道

将伪造源地址的方法与本文域名欺骗方法结合,可以起到更好的DNS隧道流量隐蔽效果,通过伪造源地址来分散DNS隧道流量,对内网数据泄密和远程控制隧道加入了新的挑战。
三.绕过DNS过滤

利用这个方法进行DNS过滤设备的穿透具有一定的可行性,目前已发现大量带有域名过滤的安全设备.存在被绕过的风险。

6
修复

1. 通过DNSPython修复,在dns\resolver.py->Resolver()->query() 第802行,加入过滤异常的特殊字符。

2. 安全设备中扩大过滤DNS请求特殊字符的范围

7
附录

DNS简单服务器:

import socketserverimport struct# DNS Queryclass SinDNSQuery: def __init__(self, data): i = 1 self.name = '' while True: d = data[i] if d == 0: break; if d < 32: self.name = self.name + '.' else: self.name = self.name + chr(d) i = i + 1 self.querybytes = data[0:i + 1] (self.type, self.classify) = struct.unpack('>HH', data[i + 1:i + 5]) self.len = i + 5 def getbytes(self): return self.querybytes + struct.pack('>HH', self.type, self.classify)# DNS Answer RRS# this class is also can be use as Authority RRS or Additional RRSclass SinDNSAnswer: def __init__(self, ip): self.name = 49164 self.type = 1 self.classify = 1 self.timetolive = 190 self.datalength = 4 self.ip = ip def getbytes(self): res = struct.pack('>HHHLH', self.name, self.type, self.classify, self.timetolive, self.datalength) s = self.ip.split('.') res = res + struct.pack('BBBB', int(s[0]), int(s[1]), int(s[2]), int(s[3])) return res# DNS frame# must initialized by a DNS query frameclass SinDNSFrame: def __init__(self, data): (self.id, self.flags, self.quests, self.answers, self.author, self.addition) = struct.unpack('>HHHHHH', data[0:12]) self.query = SinDNSQuery(data[12:]) def getname(self): return self.query.name def setip(self, ip): self.answer = SinDNSAnswer(ip) self.answers = 1 self.flags = 33152 def getbytes(self): res = struct.pack('>HHHHHH', self.id, self.flags, self.quests, self.answers, self.author, self.addition) res = res + self.query.getbytes() if self.answers != 0: res = res + self.answer.getbytes() return res# A UDPHandler to handle DNS queryclass SinDNSUDPHandler(socketserver.BaseRequestHandler): def handle(self): data = self.request[0].strip() dns = SinDNSFrame(data) socket = self.request[1] namemap = SinDNSServer.namemap if(dns.query.type==1): # If this is query a A record, then response it
name = dns.getname(); if namemap.__contains__(name): # If have record, response it dns.setip(namemap[name]) socket.sendto(dns.getbytes(), self.client_address) elif namemap.__contains__('*'): # Response default address dns.setip(namemap['*']) socket.sendto(dns.getbytes(), self.client_address) else: # ignore it socket.sendto(data, self.client_address) else: # If this is not query a A record, ignore it socket.sendto(data, self.client_address)# DNS Server# It only support A record query# user it, U can create a simple DNS serverclass SinDNSServer: def __init__(self, port=53): SinDNSServer.namemap = {} self.port = port def addname(self, name, ip): SinDNSServer.namemap[name] = ip def start(self): HOST, PORT = "0.0.0.0", self.port server = socketserver.UDPServer((HOST, PORT), SinDNSUDPHandler) server.serve_forever()# Now, test itif __name__ == "__main__": sev = SinDNSServer() sev.addname('www.aa.com', '192.168.0.1') # add a A record sev.addname('www.aa.com.www.bb.com', '192.168.0.2') # add a A record sev.addname('*', '192.168.0.5') # default address sev.start() # start DNS server# Now, U can use "nslookup" command to test it# Such as "nslookup www.aa.com"
有防便有攻,任何的奇思妙想都可以发展成影响重大的安全问题…


本文章来自团队成员vr_system分享,仅供白帽子、安全爱好者研究学习,对于用于非法途径的行为,发布者及作者不承担任何责任。

我们建立了一个以知识共享为主的 免费精品 知识星球,旨在通过相互交流,促进资源分享和信息安全建设,为以此为生的工作者、即将步入此行业的学生等人士提供绵薄之力。目前星球已发布上千篇精品安全技术文章、教程、工具等内容,已加入上百位安全圈大咖及数千位安全从业者,期待在此共同与你交流。

如果你是安全行业精英,可以加入我们的微信群,目前聚集了来自全球的信息安全公司CEO,安全部门主管,技术总监,信安创业者,网络安全专家,安全实验室负责人,公司HR等。在这里将获得更多与安全大咖们面对面交流的机会,最新的安全动态,更真实的高薪信息安全岗位,更高效率的技术交流空间。可以扫码添加我的微信,需提供真实有效的公司名称+姓名,验证通过后可加入···

人已赞赏
安全工具

【HTB系列】靶机Access的渗透测试详解

2019-10-11 17:04:51

安全工具安全教程

由一则敏感文档泄露事件溯源说起

2019-10-11 17:05:03

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索