应用程序指纹识别
指纹识别是web应用程序测试的第一个步骤。指纹识别会给测试者提供很多有用的信息,某种程度上暴露其他漏洞的脆弱性,更容易实施成功的漏洞攻击。
识别web服务器
识别web服务器是为了尝试检索尽可能多的有关服务器的信息,有以下几点:
服务器的名字和版本
后台是否用了应用程序服务器
后台用的数据库,数据库和服务器是不是在同一机器上
是否使用反向代理
负载均衡策略
运用哪种程序设计语言
检索服务器的名字和版本通过检查http头就很容易得到:
$ telnet vulnerab le 80
GET / HTTP/1.1
Host : vulnerable
HTTP/1.1 200 OK
Date: Sun, 03 Mar 2013 10:56:20 GMT
Server: Apache/2.2.16 (Debian)
X -Powered-By: PHP/5.3.3-7+squeeze14
Content -Length: 6988
Content -Type: text /html |
你也可以用一个假的主机头(或者IP)来获取默认的虚拟主机以进一步获取更多的信息
$ telnet vulnerable 80
GET / HTTP/1.1
Host : this is a bad value |
浏览网站
在指纹识别过程中要做的另一件事是简单的浏览下该网站,发现一些有意思的网站功能:
上传下载功能
认证表单和链接:登陆,登出,密码找回功能等
管理员功能部分
可留下数据部分:留言,联系我们等等表单
在这个阶段,有个要做的有意思的事是检查页面源代码检索html注释语。注释部分经常会提供一些关于网站的有意思的信息。所有的浏览器都能让你查看页面源代码。你可以查找html的注释标签:在<!—和–>之间的所有信息。大多数情况下,源代码都有颜色标注的,注释部分很容易被认出来。
网站页面文件的后缀能告诉你网站用的哪种脚本语言:
如果你看到.php结尾的文件,那么这个网站用的脚本程序是PHP
如果你看到.jsp或者.do的结尾的文件,那么这个网站用的脚本程序是java …
显然有些人能写个以.PHP结尾的java脚本程序,或者是以.do结尾的PHP脚本程序,但是这种可能性实际上很小。
通过URL的拓扑结构方式方法也能识别出网站的指纹。举个例子,在Ruby-On-Rails中,对于一个给定的对象,开发者能用脚手架程序自动生成代码管理视图(html源码),模型(存储逻辑)和控制器(业务逻辑)。这种生成的URL拓扑有如下类似的结构:
/objects/ 给你一个对象的列表
/objects/new 给你一个页面去创建新的对象
/objects/12 给你一个id为12的对象 /objects/12/edit
给你一个页面去修改id为12的对象 …
检查favicon.ico图标
favicon.ico图标是个小小的图片,浏览网页时能在浏览器url标签找到它:
这个图片能被作为指纹识别的元素,因为大部分应用程序或者服务器有自己独特的标识图标,而大部分开发者或者系统管理员都没有去变更这个图标。举个例子,下面这个图片就是Drupal独有的。
检查robots.txt文件
另一个常被应用程序部署的文件是robots.txt。一些基于PHP的应用程序很经常使用robots.txt来组织网络爬虫来索引网站。这是信息来源的好地方,通过它我们能发现网站有意思的拓扑结构,并从中发现程序用的什么框架来搭建这个网站。
比如说,下面这个rotots.txt表明它用的是Joomla的CMS:
# If the Joomla site is installed within a folder such as at
# e.g. www.example.com/joomla/ the robots.txt file MUST be
# moved to the site root at e.g. www.example.com/robots.txt
# AND the joomla folder name MUST be prefixed to the disallowed
# path, e.g. the Disallow rule for the /administrator/ folder
# MUST be changed to read Disallow: /joomla/administrator/
#
# For more information about the robots.txt standard, see:
# http://www.robotstxt.org/orig.html
#
# For syntax checking, see:
# http://tool.motoricerca.info/robots-checker.phtml
User-agent: *
Disallow: /administrator/
Disallow: /cache/
Disallow: /cli/
Disallow: /components/
Disallow: /images/
Disallow: /includes/
Disallow: /installation/
Disallow: /language/
Disallow: /libraries/
Disallow: /logs/
Disallow: /media/
Disallow: /modules/
Disallow: /plugins/
Disallow: /templates/
Disallow: /tmp/ |
同时这个文件也告诉你需要接下来要做的检查。一个网站不愿意让别人索引的部分很可能是因为这里是个有意思的安全问题所在。
搜索目录和页面
浏览网站之后,很重要的去搜索网站存在的页面或者目录,但这不是说通过直接的点击页面链接得到。为了达成这个目的,你需要用一个包含所有可能存在的文件或者目录的列表,来检查这些表上的名字在服务器上是否存在。
目录/页面暴力测试
Wfuzz(http://www.edge-securi ty.com/wfuzz.php)这个工具可以通过导入名字字典文件来检查服务器上存在的页面和目录。
下面的命令执行后可以检查存在的远程文件和目录:
$ python wfuzz.py -c -z file,wordlist/general/common.txt –hc 404http://vulnerable/FUZZ |
你可以用Wfuzz做更多的事情:
通过返回的http错误代码做过滤器
只检索给点后缀的文件名:http://vulnerable/FUZZ.php.
力破解证书凭据
......
用其他工具也是一样的,最好的学习方式是一边使用工具一边看你能做什么。
寻找管理后台页面
大部分管理页面都是耳熟能详的URL,通常可以用目录爆破器探知,但是为每一种架构的服务器制作一份管理页面文件还是相当快捷有效的。你可以参考product/project文档说明来获取这个信息。
在你制作的管理后台页面列表中,同时要保存着默认的可使用的登陆凭据。
产生的错误提示
测试过程中产生的404错误能给你许多关于后台服务器运行的web应用程序的信息。为了制造这种错误,你只需要在提交的url链接上加一些随机字符串,比如随机长串。
通过调节Web服务器的配置显然可以改变这一回显错误行为。如果你遇到的是tomcat服务器,将返回下面的404错误:
如果是Ruby-on-Rails:
有许多方法可以在web应用程序中制造错误。比如,在url中添加一些特殊字符,可以是空值(%00)、单引号(%27)、双引号(%22),这些字符都可能导致错误的产生。或者你也可以将http请求中的某些参数值给去除。一旦你成功的制造了错误并得到了相应的错误页面,你就能明白你所攻击的是什么类型的应用程序(举个tomcat的例子):
任何事都有可能改变浏览器的行为,但是生成的错误页面是获取信息的好方法。对于一个php程序来说有个简单的测试方法,将形如/index.php?name=hacker替换为/index.php?name[]=hacker。
最重要的一件事是你要学会读懂这个错误提示。这个听起来可能不以为然,但是你会惊讶为什么那么多人把两种错误当做一样的,即使两者错误信息是不同的:细节决定成败。
记录信息
测试过程中的任何信息都要记录下来,任何事情都要被保存下来:
远程服务器的路径地址
错误信息
后台使用的数据库
报头泄露的内部IP地址
其他任何事情…
记录信息能帮助你实施另外的漏洞攻击。举个例子,假如你需要服务器网站的物理地址来完成攻击,你可能已经通过应用程序另一块的错误信息得到这个地址信息。
编写一些有用的工具
编写一些简单的脚本工具来发送http请求是非常方便的。推荐编写以下的辅助小工具:
一个用传统HTTP库函数(比如ruby的net/http)编写的HTTP客户端和一个用socket编写的只能发送简单的get和post请求的客户端
一个能支持SSL协议的HTTP客户端(包括用HTTP库和socket编写的两种)
一个支持cookie的HTTP客户端(包括用HTTP库和socket编写的两种)
一旦你成功开发并使用这些工具了,你可以尝试将他们整合在一个工具中。
只要你准备好了这些工具,就很容易利用它们实施一次漏洞攻击或者在测试中发现一些问题。复杂的漏洞都需要一些自动化的测试过程。如果你没有自己的HTTP客户端程序来辅助的话,你就很难成功实施漏洞攻击。我们将在下一章当中与大家分享一些Web漏洞、攻击的样例。
WEB漏洞的样例
本章把一些对web漏洞的实际操练放在一起。如果你已经熟悉web渗透测试,那就可以不用看更多了直接尝试去做吧。回顾下,看看你可以用其他的什么方法,能达到什么样的预期效果。
我主要混合使用两种方式来进行web漏洞测试:
想办法弄明白在服务器端的代码是怎么运作的。
如果一个页面是存在漏洞的,尝试发送不同的值但是能得到相同的结果。
我在ISO中提供了上述方法的测试样例。
WEB渗透实例
这一节会用几个实验来模拟利用一下常见的web漏洞.
web渗透的时候,我主要会混着用用下面两种方法:
1.试图猜下服务端的代码会是什么样子
2.试图发送不同的数据到服务端
在ISO文件中,我放了一些这些方法的实例进去.
在这次练习的时候,错误信息都是会被显示在大多数页面上.不过,在实际渗透中,错误信息应该也是经常被关掉了.这里用到的检测漏洞的方法也适用于实际中的渗透.你应该铭记渗透测试是个有时靠猜的游戏.有时你需要把正确的路径猜出来
大部分的安全问题都由一个问题引起:存在问题的代码,为了利用一个SQL注射漏洞,使用了特殊的SQL语句,为了利用一个XSS,使用了特别的HTML代码.
例如下面这样的形式:
[代码][分隔符号][用户输入][分隔符号][代码]
你的目标就是利用[用户输入]这一块来注入代码,为了实现这个,你的输入里面应该有一个[分隔符号]作为[用户输入]的一部分.但是有时候又不需要这个[分隔符号].
大多数情况下,分隔符号就是’,”,`这些符号的一个.一个一个试着注入这些符号,然后观察返回的页面,可能可以得到一些你想要的东西.
跨站脚本(XSS)
跨站脚本的产生是由于信息被发送到用户的时候没有被正确编码.XSS可以被用来注入任意的HTML和JavaScript代码;结果就是XSS的payload运行在合法用户的浏览器中.和其他类型的攻击不同的是,XSS是针对于用户,而不是直接针对服务器.
大概的流程一般是:
1.注入一个虚假的登录框
2.接受合法用户的cookies
3.注入浏览器
4.让用户在web应用程序中执行代码
在这一节中,我们会关注怎么发现XSS的漏洞.为此你需要多练习.
最简单,也是最常用的证明XSS的存在就是弹出一个窗口.这个payload有很多优点:
它能表明JS代码确实被触发了
它很简洁
它对用户没造成什么损害
为了弹窗,你只需要用到下面的payload:alert(1).
如果你是在HTML代码中注入,你需要告诉浏览器这是JavaScript代码.你需要<script>标签来完成这个:
<script>alert(1);</script> |
当测试XSS的时候,有两件事很重要:
你从服务器得到的响应不一定是payload出现的地方.也就是说,如果你注入了一个payload,然后响应的页面是A,A中payload已经被正确编码了,但是payload还是可能页面B中没有被正确编码.
如果你发现有一处编码问题,虽然你的XSS payload不能运行,但是可能别人可以.所以,你的报告需要给出这个编码问题,即使是一些保护措施让你的payload无法运行.
安全是不断发展的领域,每周都有新的攻击手段出现.
有三种类型的XSS:
反射性:payload直接在响应中出现
存储型:响应也会直接在响应中出现,但是更重要的是,当你返回元页面或访问其他的页面的时候,payload还是会出现在响应中.这是因为payload被存在了应用程序的后端存储中(数据库中).典型的例子就是留言板中插入XSS后.
DOM型:payload不会在响应中出现.当浏览器渲染页面的时候,payload会被执行.
测试XSS的时候,你应该读下返回的HTML代码,你不能坐等alert窗口弹出来.检查哪些字符被编码了,而哪些字符没有没有被编码.由此可能发现一个能工作的payload.
一些浏览器有一些自带的保护措施来抵抗xss.这个抵抗功能可以被服务器禁用或者开启.(在ISO文件的例子中已经被禁止了).如果你发现你的payload在返回页面中出现了,但是并没有执行,那就有可能是这个抵抗功能在发生作用.你自己可以关闭掉这种保护功能.例如,在Chrome中,带参数–disable-xss-auditor启动浏览器就可以禁止保护功能.
例1
这里第一个例子只是告诉你存在XSS时一般会发生什么.使用最简单的payload,你可以得到一个弹窗.
一旦你发送你的payload,你可以得到下面的东西:这个例子中必须保证你发送的payload在响应中出现,并且没有被HTML编码.
例2
在这个例子中,我们过滤了一些东西.web开发者在网页源码中添加了一些正则表达式来,防止一些简单的XSS
payload被执行到.
如果你试了一下,你可以看到<script>和</script>被过滤了.最简单过掉这种保护的方法之一就是:发送<sCript>和</sCRIpt>代替原来的<script>和</script>,这样就可以看到弹窗了.
例3
你告诉了那个开发者你的bypass方法.然后,他在网页源码中添加了更多的过滤,现在确实看起来确实可以防止你先前的payload.然而,他在代码中犯了一个天大的错误(在以前的代码中也出现过)…如果你继续试了下,你可以发现如果你用Pentest<script>erLab当作payload,你在返回的页面中就能看到PentesterLab.你可以用这种方法在页面中得到<script>(====),然后你又可以看到弹窗了.
例4
在这个例子中,开发者决定完全把script这个但是拉进黑名单:如果请求中匹配到script,请求就会被拦截.幸运是(或者说不幸的是),还是有很多方法可以让JavaScript执行(不完整的方法列表):
<a标签中有下面的这些事件:onmouseover(你需要把鼠标放到链接上才能触发payload),onmouseout,onmousemovoe,onclick…
<a标签中直接出现在URL中:<a href=”javascript:alert(1)”…(你需要点击链接才能触发JavaScript代码,但是由于在这个例子中不能用script这个单词,所以这个方法在这个例子中实际上不能工作)
<img标签中onerror事件onerror:<img src=’zzzz’
onerror=”alert(1)”/>.
<div标签中有下面的事件:onmouseover(你需要把鼠标放在那个链接上才能触发payload),onmouseoutonmousemove,onclick…
亦可以用上面的方法来弹窗.
例5
在这个例子中,<script>标签没有被过滤,并且在响应中出现了.但是一会儿,当试图注入payload来弹窗的时候,PHP代码就停止执行了.问题看起来是由于过滤了单词alert.
用JavaScript中的eval和String.fromCharCode(),你可以弹窗并且不需要直接用alert这个单词.String.fromCharCode()函数会将一个整数编码到对应的字符中.
用这个小技巧和ascii码表,你可以轻易地产生字符串:alert(1)并且调用eval函数执行它.另一个更简单的bypass就是用JavaScript代码中的prompt或者confirm函数.这两个函数比较少见,但是可以产生相同的效果.
例6
这里,HTML页面的源代码有点不相同.如果你读了源代码,你会发现发送的payload是在JavaScript代码中的.为了弹窗,现在payload中不需要<script>标签了,你只需要注入已完成前面的已经存在的JS代码和添加你自己的payload,然后你需要在你的注入点处把后面的代码都注释掉(用//)或者添加一些多余的代码(var
$dummy=”)来吧后面的代码正确关闭掉.
例7
这个例子和前面的一个相似.这次,你不能用任何的特殊字符,因为他们都被HTML编码了.
如你所见,你实际上不需要这些字符中的任何一个.这个问题普遍存在于PHP应用程序中,因为那个出场率很高的函数htmlentitie有HTML编码的功能,不过如果你调用时没有用到ENT_QUOTES标志,这个函数是不会HTML编码单引号的.
例8
这个例子中,响应中的payload都已经被合适地编码过了.不过,页面中还是存在一个XSS.
为了构成表单,程序开发者使用了PHP_SELF,这个变量是受用户控制的.可以操控程序的路径用来干这些事:
请求当前页面(不过你会得到一个404页面)
得到一个页面中的XSS payload
上面的是可以坐到的,因为服务器现在的配置是任何匹配/xss/example8.php/…的URL都可以请求/xss/example8.php页面.你可以访问/xss/example8.php/[XSS_PAYLOAD].现在你知道了哪里放payload,你需要来个弹窗.
相信用户提供的path是个开发人员常犯的错误,并且经常被用来触发XSS,当然也可能导致其他的问题.这个在表单和错误页面中(400和500页面)较为常见.
例9
这个例子是一个基于DOM的XSS.这个实际上是个静态网页,不过依然存在漏洞.
在这个例子中,你需要阅读页面的源代码来理解发生了什么.当页面渲染的时候,JavaScript代码用URL得到锚点(URL中#后面这一部分),并且在客户端动态地写入页面.如果你用payload当作URL的一部分,这个可以用来触发XSS漏洞.
SQL注入
SQL注入是最常见的web漏洞之一.这里所有的SQL注入漏洞联系都是用MySQL当作数据库.SQL注入是由于程序做SQL查询的时候缺乏编码或者转义用户的输入.
由于SQL查询语句信息添加方式不同,你需要用不同的语句来完成SQL注入:
使用引号:单引或者双引号
用反引号
直接添加值
例如,如果你想利用字符串构造信息,你可以这么做:
SELECT * FROM user WHERE name="root"; |
或者
SELECT * FROM user WHERE name='root’; |
如果你想用数字构造信息,你可以这么做:
SELECT * FROM user WHERE id=1; |
如果你想要用列名来构造信息,你需要这么做:
SELECT * FROM user ORDER BY name; |
或者
SELECT * FROM user ORDER BY `name`; |
也可以用一个数字当作字符串,不过这样查询会慢一点
SELECT * FROM user WHERE id='1'; |
信息显示的方式,甚至用到的是什么分隔符,都会决定我们用到什么技术来注入.不过,你不知道它的源代码,你只能试着猜了.你可以先试着假设一下,然后再试着验证你的假设.这就是为什么花时间在liveCD里的例子是这么重要了.
例1
在这个例子中,我们可以看到参数是一个字符串,并且我们可以看到表中有一行数据.为了理解服务端的代码,我们现在随便试下:
如果我们添加一些多余的字符,比如”1234″,那么我们就用?name=root1234,结果表格中没有任何数据.从这里,我们可以猜到服务端用到了我们提交的数据,并且对这个数据做了某些匹配.
如果我们在请求中注入一些空格,我们用?name=root+++(URL中+号代表的是空格),表格中就有数据了.MySQL默认情况下在做比较操作的时候会忽略掉空格.
如果我们注入一个双引号,用?name=root”,结果表格中没有数据.如果我们注入一个单引号,用?name=root’,表格不见了.我们可以破坏了什么东西.
从前面的东西,我们可以推断出SQL查询语句是这样的:
SELECT * FROM users WHERE name='[INPUT]'; |
现在让我们来验证一下猜想.如果我们是对的,那么下面的语句应该返回同样的结果.
name=root’ and ’1′=’1′ #(别忘了在URL中编码一下#):原先的查询语句中单引号’会被#符号注释掉.
name=root ‘and 1=1 #(还是别忘了在URL中编码一下#):原先的查询语句中单引号’会被#符号注释掉,并且我们去掉’1′=’1′中的引号了
name=root’ #(不要忘记URL中编码#):原先的查询语句中单引号’会被#号注释掉,并且我们去掉了1=1.
下面的这些请求也许不会发挥同样的结果:
name=root’ and ’1′=’0:返回的页面应该有一个空表,其中没有数据,因为select语句总是返回false.
name=root’ and ’1′=’1 #(不要忘记在URL中编码#号):返回的页面应该跟上面的结果是相同的
name=root’ or ’1′=’1:返回的页面应该和最开始查询的结果是相同的,但是又不一定,因为这个例子中那个值被用作一个过滤器====(假设页面一个时刻只能显示一个结果)
name=root’ or ’1′=’1′#(不要忘了编码#号):这个应该和上面的结果是一样的.
通过上面的测试,我们可以确信这里有一个SQL注入了.这个例子只是讲了怎么检测SQL注入点.你可以看下其他的PentesterLab的例子,看下注入点是怎么发挥作用的.
例2
在这个例子中,开发者的保护措施会导致错误信息:ERROR NO SPACE.只要请求中有空格字符,页面就会报错.这样可以防止我们使用’
and ’1′=’1方法,或者其他的有空格字符的方法.
不过,这个过滤很容易就能过掉,我们可以用Tab符号(HT或者\t).当然,要想在请求中用到这些字符,你需要编码下这些字符.用这个简单的bypass,你可以检查页面是否有SQL注入漏洞.
例3
在这个例子中,开发者过滤了空格和Tab符号.依然有办法过掉过滤器.你可以在单词之间用注释符号来构造合法的请求,这样可以不用到空格和Tab符号.可以用下面的SQL注释语句:/**/.在这个例子中,你可以用这个注释语句来代替所有的空格和Tab符号,这样可以检测到页面是否存在SQL注入漏洞.
例4
这个例子说明了一个在预防SQL注入时典型的误解.在前面的三个例子中,用mysql_real_escape_string函数就可以预防SQL注入.在这个例子中,开发者仍然使用这个函数来预防SQL注入.不过,输入的值是一个整数型的,并且不在单引号中.由于值直接被放在了查询语句中,用mysql_real_escape_string函数无效.这里,你只需要添加一些空格和SQL关键字就能注入.这个检查漏洞的方法和基于字符串的SQL注入非常相似.只不过这里不需要引号了.还有一个方法可以检测到这种漏洞的存在.原来的请求是?id=2.对2这个值做一些变化,我们可以检测到
SQL注入的漏洞:
id=2 #(#号需要在URL中编码一下)应该和原来的请求返回相同的结果.
id=3-1应该也是返回一样的结果.数据库会自动执行减法,然后我们可以得到相同的结果.
id=2-0应该也是返回一样的结果
id=1+1(+号需要在URL中编码一下)应该会返回一样的结果.数据库会自动执行加法.
id=2.0应该也是返回一样的结果.
下面的两个就不会返回相同的结果了:
id=2+1.
id=3-0.
例5
这个例子和前面的例子很相似.如果你仔细看代码的话,你可以看到开发者用了一个正则表达式来预防SQL注入.
1 if (!preg_match('/^[0-9]+/', $_GET["id"])) { 2 die("ERROR INTEGER REQUIRED"); 3 } |
不过,这个正则表达式没有勇对.他只确保了id参数是以一个数字开头的.上个例子说得检测方法也可以用来检测这个例子中的漏洞.
例6
这个例子中,开发者又用错了正则表达式:
1 if (!preg_match('/[0-9]+$/', $_GET["id"])) { 2 die("ERROR INTEGER REQUIRED"); 3 } |
这个正则表达式值确保了参数id是以一个数字结尾的.他不能确保id参数的开头是合法的.你可以变通一下前面说到的检测方法.你只需要在你的payload后面加上数字.比如你可以这样:1
or 1=1 # 123.
例7
又是一个错误的正则表达式.
1 if (!preg_match('/^-?[0-9]+$/m', $_GET["id"])) { 2 die("ERROR INTEGER REQUIRED"); 3 } |
这里我们可以看到参数的开始和结果都被检查了.不过,正则表达式中有一个修饰符/m.这个符号就是说在几行数据有一行符合正则表达式,那么这行就被匹配到了.所以说,下面的这些payload是会被正则表达式认为是匹配到了的:
.123\nPAYLOAD; .PAYLOAD\n123; .PAYLOAD\n123\nPAYLOAD. |
这些值在URL中需要编码一下,然后你就可以用这个检测到这个例子中的漏洞了.
例8
这个例子中,参数名泄露了它在SQL语句中的位置.如果你查阅一些MySQLwendang
,就知道有两种方法可以在ORDER BY语句后面加一个值.
直接加:ORDER BY name;
在反引号中加:ORDER BY `name`.
ORDER BY 语句后面接的值不能在单引号’或者双引号”中.为了检测到这种漏洞,我们可以用不同的payload得到一样的结果.
name` #(#号需要编码)应该返回相同的结果.
name` ASC #(#需要编码)应该返回相同的结果.
name`,`name:原来查询语句中的反引号`会闭合这里的反引号`.
下面的payload应该返回不同的结果:
name` DESC #(#需要编码).
name`应该不返回任何结果,因为语法不正确
这个例子和前面那个的类似,但是用的是反引号“`“,被排列的值会在查询后被输出。相比之前的查询语句只需要一点点改动。
还有利用别的方式来达到这个目的,直接发送注入请求不用反引号。我们可以用MySQL if语句来构造更多的攻击语句:
If(1,name,age)能得到同样的结果
If(0,name,age)得到不同的结果。从中得知字段是按年龄排序的,但是排序函数比较的是字符,而不是整型(10比2小)。这是使用if语句的负面效果,如果字段中有一个是字符型的,那么排序结果就都按字符型排序。
Web渗透测试攻略(上)
Web渗透测试攻略(下)
|