本篇博文是對SQL手工注入進行基礎知識的講解,更多進階知識請參考進階篇(咕咕),文中有誤之處,還請各位師傅指出來。學習本篇以前,請先確保以及掌握瞭如下知識:php
文中全部例題選自sqlilab,能夠先配置好一塊兒邊看邊操做。由於虛擬機炸了,因此我本身搭建了一個簡陋的平臺,sqlilab可自行進行搭建練習,在後面的博客也會寫一些關於sqlilab的wp。html
在開始SQL注入以前,咱們首要須要瞭解SQL注入的原理,對於一個新安裝好的MySql數據庫,你至少會包含三個已經創建好的數據庫分別是user、infomation_schema、performance_schema以下圖所示mysql
而SQL注入要乾的,這些系統數據庫中存儲了MySql各個數據庫的屬性以及用戶信息,咱們要作的就是繞過過濾再來利用這些系統表進行查詢。sql
瞭解了這個以後咱們還要了解一點就是PHP的GET方法和POST方法傳參的區別,若是使用GET方法,則會自動進行一次url解碼,例如傳入%23實際獲得‘#’,而POST則會將數據原封不動的傳輸。下面咱們開始進入正式的SQL注入階段。數據庫
通常的對於SQL的查詢語句,有字符型和數值型查詢,而這二者的區別就是是否有單引號,這決定了咱們接下來應該如何構造SQL注入語句。ide
判斷方法有以下幾種,函數
除了上述幾種方法還能夠用其餘方法進行判斷,但原理都是構造SQL語句進行判斷。學習
經過上述方法知道了注入類型以後,咱們就能夠進行下一步的操做了,在這裏我搭建了一個簡易的存在字符型查詢漏洞的頁面,你們能夠在本地搭建一下一邊學習一邊練習。fetch
SQL代碼以下:flex
1 /* 2 Navicat MySQL Data Transfer 3 4 Source Server : Mysql 5 Source Server Version : 50553 6 Source Host : localhost:3306 7 Source Database : test 8 9 Target Server Type : MYSQL 10 Target Server Version : 50553 11 File Encoding : 65001 12 13 Date: 2019-09-18 23:17:53 14 */ 15 16 SET FOREIGN_KEY_CHECKS=0; 17 18 -- ---------------------------- 19 -- Table structure for secret_table 20 -- ---------------------------- 21 DROP TABLE IF EXISTS `secret_table`; 22 CREATE TABLE `secret_table` ( 23 `fl4g` varchar(32) DEFAULT NULL 24 ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 25 26 -- ---------------------------- 27 -- Records of secret_table 28 -- ---------------------------- 29 INSERT INTO `secret_table` VALUES ('flag_is_here'); 30 31 -- ---------------------------- 32 -- Table structure for student 33 -- ---------------------------- 34 DROP TABLE IF EXISTS `student`; 35 CREATE TABLE `student` ( 36 `id` int(11) NOT NULL AUTO_INCREMENT, 37 `name` varchar(255) DEFAULT NULL, 38 `class` varchar(255) DEFAULT NULL, 39 `age` int(11) DEFAULT NULL, 40 `note` varchar(16) DEFAULT '', 41 PRIMARY KEY (`id`) 42 ) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 43 44 -- ---------------------------- 45 -- Records of student 46 -- ---------------------------- 47 INSERT INTO `student` VALUES ('1', 'zhangsan', 'nss', '18', ''); 48 INSERT INTO `student` VALUES ('2', 'lisi', 'nss', '19', ''); 49 INSERT INTO `student` VALUES ('3', 'wangwu', 'nss2', '20', ''); 50 INSERT INTO `student` VALUES ('4', 'zhaoliu', 'nss2', '21', ''); 51 INSERT INTO `student` VALUES ('5', 'sunqi', 'nss2', '22', ''); 52 INSERT INTO `student` VALUES ('6', 'qianba', 'nss', '23', ''); 53 INSERT INTO `student` VALUES ('7', 'liujiu', 'nss', '24', '');
PHP代碼以下:
1 <?php 2 if (!empty($_POST['st'])) { 3 $conn = mysqli_connect("localhost","root","root","test"); 4 $name = $_POST['name']; 5 $sql = "select * from student where name='".$name."';"; 6 7 $result = mysqli_query($conn,$sql); 8 9 echo "<table border='1'> 10 <tr> 11 <th>Id</th> 12 <th>Name</th> 13 <th>Class</th> 14 <th>Age</th> 15 <th>Note</th> 16 </tr>"; 17 18 while($row = mysqli_fetch_array($result)) { 19 echo "<tr>"; 20 echo "<td>" . $row['id'] . "</td>"; 21 echo "<td>" . $row['name'] . "</td>"; 22 echo "<td>" . $row['class'] . "</td>"; 23 echo "<td>" . $row['age'] . "</td>"; 24 echo "<td>" . $row['note'] . "</td>"; 25 echo "</tr>"; 26 } 27 echo "</table>"; 28 29 mysqli_close($conn); 30 } 31 ?> 32 33 <!doctype html> 34 <!-- flag in SQL --> 35 <form action="" method="POST"> 36 <input type="text" placeholder="Input A Name" name="name" value=""> 37 <button type="submit" name="st" value='1'>提交</button> 38 </form> 39 <div style="position: absolute;bottom:0;width:100%;display:flex;flex-direction:column;"> 40 <hr > 41 <a style="align-self:center;" href="./source.txt">Source</a> 42 </div>
經過上面的判斷咱們知道是字符型注入,如今咱們須要查出這個數據表的列數來爲後面的聯合查詢作鋪墊。
當查詢語句最後爲where xx 的時候咱們使用order by num;
當查詢語句最後爲limit xx的時候咱們使用into @,@;
對於第一種order by num; num表明數值,語義就是以第幾列進行排序,當列不存在是就會報錯,咱們就能夠用二分的方法找出正確的列數。以下
對於lisi' order by 6#,lisi是數據庫中的正常數據,單引號是爲了閉合前面的select語句,#是mysql的單行註釋語句,提示報錯,換成5則正確,說明該表有5列。
對於limit xx的狀況,我會在另外一篇額外講解。
當咱們得到表的列數以後,就能夠經過聯合查詢得到數據庫的信息,但在此以前,咱們還須要肯定每一個字段顯示在網頁上的位置,方便查看後面的數據。
對於例題,咱們知道列數爲5以後,構造參數lisi' and 1=2 union select 1,2,3,4,5#便可知道每一個字段的位置
完整的SQL語句就是select * from student where name = ‘lisi’ and 1=2 union select 1,2,3,4,5#';
and 1=2是爲了不一些只顯示一行的頁面過濾掉後面的聯合查詢的數據,因此讓前面的查詢條件永遠爲false,這裏你不加這個,但爲了方便咱們都加上這個。
union就是合併操做,完整語義就是使用union合併兩個select的結果集,前者爲空,後者結果爲1,2,3,4,5,因此就會把這些結果合併而後顯示到對應的字段。
當列數和字段位置都知道後,咱們就能夠經過進一步的查詢來獲取數據庫信息了,下面提供一些經常使用的數據庫函數。
database():查看當前數據庫名稱
version():查看數據庫版本信息
user():返回當前數據庫鏈接的用戶
char():將ASCII碼轉化成字符,用於分隔每一個字段的內容
使用方法就是將函數放在列的位置,例如提交lisi' and 1=2 union select 1,user(),database(),4,5#,結果以下:
一樣在最開始的時候咱們提到了MySql的幾個系統表,咱們也能夠從這些系統表中獲取全部表名,列數,字段名等數據。
例如查詢全部表名,提交lisi' and 1=2 union select 1,2,3,4,TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='test,結果以下
第五個位置也就是查詢INFORMATION_SCHEMA.TABLES表中數據庫爲test的全部表名,MySql的表屬性會存儲在這個表中,由於後面還有一個單引號,因此這裏右邊就不要單引號了。
這樣咱們就查詢到了全部的表名,如今咱們發現有個表叫作secret_table,猜想flag隱藏在其中,那麼咱們再來獲取這個表全部的字段名。
提交lisi' and 1=2 union select 1,2,3,4,COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='secret_table。結果以下
語義同上,這裏就不在作解釋了,如今咱們能夠看到這個表有一個叫fl4g的字段,那flag就藏在這裏沒錯了。
接下來要作的就簡單了,咱們只須要提交lisi' and 1=2 union select 1,2,3,4,fl4g from secret_table#
到此咱們就找到了最終的flag。
SQL手工注入的基礎知識道馳就結束了,推薦看完了這篇再去學習SQLmap的知識,能夠很快上手也能夠了解其本質的東西,不推薦直接學習SQLmap成爲腳本小子。
再梳理一遍上述知識,首先找到注入點,注入點通常是網頁的某個提供查詢的地方,而後肯定是字符型仍是數值型,當肯定了注入類型以後,就是進行肯定一些數據表的信息,方便後面的盲注,最後在從這些注入點獲得咱們想要的信息。
對於CTF題目來講,通常不是讓你盲注,會將代碼給你,這時候注入點確定是會有諸多的過濾,這時候咱們就不能直接執行上述語句了,咱們就須要構造一些語句去繞過執行,但最終要達到的效果和上述內容是一致的。
對於更多的SQL注入知識,以及一些繞過技巧我將會在後面的進階篇詳解闡述。