一、Sql注入简介
Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击,它目前黑客对数据库进行攻击的最常用手段之一。
二、Web 程序三层架构
三层架构(3-tier architecture) 通常意义上就是将整个业务应用划分为:
- 界面层(User Interface layer)
业务逻辑层(Business Logic Layer)
数据访问层(Data access layer)。
区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构被应用于众多类型的软件开发。
由数据库驱动的Web应用程序依从三层架构的思想也分为了三层:
表示层。
业务逻辑层(又称领域层)
数据访问层(又称存储层)
拓扑结构如下图所示:
在上图中,用户访问实验楼主页进行了如下过程:
在 Web 浏览器中输入 www.shiyanlou.com 连接到实验楼服务器。
业务逻辑层的 Web 服务器从本地存储中加载 index.php 脚本并解析。
脚本连接位于数据访问层的 DBMS(数据库管理系统),并执行 Sql 语句。
数据访问层的数据库管理系统返回 Sql 语句执行结果给 Web 服务器。
业务逻辑层的 Web 服务器将 Web 页面封装成 HTML 格式发送给表示层的 Web 浏览器。
表示层的 Web 浏览器解析 HTML 文件,将内容展示给用户。
在三层架构中,所有通信都必须要经过中间层,简单地说,三层架构是一种线性关系。
发现
找到有效SQL注入的第一部分是发现漏洞。而其中最重要的部分是知道在你的输入会在哪一SQL语境中结束。这里有一些基本的例子:
· SELECT user_input FROM tournament ORDER BY region;
· INSERT INTO tbl_name (col1,col2) VALUES (15,user_input);
· DELETE FROM somelog WHERE texts ='user_input'
现在我们可以看到我们的user_input在不同语境中结束:在()之间,在’之间或者没有任何分隔符。这些命令的共同之处是,一旦’作为输入进行注入,它们就会失效。如果使用了不存在的系统变量,前两个即使没有分隔符也会给出一个错误。像:@@versionz'(取代@@version)。
一旦你能够让这个服务返回错误(主要是HTTP 500),你需要确认是SQL命令引起这个错误而不是一些类似数据解析器的东西。要做到这一点,你可以使用一系列技巧:
| 如果一个’导致错误,试着查看’能否成功(因为反斜杠在MySQL中取消了单引号)
l 你也可以尝试注释掉’就会返回成功消息,就像:%23’或者--’。这是因为你告诉MYSQL忽略注释之后的所有东西,包括额外的’。
l 如果’是不允许,你可以在有效和无效的系统变量之间比较,类似@@versionzvs @@version',或在无效函数和有效函数之间的比较:SLEP(5) vsSLEEP(5)。
l 有些时候你的输入会在()之间结束,以确保你可以测试input)%23,也会查看你是否可以突破这些,例如利用UnionSQL注入( input)order by 5%23)。
l 如果正常的输入只是一个整数,你可以尝试减去一些量,然后查看减法是否有效( id=460-5 )。
l 试着看偶数的引号是否会返回成功(例如460''或460-'')并且奇数的引号会导致错误(460' 或460-''')。
利用
在你找到SQL注入之后,你总是要尝试证明输出或者输出中的敏感数据有差异。不幸的是这并不总是直截了当的,特别是防火墙和黑名单会妨碍你。这部分是帮助你绕过那些问题。
防火墙
当你处理防火墙时,你要做的第一件事是看你是否可以在一个设置中找到错误配置。对于大多数防火墙和CDN,你可以通过访问真实IP并用真实域名作为主机值访问不受保护的网站(防火墙在IP前面)。这里第一步应该是找到网站的原始IP,使用追踪某个网站所使用的IP服务通常不会太难。通常用在防护墙ip前的那一个是他们仍在使用的那一个(基本上就是后面所说的cloudflare 或 akamai)。在寻找真实IP时Shodan 也非常有用。
在你找到真实IP地址之后,尝试访问带有真实主机HTTP头信息的网站。在cURL中这部分会这样工作的(添加头文件也适用于sqlmap):
$ curl --header 'Host: www.example.com' 'https://54.165.170.2/' -v -k
很不幸我的目标被禁止了。所以我必须具有创造力。在这我发现通过给主机域名后面添加一个点(http://www.example.com.)不会受限制而且仍然允许我浏览这个直接IP。在你的/etc/hosts文件中添加:
54.165.170.2 http:www.example.com
就会允许你在浏览器中浏览http://www.example.com,并且在这之间没有任何防火墙。从这开始,所有的限制都应该消失,而且SQL注入的利用应该相当容易
绕过黑名单
有时, 您无法绕过防火墙, 这将要求您绕过已就位的 (可能) 黑名单。这里我最大的提示是, 谷歌是你的朋友。找到具有强大功能的利基功能, 要求您以某种方式提取数据。但是这里有一些常见的提示:
1. 在查询中使用注释作为空格以破坏防火墙中的正则表达式或词组。例如:2/dhab bc/OR/dahdshka/2/sd/LIKE/da/"2"/**/%23就是:2 OR 2=2%23。
2. 如上例所示,用 LIKE 代替 =,用 2 代替 1,用 ” 代替 ’ 。很多人都很懒,防火墙也是如此,走小路会让许多防火墙措手不及。
3. 正如我上面说的用Google找niche函数。例如,我发现MID()和SUBSTRING()的工作方式一样,然而,后一个是被禁止的,第一个不是。变量也是一样的CURRENT_USER()是被禁止的,而CURRENT_USER不是。
把它们放在一起
从SQL注入发现,这很可能是我最喜欢的。因为利用真的非常难。所以我将详细地描述它,并且希望你们可以从中学到一些东西。
我偶然发现这个URL:
https://www.example.com/php/analyticsExcel.php?action=res_unique_analytics&resid=2100935&start_date=2016-07-11 00:00:00&end_date=2017-08-11 23:59:59&action=res_unique_analytics&entity_type=restaurant
这里我有2100935的合法访问权,而且请求2100935将会导致未经验证的错误。奇怪的是,在2100935后面添加一个引号,就会导致错误500,添加两个引号就会使URL再次起作用。在这里,为了让利用更容易,我用www.example.com.绕过。在那里,我试图在输入中找到一个地方在输入中注入我自己的SQL命令,然而我没有设法在字符串中插入注释,因此我断定这个查询是非常复杂的。考虑到这一点我决定把侧重点烦躁一个更简单的OR语句上,这个语句几乎可以在任何地方注入。在这我注意到' OR 1='1会返回状态码200,' OR 1='2,' AND1='1,' AND 1='2也都是一样的。而且最终似乎没有哪个sleep()命令有效果。
非常奇怪的是,我可以确定我注入的语法是正确的,但是没有任何技巧可以提取数据。在这我尝试' OR@@version=5,它会导致状态码200,以及' OR@@versionz=5,它会导致错误500。这至少证实我正在使用MySQL并表明注入确实在这里。这里指向MySQL的IF函数。我的想法是如果我可以根据if语句是真是假来返回一个无效函数,我就可以证明数据提取。由于MySQL似乎在验证IF语句之前验证了命令,但是进展并没有如想象般顺利。但是IF语句最终成为解决方案的一部分。在一个具有正整数的IF语句中输入一个有效的SLEEP命令将会导致服务器超时,而从IF语句中返回一个简单的整数时,将会迅速返回一个状态码200。从这里我利用了下面的POC:
TRUE: if @@version starts with a 5:
2100935' OR IF(MID(@@version,1,1)='5',sleep(1),1)='2
Response:
HTTP/1.1 500 Internal Server Error
False: if @@version starts with a 4:
2100935' OR IF(MID(@@version,1,1)='4',sleep(1),1)='2
Response:
HTTP/1.1 200 OK
总结
如果注入一个单引号会在响应中导致不同的输出,请尝试使用这篇博客中列出的其他的技术,以查看你是否在处理SQL注入。在确定了你正在使用的SQL上下文之后,利用一个POC,它可以显示敏感信息(基于error或union),或者根据问题是真是假(基于布尔和时间)来显示输出中的差异。