1. 程式人生 > 其它 >SQL注入中information_schema的作用學習記錄

SQL注入中information_schema的作用學習記錄

在MySQL資料庫的注入中,如果你有仔細看過SQL注入語句的話,你可能就會發現,在獲取資料庫名、表名和欄位的時候,注入語句中information_schema這個資料庫出現得很頻繁,那麼有沒有想過為什麼會需要用到這個資料庫呢? 這個資料庫又是什麼?它裡面儲存了什麼?

information_schema資料庫是MySQL自帶的,MySQL 5以下沒有這個資料庫,它提供了訪問資料庫元資料的方式。什麼是元資料呢?元資料是關於資料的資料,如資料庫名或表名,列的資料型別,或訪問許可權等。也就是說information_schema中儲存著關於MySQL伺服器所維護的所有其他資料庫的資訊。如資料庫名,資料庫的表,表欄的資料型別與訪問許可權等。在INFORMATION_SCHEMA中,有數個只讀表。

在phpmyadmin中,在左側點選information_schema資料庫。

展開後如下圖,顯示了該資料庫中的所有表,由於表數量太多,只截了一部分,可以拉動右邊的滾動條檢視所有表。

也可以執行如下SQL語句來檢視該庫中的所有表:

需要注意的是,要在information_schema這個資料庫中執行該SQL語句。如何進入information_schema資料庫執行SQL語句,請參考前面進入sqli資料庫執行SQL語句的步驟。

這上面顯示的表,它們實際上是檢視,而不是基本表,所以你在資料庫的資料儲存目錄,會看不到這個資料庫的實體檔案。資料庫的資料儲存在 C:\wamp\bin\mysql\mysql5.6.17\data 目錄,在這個目錄下一共有如下4個目錄:

想要檢視資料庫的資料儲存目錄,可以執行select @@datadir,如下圖:

每一個目錄對應資料庫中的一個數據庫,在資料庫中執行show databases;的時候,可以看到存在5個數據庫,正是少了information_schema這個資料庫。

在SQL注入中,我們重點關注的表有如下幾個,因為主要的時候主要利用這幾個表來獲取資料:

SCHEMATA:提供了當前mysql資料庫中所有資料庫的資訊,其中SCHEMA_NAME欄位儲存了所有的資料庫名。show databases的結果取自此表。

TABLES:提供了關於資料庫中的表的資訊,詳細表述了某個表屬於哪個schema,表型別,表引擎,建立時間等資訊,其中table_name欄位儲存了所有列名資訊,show tables from schemaname的結果取自此表。

COLUMNS:提供了表中的列資訊。詳細表述了某張表的所有列以及每個列的資訊,其中column_name儲存了所有的欄位資訊。show columns from schemaname.tablename的結果取自此表。

為了更好地說明這些表的作用,我們進入mysql終端。

會彈出一個命令列視窗,這就是mysql客戶端,此時要求輸入密碼,由於root的密碼為空,直接回車即可。

進入information_schema 資料庫,命令為:use information_schema;。一定要注意後面記得加分號,分號表示一個語句結束,如果沒有檢測到你輸入分號,它會認為你一個語句還沒結束,直到碰到分號後,才開始執行語句。

首先執行show databases;檢視所有的資料庫,然後再執行select schema_name from schemata;。

可以看到他們的作用是一樣的,都是列出所有資料庫,跟我們前面說的一樣,SCHEMA_NAME欄位儲存了所有的資料庫名。

所以,在注入中,我們可以通過注入select schema_name from schemata 來查詢的當前資料庫中所有的資料庫名,如果你去檢視一些爆資料庫名的注入語句,就會發現裡面包含這麼一句:select schema_name from information_schema.schemata limit 0,1,其原理就是通過查詢information_schema.schemata中schema_name的結果,其中limit 0,1用來獲取第一條記錄,通過遞增第一個引數,可以每次獲取一條記錄,也就是一次獲取一個數據庫名,直到出現錯誤為止,說明沒有更多的錯誤。

通常在獲取了資料庫名後,就會選擇感興趣的資料庫,然後來獲取其中的資料,首先需要獲取感興趣的資料庫中的所有表名,通過查詢information_schema庫中的TABLES表就可以獲取表名。

