介绍
这份文档主要介绍渗透测试所需要的知识。
web应用程序
系统启动后,可以用ifconfig命
$ ifconfig eth0 eth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56 inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0 inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:88 errors:0 dropped:0 overruns:0 frame:0 TX packets:77 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:10300 (10.0 KiB) TX bytes:10243 (10.0 KiB) Interrupt:11 Base address:0x8000 |
令获取当前的ip地址:
这个例子中,ip地址就是10.0.2.15.
在整个训练中,我们用来模拟受害机器的主机名叫vulneralble.你可以用ip地址来代替主机名,或者你可以在host文件中添加主机名和对应的ip地址.
windows下,host文件位于C:\Windows\System32\Drivers\etc\hosts
Unix/Linux和Mac OS X下,host文件位于/etc/hosts 提示:ip重启机器后,ip地址会改变,也许你需要在host文件中做一些对应的更新.
访问web程序后,可以看到下面的页面
Web
很多公司可能都有开放了web服务,并且,很多应用程序现在都有了web版本.由此,web安全的重要性不言而喻.
web安全模型
web安全模型的精髓很简单:不要相信客户提交的数据.服务端得到的大部分信息都是客户端提交的.我们最好过滤和转义用户提交的数据.
web安全风险
web应用程序带来的风险和其他类型的程序带来的是一样的:
信息泄漏
形象损失
信息丢失
经济损失
web技术
构架
大多数的web程序以来三个组件:
1.客户端:大部分情况下是浏览器
2.用来接受客户端请求的web服务器.
一个应用程序服务器会处理这个请求,这种情况下,web服务器就只是把请求传递给了应用程序服务器.
3.用来存放信息的存储后端:通常是数据库.
这些组件的不同行为可能暴露出弱点和安全问题.
客户端技术
每天被用到的客户端技术大部分就是HTML,JavaScript,Flash…通过浏览器(谷歌,火狐IE等)连接服务端.不过,web程序的客户端也可能是一段连接web服务的脚本而已.
服务端技术
在服务端就有很多的技术会被用到,即使这些技术都容易收到攻击.
这些技术可以细分为下面几种
1.web服务器
如Apache,lighttpd,Nginx,IIS…
2.应用程序服务器
如Tomcat,Jboss,Oracle Application server
3.编程语言
入PHP, Java, Ruby, Python, ASP,C#, …编程语言也可以被用到一些框架中,如Ruby-on-Rails,.Net
MVC,Django.
存储后端
存储后端可以和web服务器位于同一机器上,也可以位于不同机器上.
一些存储后端的例子:
文件存储
关系数据库
比如Mysql,Oracle, SQL Server, PostgreSQL.
其他的数据库
比如MongoDB, CouchDB.
目录
比如openLDAP或者活动目录 .
一个应用程序可以使用多种存储的方法.比如说,一些程序用LDAP来存用户名和密码,同时用Oracle来存用户的其他信息.
HTTP协议
HTTP是整个web的基础,想要web测试,那对这个协议有一个深入的了解是很重要的.熟悉HTTP规范有助于挖掘漏洞.
一次客户端和服务端的会话
HTTP是一个客户端和一个服务端之间的会话.客户端,假设是浏览器,就会发送请求给服务端,然后服务端对这个请求返回一个响应.HTTP是文本协议,所以对于我们人类来说很容易读懂.一般情况下,web服务监听的端口都是TCP/80.当你在浏览器地址栏上输入http://pentesterlab.com/并回车时,实际上是在连接到pentesterlab.com对应的ip的80端口.大多数的请求发生在浏览网页的时候.浏览器发送一个由下列元素组成的请求:
HTTP方法
这个让服务器理解浏览器进行的是什么操作
资源
说明客户端想访问服务器上的是什么
版本信息
说明服务器使用的是哪个版本的HTTP协议
各种各样的头部信息
这些信息告诉了服务器浏览器的名字和版本,用户偏爱的语言(如英语,德语,法语…)..
请求主体
根据HTTP方法不同而有不同的解释
一个例子,打开http://vulnerable/index.php会产生下面的HTTP请求
GET /index.php HTTP/1.1 Host: vulnerable User-Agent: Mozilla Firefox |
请求
方法
有很多的HTTP方法:
GET方法
得到网页内容,浏览器最常用的方法
POST方法
POST方法,被用来发送内容较多的数据,常被用在很多表单和文件上传中.
HEAD方法
HEAD方法和GET方法很类似,唯一的区别就是server返回的响应.HEAD方法得到的响应只包含头部,而没有实体.web蜘蛛检查一个页面有没有更改的时候常用到这个方法,这样蜘蛛就不需要下载整个页面的内容了.
还有许多其他的HTTP方法:PUT,DELETE,PATCH,TRACE,OPTIONS,CONNECT…
参数
请求还有一个很重要的部分就是参数.当客户端访问下面的页面http://vulnerable/article.php?id=1&name=2
时,下面的请求会被发送到web服务器:
POST请求非常相似,但是实际的参数是包含在请求实体中的.如下面的表单:
<html> [...] <body> <form action="/login.php" method="POST"> Username: <input type="text" name="username"/> <br/> Password: <input type="password" name="password"/> <br/> <input type="submit" value="Submit"> </form> </body> </html> |
这份HTML代码对应下面的登录表单:
如果表单的值是下面这样的
username是’admin’
password是’Password123′.
那表单提交后,下面的请求就会被发送到服务器:
POST /login.php HTTP/1.1 Host: vulnerable User-Agent: Mozilla Firefox Content-Length: 35 username=admin&password=Password123 |
如果<form标签中用的是GET方法,那发送的请求就是下面这样的:
GET /login.php?username=admin&password=Password123 HTTP/1.1 Host: vulnerable User-Agent: Mozilla Firefox |
如果form标签包含属性enctype=”multipart/form-data”,发送的请求是下面这样的.
POST /upload/example1.php HTTP/1.1 Host: vulnerable Content-Length: 305 User-Agent: Mozilla/5.0 [...] AppleWebKit Content-Type: multipart/form-data; boundary=—- WebKitFormBoundaryfLW6oGspQZKVxZjA ——WebKitFormBoundaryfLW6oGspQZKVxZjA Content-Disposition: form-data; name=”image”; filename=”myfile.html” Content-Type: text/html My file ——WebKitFormBoundaryfLW6oGspQZKVxZjA Content-Disposition: form-data; name=”send” Send file ——WebKitFormBoundaryfLW6oGspQZKVxZjA– |
我们可以看出在请求头部中Content-type很特殊:Content-Type:
multipart/form-data; boundary=—-WebKitFormBoundaryfLW6oGspQZKVxZjA.
“WebKit”出现在基于webkit内核的浏览器中,其他内核的浏览器会用一个随机的字符串替代.这个字符串在好几个地方都出现了.最后一行中那个字符串后面还跟了个–字符串.当你上传一个文件时,浏览器会发送下面的东西.
文件名:myfile.html
参数名:image
文件类型:text/html
文件内容:my file
也可以把数组当作参数传送过去(或者hash加密参数,只要服务端能够解析出来).你也可以用/index.php?id[1]=0来编码包含值0的数组.
这种编码经常被一些组建用来对对象映射的自动请求.比如说,下面的请求:user[name]=louis&user[group]=1会被映射到一个User对象,这个User对象有一个属性name值为louis,还有一个group属性值为1.自动映射有时候会被攻击.通过发送别的属性值,如果程序没有保护这个属性,也许你可以改变那个对象的属性.在我们先前的例子中,你可以增加一个user[admin]=1到请求中,看下你能否得到admin权限.
from:91ri.org
HTTP头部
HTTP请求中包含很多的头部信息.很明显,你可以控制所有的头部信息,但是如果你把头部值设置错了什么的,很有可能这个请求就废了,服务端根本就不理你改的请求.大多数程序只用到几个常用的头部:
Referer
含义:确认客户端是从哪一个链接跳到这儿来的
Cookie
含义:接受客户端发送来的cookies
User-Agent
含义:用来确认客户端用的是什么浏览器
X-Forwarded-For
含义:得到客户端的ip地址(这个不是得到ip地址最好的方法,因为这是可以被伪造的)
其他的HTTP头部被服务端用到很多,服务端处理这些头部的时候可能存在安全隐患.但是,在web服务端发现bug比在web程序中发现bug要难的多.
有一个非常重要的头部是”Host”.Host头部主要被web服务器用来确认你想访问的是哪个网站.比如说,在一个服务器上搭建了好几个网站(提供虚拟主机服务),几个网站的对外ip都是一样的.当你访问其中的一个网站时,虽然几个网站的ip都是一样的,但是服务器会看host这个字段值,这样它会知道你想要访问哪个网站了,就会返回这个网站的内容给你.如果你把Host字段的值改成ip地址或者一个无效的主机名,你有可能会得到别的网站返回的内容.
当你发送一个请求时,服务端会返回http响应.比如下面的响应:
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 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>PentesterLab » Web for Pentester</title> <meta name="viewport" content="width=device-width, initialscale=1.0"> <meta name="description" content="Web For Pentester"> <meta name="author" content="Louis Nyffeneggerlouis@pentesterlab.com"> |
响应中那个状态码非常重要.返回的状态码位于响应的第一行.客户端会根据响应码来处理响应.
下面的状态码是很常见的.
200 OK:服务器成功处理请求了
302 Found:重定向
401 Unauthorized:访问受限了
404 Not Found:请求的资源没找到
500 Internal Server Error:服务器处理请求的时候出错了
有一些状态码就很生僻了,比如418:I’m a teapot.
在状态码后面就是HTTP头了.返回的HTTP头会影响浏览器显示网页的方式.在上面示例的响应中,头部包含了下面的信息:
Date
含义:日期
Sever
含义:头部透露了一些关于服务器的信息,这个就是apache服务器,版本2.2.16
X-Powered-By
含义:头部透露了更多的信息.
Content-Length
含义:头部说明了响应的大小.
Content-type
含义:头部告诉浏览器返回的是什么东西.如果这个值是text/html,浏览器会渲染响应.如果是text/plain,浏览器不会渲染.
content
含义:部分就是返回的信息,可以是HTML网页,一些图片.当浏览器接收到一个HTML网页时,它会解析它并且自动接受一些其他的文件如:
Javascript文件
CSS文件
图片
…
HTTPs
HTTPs只是基于SSL的HTTP.SSL会确保客户端:
它和正确的服务器在交互:认证连接是安全的:加密.
SSL存在很多版本,其中一些版本被认为很弱(SSLv1和SSLv2).
SSL也可以用来确认客户端的身份.客户端有个证书,这个证书确保了只有拥有有效证书的客户端才能和服务器通信.
这种方法常被用在安全性要求较高的系统中.然而,保存证书确实一个令人头痛的事情.
监听HTTP请求
有3种方法来监听HTTP请求:
用Wireshark或者tcpdump这类工具直接监听网络
在浏览器上查看.大多数浏览器都有可以允许用户查看发送和接受数据包的插件.
在浏览器和服务器中设置一个代理
三种方法有利有弊.使用哪一种主要取决于连接是否用了SSL和用户是否想修改请求.
产生HTTP请求
有好几种方法产生请求
由于HTTP是一个基于文本的协议,你可以使用telnet或者netcat这些工具来构造请求
也可以编程实现发送HTTP数据.用socket很容易实现在网络上读写数据.更方便的是,很多
语言都有HTTP库,用这些库我们会很容易构造和发送请求,也很轻松就能获得响应.
当然,最简单的就是用浏览器来发送请求了.
用浏览器明显最便捷.但是,其他的方法更有益于你连接HTTP请求的细节.
用telnet发送请求
$ telnet vulnerable 80GET / HTTP/1.1 Host: vulnerable |
也可以用netcat
$ echo "GET / HTTP/1.1\r\nHost: vulnerable\r\n\r\n" | nc vulnerable 80 |
数据编码
代码vs数据
大多数的安全问题发生在攻击者可以操做上传的数据,并且应用程序会把这些数据当作代码执行.
比如xss和SQL注入.
URL编码
有一些字符在HTTP中需要特殊对待:
比如说url中的?,&,=号都有它自己的意思.不过对于大多数攻击来说,这些字符是必须的.为了保证这些字符能被理解成是值而不是请求的分隔符号;我们应该对这些字符进行编码.最简单的编码方式就是%后面跟上字符的十六进制值.为了得到一个字符的十六进制值,我们应该了解下ascii码表.下表可以看到字符和对应URL中编码的值:
Character URL encoded value
\r %0d \n %0a %20 or `+` ? %3f & %26 = %3d ; %3b # %23 % %25 |
想要得到完整的ASCII码表,在大多数的linux系统上可以使用man
ascii命令得到,或者google吧.
两次编码
有时候,被检测的系统会解码两次.例如,服务端解码后,应用程序解码第二次.这样的话,我们应该编码两次我们想要发送的字符.
例如,如果想要对等号=编码两次,第一次它被编码成%3d,第二次它就被编码成%253d了.服务端接收到%253d以后,会把它解码成%3d,然后应用程序会把%3d解码成=号.两次编码有时也可以用来bypass一些过滤.
HTML编码
和URL编码一样,一些HTML环境中的字符也有特殊的含义,所以如果要把他们当作普通的值用,也要对它们编码.
Character HTML encoded value
\r %0d \n %0a %20 or `+` ? %3f & %26 = %3d ; %3b # %23 % %25 |
任何字符都可以用十进制或者十六进制编码.
比如=可以编码成=,也可以编码成=.(注意有;分号)
Cookies和sessions
服务端会用一个HTTP头:Set-Cookie来初始化Cookie.浏览器接受到这个头部后,就会自动地把cookie
发送回服务端,并且以后的每次请求中都会用cookie头部带上这个cookie.
Set-Cookie头包含几个可选域:
expiration date
过期时间:告诉浏览器什么时候该删除这个cookie.
Domain域:
告诉浏览器cookie应该被发送到哪个主机或者子域名下,子域可以读取父域的cookie.
Path路径:
告诉浏览器cookie应该发送到哪个路径下,只有目标路径下的js代码才能读到这个cookie.
安全标志
Path和Domain字段多数情况下是为了安全性.Cookie有两个安全相关的标志
httpOnly
可以阻止js代码读写cookie.这样在跨站脚本中document.cookie就没作用了,盗不了cookie.
secure
带有这个标志的cookie只能在HTTPS层面安全传输.如果请求是HTTP的,这个cookie就不会被传送.session机制采用的是在服务器端保持状态的方案。而cookie机制采用的是在客户端保持状态的方案。
当客户端访问到一个程序时,程序要为客户创建一个session,在创建这个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识(session
id),如果已包含一个session id则说明之前已经为此客户端创建过session,服务器就按照session
id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session
id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session
id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。保存这个session
id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。
Rack::Session::Cookie
在基于Rack程序中是默认使用的(大多数的Ruby程序都会用到Rack).它提供了一种不同的会话方法.用户虽然能接收到session的信息,但是这些信息是加密了的.这样的话,用户就不能修改session中的信息了(但是一旦解码出来,又能访问了).
在PHP中,默认情况下每一个sessions会存放在一个文件中,并且没有加密.(在Debian系统中位置在/var/lib/php5).如果你有权限访问这些文件,你就可以读取
到别人的session信息了.举个例子,加入你的session id是o8d7lr4p16d9gec7ofkdbnhm93,你会看到一个名字sess_o8d7lr4p16d9gec7ofkdbnhm93的文件,这个文件就包含了session中的信息.
# cat /var/lib/php5/sess_o8d7lr4p16d9gec7ofkdbnhm93pentesterlab|s:12:"pentesterlab";HTTP认证
HTTP也提供方法来认证用户.协议中有三种可用的方法:
Basic Authencation
用户名和密码用base64编码了,并且用了Authencation头部:
Authorization: basic YWRtaW46YWRtaW4K. Digest Authencation
服务端发送一个–(独一无二的信息),客户端返回一个–(hash加密信息,其中包含用户的密码).这种方法确保了发送给服务器的密码是加密的.
NTLM authencation
这种方法主要用在微软系统中,并且和Digest方法很像.
Web服务
想调用远程方法,用HTTP发送请求到服务是个不错的方法,基本上就是发送命令到服务端然后得到一个返回的响应.发送的东西可以是:
HTTP请求
XML消息
基于JSON的消息
远程的命令可以被服务端接受:
基于URL
基于HTTP头部(例如SOAPAction头部)
测试web服务和测试一般的web应用程序差不多,只不过浏览器不能跟服务端交互而已.如果你有一个请求的样本,你可以用工具或者自己用脚本语言来根据这个请求来fuzz和攻击服务端的代码.
web应用程序安全
客户端安全
一个普遍错误的做法是程序员在客户端进行安全检查,例如javascript中,验证一个手机号码是否有效.
一开始,用户会输入一个手机号码
JS代码会检查这个值
这个手机号码值貌似是有效的
这个值就会被发送到服务端
如果这个值无效,浏览器就不会发送这个这个请求
js代码会检查这个值
并且说明了这个值是错的
请求将被发送到服务器。
这种方式的检查是低能的,很容易就被绕过所以不能被用为安全检查机制。但是,通过限制发往服务器请求的数量,这种检查方式能减轻服务器的负担。如果每个发往服务器的请求都是正确的,错误的请求不会被发送到服务器,这样就相当于减轻了服务器的负担。
绕过客户端方面的检查
要绕过客户端这一侧检查,你需要架设一个像Burp Suite那样的代理服务器。当你运行了代理服务器之后,你还要设置下让你的浏览器将所有请求都转发到这个代理服务器(根据你的浏览器和操作系统来调节下
浏览器的设置或者环境变量)。之后你就可以看到所有浏览器发出的请求,并且有能力拦截和修改请求数据。
当你架设好这个代理服务器以后,你就有能力拦截浏览器发出的请求。
然后你可以修改它:
服务器会响应你修改过的请求:
在浏览器填写正确的值,就可以成功提交表单。但是,代理服务器拦截到这一请求,就能通过修改这个数值来达到攻击web应用程序的目的。
服务器端
应用程序的安全是在服务器这一侧履行的。所有的收到的信息都不应受信任;数据本身和数据格式都应该被考虑为带有恶意的输入。不要指望输入的参数是一连串的;
它可以是混杂或者是阵列的。不要指望输入的参数是整型;他可以是字符串。甚至连当前服务器的主机名(由host头提供)也能是恶意输入。不要信任任何输入数据,并确保你已经再次检查了所有的输入数据。如果你编写了一个安全性脆弱的应用程序,那很有可能被一些人找到一些问题。不要寄希望于别人找不到问题,如
果你编写了一个安全性脆弱的东西总会有人找到漏洞所在。
Web渗透测试攻略(中)
Web渗透测试攻略(下)
|