SQL 基礎語法的詳細歸納
做安全最困難的恐怕就是知識點過於雜碎,涉及面過於廣闊,所以趁還記得,沒事就總結完善備忘一下(參考一些資料,並加入一些自己使用過程中的理解),以防老了失憶,平時短路了拿出來查一下也是不錯的。
0X01 基本語法
這一小節主要介紹的是一些增刪改查的基本語法
(1)select
mysql 中 select 命令中允許不出現資料庫或者資料表的名字,也就是可以沒有 from ,這種情況下能將執行結果返回成一個單行單列的表格,例如 select now();返回當前時間。
(2)count()
select count(列) … 可以確定資料表中的記錄條數
(3)limit offset,n
limit offset,n 指定輸出從 offset 開始的 n 條資料 , 其中 offset 是從 0 開始的 ,例如 limit 2,2 表示跳過兩條資料,從第三條資料開始輸出第三第四條資料(offset 不寫時,預設為0)
(4)order by
order by column 表示按照某一列對查詢結果進行排序
(5)where/having
where/having 表示對行進行篩選,其中 where 的優先順序高於 Having ,having 可以和 group by 配合使用,但是 where 不可以
(6)join
select table1.xxx,table2.xxx from table1 left join table2 on table1.yyy=table2.zzz/using(xxx); 是用來擴充套件查詢結果的列的,兩個表有鍵值是關聯的,如果沒有關聯就相當於笛卡爾積,沒什麼太大的意義
下表是常用的一些語句模型
(7)union
union 實現的是行的擴充套件,它將兩個查詢的結果合併在一起,並要求兩個查詢的列個數相同,資料型別相同,如果不同,系統將會自動將 union 後面的資料的型別轉換成前面的對應型別(union 是 mysql 中唯一的集合分隔符)
(8)group by
group by 配合如 count() sum() min() max() 等統計函式 計算有相同欄位的行的另一些欄位的統計資料,如果是想輸出有相同欄位的行的另一欄位的所有資料,可以使用 group_concat() 這個統計函式,否則只能輸出第一個,group by … with rollup 可以在結果的最後一行新增一個計算總和的行
(9)備份與還原資料表
備份:
create table newtable select * from oldtable;
還原:
delete from oldtable; insert into oldtable select * from newtable;
(10)備份和還原資料庫
使用外部工具 mysqldump
備份:
mysqldump -u loginame -pdbname > backupfile
還原:
沒有專門的恢復工具,我們直接使用下面的命令
mysql -u loginame -pdbname < backupfile
或者我們直接就能在互動模式下重建
create database dbname; use dbname; source backupfile;
(11)插入資料
intert into tablename(column1,column2...) values (value1,value2...),(value3,value4...);
(12)更新資料
update tablename set column1=value1,column2=value2,...where columnID=ID;
(13)刪除資料
delect from tablename where ID=ID;
(14)修改資料表
增加一個數據列:
alert table tablename add columnname clotype clooptions;
修改一個數據列:
alert table tablename change oldcolname newcolname coltype coloptions;
(15)information_schema 資料表家族
這是在 mysql5.0 以後引入的機制,有了這個機制,我們能在這些資料表中使用 select 命令檢索有關資料庫、資料表、資料列的元資料。
這是一個非常重要的機制,最明顯的體現就在於使用 sqlmap 跑 MySQL 資料庫的時候為什麼那麼方便,分分鐘就能把所有的資料庫和表列出來,有些名字甚至是很複雜的都沒有絲毫的影響,但是如果你嘗試跑 sql server 的資料庫,你就會發現情況大不相同, sqlmap 會告訴你需要進行暴力破解,然後就會從他自帶的字典中開始遍歷,不僅時間非常的長,而且效果也很差,一些自定義的不常見的名字永遠都不可能猜出來。
注意一下, information_schema 這個資料庫是虛擬的,是在計算機上沒有對應的檔案的(我們知道資料庫也是一個檔案,都儲存在計算機上,只不過不是純文字檔案罷了,有時候我們還能利用這個檔案進行
getshell操作)
所以我們也不能直接使用 use information_schema 、show databases 、select * from information_schema.schemata 來檢視有關資訊
但是我們能通過 show tables from information_schema 、show columns from information_schema.columns 檢視這個資料庫內部的資訊
mysql> select * from information_schema.schemata; +--------------+--------------------+----------------------------+------------------------+----------+ | CATALOG_NAME | SCHEMA_NAME| DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME | SQL_PATH | +--------------+--------------------+----------------------------+------------------------+----------+ | def| information_schema | utf8| utf8_general_ci| NULL| | def| ctf| utf8| utf8_general_ci| NULL| | def| employees| utf8| utf8_general_ci| NULL| | def| mysql| utf8| utf8_general_ci| NULL| | def| performance_schema | utf8| utf8_general_ci| NULL| | def| test| latin1| latin1_swedish_ci| NULL| | def| world| utf8| utf8_general_ci| NULL| +--------------+--------------------+----------------------------+------------------------+----------+ 7 rows in set (0.00 sec)
我們來看看這個資料庫中有哪些表,它們分別對應著什麼資料
還要補充的一點是,information_schema 資料表對所有使用者開放而無視許可權
0X02 解決方案
這一小節主要介紹的是一些一些實用的函式或者技巧
1.字串操作
(1)合併字串
concat(str1,str,str3...); concat_ws(separator ,str1 ,str2 ,…);
(2)擷取字串
1.substring(str,pos,n);/substr(str,pos,n);/mid(str,pos,n);
mysql> select substring('www.baidu.com','5',5); +----------------------------------+ | substring('www.baidu.com','5',5) | +----------------------------------+ | baidu| +----------------------------------+ 1 row in set (0.00 sec)
注意:
(1)這裡的 pos 是從 1 開始的,而不是從 0;
(2)n 不寫時表示取到字串末尾
2.letf(str,len); 返回從第一個字元開始的 len 個字元
3.right(str,len); 返回從最後一個字元開始的len 個字元
4.substring_index(str,delim,count);
mysql> select substring_index('www.baidu.com','.',1); +----------------------------------------+ | substring_index('www.baidu.com','.',1) | +----------------------------------------+ | www| +----------------------------------------+ 1 row in set (0.00 sec) mysql> select substring_index('www.baidu.com','.',2); +----------------------------------------+ | substring_index('www.baidu.com','.',2) | +----------------------------------------+ | www.baidu| +----------------------------------------+ 1 row in set (0.00 sec) mysql> select substring_index('www.baidu.com','.',-2); +-----------------------------------------+ | substring_index('www.baidu.com','.',-2) | +-----------------------------------------+ | baidu.com| +-----------------------------------------+ 1 row in set (0.10 sec)
(3)計算字串長度
char_length()/character_length() 和 length()/octet_length()
注意:char_length()/character_length() 計算的是字元的個數,而length()/octet_length() 計算的是位元組的長度
但是我在測試的時候發現即使使用中文兩者的結果還是一樣的,都是按照位元組去計算的,可能需要進一步考證
mysql> select LENGTH('你好'); +----------------+ | LENGTH('你好')| +----------------+ |4 | +----------------+ 1 row in set, 1 warning (0.00 sec) mysql> select CHAR_LENGTH('你好'); +---------------------+ | CHAR_LENGTH('你好')| +---------------------+ |4 | +---------------------+ 1 row in set, 1 warning (0.00 sec)
(4)改變儲存的字串
將 colname 列的雙引號換成單引號
replace(colname,'"','\'');
(5)判斷子字串在字串中出現的位置
1.locate()
返回子串 substr 在字串 str 中第一次出現的位置。如果子串 substr 在 str 中不存在,返回值為 0:
LOCATE(substr,str);
例如:
mysql> SELECT LOCATE('bar', ‘foobarbar'); -> 4 mysql> SELECT LOCATE('xbar', ‘foobar'); -> 0
返回子串 substr 在字串 str 中的第 pos 位置後第一次出現的位置。如果 substr 不在 str 中返回 0 :
LOCATE(substr,str,pos)
例如:
mysql> SELECT LOCATE('bar', ‘foobarbar',5); -> 7
2.instr()
在一個字串 str 中搜索指定的字元 substr,返回其第一次出現的位置 index ,其中 index 從 1 開始,如果不存在返回 0 ;
instr(str,substr);
例如:
instr('123', '12'); ->1 instr('123', '23'); ->2
3.position()
是 locate() 的別名,用法完全一樣
4.find_in_set()
FIND_IN_SET(str,strlist)
-
假如字串str在由N子鏈組成的字串列表strlist中,則返回值的範圍在1到N之間。
-
一個字串列表就是一個由一些被‘,’符號分開的自鏈組成的字串。
-
如果第一個引數是一個常數字符串,而第二個是typeSET列,則FIND_IN_SET()函式被優化,使用位元計算。
-
如果str不在strlist或strlist為空字串,則返回值為0。
-
如任意一個引數為NULL,則返回值為NULL。這個函式在第一個引數包含一個逗號(‘,’)時將無法正常執行。
注:strlist:一個由英文逗號“,”連結的字串,例如:”a,b,c,d”,該字串形式上類似於SET型別的值被逗號給連結起來。
例如:
SELECT FIND_IN_SET('b','a,b,c,d'); //返回值為2,即第2個值
(6)正則匹配
1.like
like 常用萬用字元:% 、_ 、escape
% : 匹配0個或任意多個字元 _ : 匹配任意一個字元 escape : 轉義字元,可匹配%和_。如SELECT * FROM table_name WHERE column_name LIKE '/%/_%_' ESCAPE'/'
2.regexp/rlike
rlike和regexp:常用萬用字元:. 、* 、 [] 、 ^ 、 $ 、{n}
. : 匹配任意單個字元 * : 匹配0個或多個前一個得到的字元 [] : 匹配任意一個[]內的字元,[ab]*可匹配空串、a、b、或者由任意個a和b組成的字串。 ^ : 匹配開頭,如^s匹配以s或者S開頭的字串。 $ : 匹配結尾,如s$匹配以s結尾的字串。 {n} : 匹配前一個字元反覆n次。
注意:
(1)rlike 和 regexp 並不是完全匹配,而是隻要模板匹配字串的一部分就可以了,但是 like 是完全匹配
(2) Like 和 rlike 和 regexp 都是不區分大小寫的
3.字串的二進位制比較
我們想要區分大小寫就要通過這種二進位制比較的方式
mysql> select "A" = "a"; +-----------+ | "A" = "a" | +-----------+ |1 | +-----------+ 1 row in set (0.00 sec) mysql> select "A" = BINARY "a"; +------------------+ | "A" = BINARY "a" | +------------------+ |0 | +------------------+ 1 row in set (0.10 sec)
(7)自定義排序
1.elt()
ELT(N ,str1 ,str2 ,str3 ,…)
函式使用說明:若 N = 1 ,則返回值為 str1 ,若 N = 2 ,則返回值為 str2 ,以此類推。 若 N 小於 1 或大於引數的數目,則返回值為 NULL 。 ELT() 是 FIELD() 的補數
mysql> SELECT ELT(3,'hello','halo','test','world'); +--------------------------------------+ | ELT(3,'hello','halo','test','world') | +--------------------------------------+ | test| +--------------------------------------+ 1 row in set
盲注中我們可以這樣將其作為開關
mysql> select ELT(1,1); +----------+ | ELT(1,1) | +----------+ | 1| +----------+ 1 row in set (0.00 sec) mysql> select ELT(1,0); +----------+ | ELT(1,0) | +----------+ | 0| +----------+ 1 row in set (0.00 sec)
2.field()
FIELD(str, str1, str2, str3, ……)
order by field(str,str1,str2,str3,str4……),str與str1,str2,str3,str4比較,其中str指的是欄位名字,
意為:欄位str按照字串str1,str2,str3,str4的順序返回查詢到的結果集。如果表中str欄位值不存在於str1,str2,str3,str4中的記錄,放在結果集最前面返回。
假如說表是這樣的:
root@localhost|iris>select * from ta; +----+--------+------+-------+ | id | name| age| class | +----+--------+------+-------+ |1 | iris|11 | a1| |2 | iris|22 | a2| |3 | seiki|33 | a3| |4 | seiki|44 | a4| |5 | xuding |55 | a5| |6 | xut|66 | a6| |7 | iris|12 | a2| |8 | iris|24 | a4| |9 | seiki|36 | a6| | 10 | seiki|48 | a8| | 11 | xuding |50 | a0| | 12 | xut|77 | a7| +----+--------+------+-------+ 12 rows in set (0.00 sec)
按照’seiki’,’iris’,’xut’來排序,結果如下:
root@localhost|iris>select * from ta order by field(name,'seiki','iris','xut'); +----+--------+------+-------+ | id | name| age| class | +----+--------+------+-------+#不在str1,str2,str3中的內容,放在最前面返回,str值相同按照主鍵的順序 |5 | xuding |55 | a5| | 11 | xuding |50 | a0| |3 | seiki|33 | a3| |4 | seiki|44 | a4| |9 | seiki|36 | a6| | 10 | seiki|48 | a8| |1 | iris|11 | a1| |2 | iris|22 | a2| |7 | iris|12 | a2| |8 | iris|24 | a4| |6 | xut|66 | a6| | 12 | xut|77 | a7| +----+--------+------+-------+ 12 rows in set (0.00 sec)
當然這是正常的用法,但是其實還可以這樣理解,這個函式返回的是str 在後面這些字串中的索引
mysql> SELECT FIELD('halo','hello','halo','test','world'); +---------------------------------------------+ | FIELD('halo','hello','halo','test','world') | +---------------------------------------------+ |2 | +---------------------------------------------+ 1 row in set
其實我們還可以把這個函式看成是一個開關,或許在盲注的時候能代替 if ,例如下面這樣
mysql> select FIELD(1,0); +------------+ | FIELD(1,0) | +------------+ |0 | +------------+ 1 row in set (0.00 sec) mysql> select FIELD(1,1); +------------+ | FIELD(1,1) | +------------+ |1 | +------------+ 1 row in set (0.00 sec)
這裡的第二個引數決定了這條語句的執行結果,和 if 還是比較類似的,有時候出現 NULL 的時候還可以配合 isnull() 這個函式將其轉換成 布林值,這裡提一下,說不定也能用的到。
2.變數與條件表示式
(1)變數
mysql 5.0 以後開始支援儲存過程,變數是儲存過程中比較重要的元素,MySQL 的變數分為三個型別
1.變數型別:
1.普通變數:以@開頭,在 sql 連線關閉時失去內容
2.系統變數:以@@開頭,表示的是mysql 伺服器的工作狀態和屬性,許多系統變數有兩種形式,一種表示當前連線 @@session.wait_timeout,另一種表示mysql 伺服器 @@global.wait_timeout
mysql> select @@session.wait_timeout; +------------------------+ | @@session.wait_timeout | +------------------------+ |28800 | +------------------------+ 1 row in set (0.00 sec) mysql> select @@global.wait_timeout; +-----------------------+ | @@global.wait_timeout | +-----------------------+ |28800 | +-----------------------+ 1 row in set (0.00 sec)
3.儲存過程的區域性變數:沒有特殊的標誌,只在儲存過程內有效
2.變數賦值:
變數賦值有兩種方式
1.set 賦值的時候 使用 =
2.select 賦值的時候使用 := 或者是 into
mysql> set @varname = 3; Query OK, 0 rows affected (0.00 sec) mysql> select @varname; +----------+ | @varname | +----------+ |3 | +----------+ 1 row in set (0.00 sec) mysql> select @varname := 4; +---------------+ | @varname := 4 | +---------------+ |4 | +---------------+ 1 row in set (0.00 sec) mysql> select @varname; +----------+ | @varname | +----------+ |4 | +----------+ 1 row in set (0.00 sec) mysql> select count(*) from bsqli into @varname; Query OK, 1 row affected (0.32 sec) mysql> select @varname; +----------+ | @varname | +----------+ |2 | +----------+ 1 row in set (0.00 sec)
2.變數賦值:
我們可以使用表示式給變數賦值,請看下面的實驗
mysql> select ID ,@rownum:=@rownum+1 as rownum from city order by ID limit 10; +----+--------+ | ID | rownum | +----+--------+ |1 |11 | |2 |12 | |3 |13 | |4 |14 | |5 |15 | |6 |16 | |7 |17 | |8 |18 | |9 |19 | | 10 |20 | +----+--------+ 10 rows in set (0.00 sec)
這樣就實現了逐個增加,是不是很神奇?我們再來看下面的測試
mysql> select @b:=@b is not null; +--------------------+ | @b:=@b is not null | +--------------------+ |0 | +--------------------+ 1 row in set (0.00 sec) mysql> select @b:=@b is not null; +--------------------+ | @b:=@b is not null | +--------------------+ |1 | +--------------------+ 1 row in set (0.00 sec)
這個怎麼理解呢?我們先來看賦值號後面的這個表示式
@b is not null
由於 @b 並沒有在當前的會話中定義過,於是一開始是 null ,然後我們做了個判斷,判斷 @b 不是 null ,很明顯這個得到的是假,也就是 0 ,那麼再執行完這個語句之後 @b 就被賦值為 0 ,當第二次再執行的時候就是判斷 0 不是 Null ,這次肯定是真,於是返回1 此時 @b 就被賦值為1
這樣就達到了在同一個會話中,相同的語句執行結果不同的效果,之前在 LCTF2018 中有一道題就是利用這種方式去做的,但是沒人解出來。
(2)條件表示式
1.if(condition,result1,result2);
當 condition 結果為真時返回 result1 否則返回 result2,這常常用作我們盲注的開關函式,
mysql> select if(1,1,0); +-----------+ | if(1,1,0) | +-----------+ |1 | +-----------+ 1 row in set (0.00 sec) mysql> select if(0,1,0); +-----------+ | if(0,1,0) | +-----------+ |0 | +-----------+ 1 row in set (0.00 sec)
2.case(…)when…then…else…end
case 有兩種變體
(1)case expr when val1 then result1 when val2 then result2 else result3 end;
就是會判斷 expr 的結果 ,根據結果是 val1 還是 val2 或者其他,來執行不同的語句
(2)case when condition1 then result1 when condition2 result2 else result3 end;
這種就是不判斷值,直接根據不同情況進行選擇
比如可以構造這樣一條注入語句
select * from test where id =-1 union select 1,case when username like 'a%' then 0 else 2222222222222222222 end,3,4 from tdb_admin
3.子查詢
(1)模板一
select ... from ... where col = [any|all](select...);
1.在這個變體裡,子查詢的返回結果必須是一個離散值(一行一列),用來和 col 進行比較操作,這裡的比較操作符=還可以是 > >= < <= <>
2.當前面有修飾符的時候,前面的修飾符可以是 any/some( 這兩個同義)和 all,這時子查詢可以返回多個值,col = any… 等同於 col = in … ,當前面的修飾符是 all 的時候只有兩種情況能讓 operator all 的返回結果為真,一種是在所有的值都能滿足這個 operator 條件時,另一種是在 select 子查詢沒有任何返回結果時
(2)模板二
select ....from ... where col [not]in(select...);
此時select 子查詢的返回結果可以是一個離散值的列表
(3)模板三
select row(value1,valu2...) = [any/some](select col1,col2...);
該語句查詢資料表中師傅存在一條對應的記錄,子查詢返回的結果是一組離散值,但是當使用 any 或者其同義詞 some 修飾的時候可以返回多組離散值,其中只要有一組滿足條件結果則為真
(4)模板四
select ... from ...where col [not]exists(select...);
(5)模板五
select ... from (select ...) as name where ...
外層 select 查詢的是內層 select 查詢出來的臨時表,MySQL 要求這種臨時表必須通過 as name 的形式給予別名
注意:mysql 不允許在子查詢中使用 limit ,limit 一定是在最外層並且最後執行的
(6)補充
當然除了上面這幾種類別以外,子查詢的位置還能放在 select 後面作為選擇列,或者放在 order by 後面作為拍序列,但是在 sql 注入中子查詢的缺點是不能回顯,union 倒是可以,只要有回顯位,但是現在能回顯的越來越少了,一般都是盲注什麼的。
0X03 其他細節
(1)命名規則:
資料庫、資料表、資料欄位等資料庫物件的名字長度最多達到64字元,允許使用的字元是 MySQL 允許使用的字符集中的所有數字字母符號以及 _ 和 $
注意:
(1)包含特殊字元和保留字的名字一般不被使用,但是還是可以通過新增 反引號的方式來使用,但是同樣這樣的欄位是沒法直接用命令列查詢的,查詢的時候也要帶上反引號
(2)當使用不在該資料庫的資料表的時候,要使用 資料庫.資料表 的形式,同理,如果遇到不能區分的列,也要使用類似的方式進行區分
(2)字串
字串可以用單引號包裹也可以用雙引號,當出現引號裡面還有引號的使用,必須使用不同的引號加以區分,或者使用反斜槓進行轉義
MySQL 支援使用 16進位制值表示物件
(3)數值
1.MySQL 支援十進位制使用小數點,並且可以使用 用e 表示的科學計數法表示很大或者很小的數值
2.MYSQL 支援使用 0x 或者 x’12121’ 表示16進位制資料
mysql> select 0x4142434445464748494a; +------------------------+ | 0x4142434445464748494a | +------------------------+ | ABCDEFGHIJ| +------------------------+ 1 row in set (0.00 sec) mysql> select x'4142434445464748494a'; +-------------------------+ | x'4142434445464748494a' | +-------------------------+ | ABCDEFGHIJ| +-------------------------+ 1 row in set (0.00 sec)
3.MySQL 支援進位制之間的隱式轉換,比如 16進位制加一個十進位制的數會變成十進位制
mysql> select 0x41; +------+ | 0x41 | +------+ | A| +------+ 1 row in set (0.00 sec) mysql> select 0x41+0; +--------+ | 0x41+0 | +--------+ |65 | +--------+ 1 row in set (0.00 sec)
4.MySQL 支援字串和數值之間的轉換,字串在和數值運算時會將字串轉換成數值(開頭部分是數值則轉化成對應數值,如果不是數值則自動轉化成0)
mysql> select "112.3456asdasd"+1; +--------------------+ | "112.3456asdasd"+1 | +--------------------+ |113.3456 | +--------------------+ 1 row in set, 1 warning (0.00 sec)
5.MySQL 5.03 開始支援二進位制數值,可以表示為 b’100110’ 的形式
(4)註釋
1.單行註釋:# 和 – (後面有一個空格)
*/
3.內聯註釋:`/
!*/`