在TABLES表中,它儲存了所有資料庫中的所有表名以及這個表所屬的庫,意思是說,不管你在哪個資料庫中的表,在這裡都會有一條記錄對應,如果你在一個數據庫中建立了一個表,相應地在這個表裡,也會有一條記錄對應你建立的那個表。

desc 可以用來看錶結構。看下tables的表結構,執行desc tables;,結果如下圖:

注意上圖中標記的那2條記錄,每一條記錄中,他們分別記錄一個表名和一個這個表所屬的庫名。其中TABLE_NAME儲存的是表名,而TABLE_SCHEMA儲存的是這個表名所在的資料庫。我們可以查詢一條記錄看看,在查詢前,先看看有多少條記錄,避免記錄太多檢視不方便,執行select count(*) from tables;結果如下圖:

說明當前所有資料庫中的表數量為142。查詢任意一條記錄檢視,我這裡選擇最後一條記錄,SQL語句為:select * from tables limit 141,1\G由於在客戶端中,預設查詢結果顯示不友好,所以,可以把語句後面的分號改成\G,他會讓一條記錄顯示一行,看起來不那麼亂。\G只支援在客戶端中用,在其他連線資料庫的軟體中,使用\G會報錯。

可以看到,最後一條記錄的TABLE_NAME是user,TABLE_SCHEMA為sqli。檢視sqli資料庫中的表,SQL語句為:show tables from sqli;可以看到確實存在user表。

既然information_schema的TABLES表中的TABLE_SCHEMTA欄位是儲存的資料庫名,而TABLE_NAME儲存了表名,那麼我們就可以使用TABLE_SCHEMTA欄位作為查詢條件,查詢TABLE_NAME,即可得知所有指定資料庫中的所有表名。比如,我們想要通過information_schema資料庫來查詢sqli資料庫中所有的表,那麼就可以使用如下SQL語句:

select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = 'sqli';

如果當前庫為information_schema,則可以省略不寫,否則跨庫查詢的時候,需要帶上庫名。結果如下圖:

通過修改TABLE_SCHEMA 的限制,可以查詢任意資料庫中的所有表名,網上的通過注入爆表名便是這個原理。

知道了表名,那麼如何獲取表中的欄位呢?要知道我們沒有表名的話,會把所有的資料查詢出來,而如果注入沒有回顯,不能進行union查詢,那麼想要獲取我們的標目資料,無疑效率極低。

幸運的是,在information_schema資料庫中,同樣存在一個表,它儲存了整個資料中,所有的列名,這個表就是COLUMNS。同樣先檢視該表結構。

這裡面,與注入相關的存在3個欄位,分別是TABLE_SCHEMA、TABLE_NAME以及COLUMN_NAME,不難猜到,如果在該表中查詢一條記錄,TABLE_SCHEMA儲存了這條記錄儲存的欄位所屬的資料庫名,而TABLE_NAME儲存的是該欄位所屬表名,COLUMN_NAME則是一個列名記錄,查詢一條記錄驗證一下,首先確定該表有多少條記錄,執行select count(*) from columns;,得知一共有1662條記錄,結果如下圖:

我們獲取最後一條記錄,執行select * from columns limit 1661,1\G

其中COLUMNS_NAME為ip,TABLE_NAME為user,TABLE_SCHEMA為sqli,這說明,在sqli這個資料中,user表存在一個ip的列,也就是我們常說的ip欄位。

檢視sqli的user表是否存在該欄位,執行SQL語句:show columns from sqli.user;

可以看到確實存在該欄位。

既然在columns中,TABLE_NAME儲存了欄位所屬的表名,TABLE_SCHEMA儲存了該欄位所屬的庫名,與通過TABLES表獲取表名一樣,我們就可以查詢把TABLE_NAME 和TABLE_SCHEMA做為查詢條件,查詢符合條件的COLUMN_NAME,也就是查詢指定資料庫中某表中的欄位。

比如,我們要通過information_schema資料庫的columns表查詢sqli資料庫中user表中所有的欄位,可以執行如下SQL語句:

select column_name from information_schema.columns where TABLE_SCHEMA='sqli' and TABLE_NAME='user';

查詢結果與show columns from sqli.user;一致。

知道了庫名、表名、欄位,如果有回顯且支援聯合查詢,就可以直接通過在注入點後面注入一個聯合查詢語句,即可直接獲取資料,如果不能回顯,則可能需要通過盲注獲取資料,可以參考MySQL盲注實驗。

課後習題: