绕过安全设备的0day

admin 2019年8月1日02:15:01评论7 views字数 4217阅读14分3秒阅读模式
1
介绍

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

绕过安全设备的0day

2
原理

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

绕过安全设备的0day

1

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

绕过安全设备的0day

2

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

3

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

绕过安全设备的0day

4

绕过安全设备的0day

5
4
危害
一.隐藏恶意软件域名:
通过该方法可绕过基于DNS流量检测的流量分析软件、算法和相关安全设备及在线文件分析系统,通过构造加入特殊字符的DNS请求,既保证了域名成功解析,又保护了恶意域名难以被发现。
二.隐藏DNS隐蔽通道
将伪造源地址的方法与本文域名欺骗方法结合,可以起到更好的DNS隧道流量隐蔽效果,通过伪造源地址来分散DNS隧道流量,对内网数据泄密和远程控制隧道加入了新的挑战。
三.绕过DNS过滤

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

6
修复

1. 通过DNSPython修复,在dnsresolver.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分享,仅供白帽子、安全爱好者研究学习,对于用于非法途径的行为,发布者及作者不承担任何责任。

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

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

绕过安全设备的0day

绕过安全设备的0day

 

原文始发于微信公众号(糖果的实验室):绕过安全设备的0day

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2019年8月1日02:15:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   绕过安全设备的0dayhttps://cn-sec.com/archives/2499687.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息