根据bugbountyforum.com的AMA版面,最常见的问题之一就是:如何测试服务器端漏洞,例如SQL注入?直到最近我还在努力解决这个问题(特别是关于SQL注入)。我发现,SQL注入经常被发现并被sqlmap利用的,之后我发现这个服务器对单引号的反应很奇怪。然而在过去的两个月,我被迫深入到手工SQL注入发现,因为我的目标有一个很强的防火墙可以使sqlmap失效。在这两个月里我能够找到超过10个SQL注入,从这些中我学到了很多。这个博客也会专注于分享我的过程和知识以及展示现实中的例子。我将会把这个博客分成两个部分:发现和利用,因为这两部分都需要很好的报告。最后,这个博客假设用的是MySQL,因为它拥有半数以上的市场份额。
发现
找到有效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 如果’
是不允许,你可以在有效和无效的系统变量之间比较,类似@@versionz
vs @@version'
,或在无效函数和有效函数之间的比较:SLEP(5)
vsSLEEP(5)
。
l 有些时候你的输入会在()
之间结束,以确保你可以测试input)%23
,也会查看你是否可以突破这些,例如利用UnionSQL注入( input)order by 5%23
)。
l 如果正常的输入只是一个整数,你可以尝试减去一些量,然后查看减法是否有效( id=460-5
)。
l 试着看偶数的引号是否会返回成功(例如460''
或460-''
)并且奇数的引号会导致错误(460'
或460-'''
)。
例子
想要把这些弄清楚,你需要下面这个URL:
这个基本的URL返回一个状态码200。输入poc=35141008'
以及poc=35141008'%23
返回错误500,但是poc=35141008''
返回状态200。这就提示,这个参数可能没有使用任何分隔符,因此我尝试了poc=35141008%23'
,它会返回状态码200。现在我们知道了我们可以在35141008
和%23
之间注入。在这之后我尝试了一个简单的35141008OR 2 LIKE 2%23
,它起作用了,但是35141008 OR 2 LIKE 1%23
返回错误500,在这里证明布尔类型的SQL注入是可行的。然而,这并不总是如此简单,而且这个例子并没有显示出太大的影响,这是下一章节的内容,证明数据检索。
利用
在你找到SQL注入之后,你总是要尝试证明输出或者输出中的敏感数据有差异。不幸的是这并不总是直截了当的,特别是防火墙和黑名单会妨碍你。这部分是帮助你绕过那些问题。
防火墙
当你处理防火墙时,你要做的第一件事是看你是否可以在一个设置中找到错误配置。对于大多数防火墙和CDN,你可以通过访问真实IP并用真实域名作为主机值访问不受保护的网站(防火墙在IP前面)。这里第一步应该是找到网站的原始IP,使用追踪某个网站所使用的IP服务通常不会太难(http://viewdns.info/iphistory/)。通常用在防护墙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:
这里我有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),或者根据问题是真是假(基于布尔和时间)来显示输出中的差异。
*参考来源:Manual SQL injection discovery tips,文章内容以原文为准,本文由丁牛网安实验室小编EVA编辑整理,如需转载请标明出处。
来源:freebuf.com 2018-04-26 20:49:20 by: DigApis
请登录后发表评论
注册