攻击JavaWeb应用[9]-Server篇[2]

攻击JavaWeb应用[9]-Server篇[2]


0x01 WebServer

Web服务器可以解析(handles)HTTP协议。当Web服务器接收到一个HTTP请求(request),会返回一个HTTP响应(response),例如送回一个HTML页面。

Server篇其实还缺少了JBOSS和Jetty,本打算放到Server[2]写的。但是这次重点在于和大家分享B/S实现和交互技术。Server[1]已经给大家介绍了许多由Java实现 的WebServer相信小伙伴们对Server的概念不再陌生了。Web服务器核心是根据HTTP协议解析(Request)和处理(Response)来自客户端的请求,怎样去解析和响应来自客户端的请求正是我们今天的主题。

 

B/S交互

攻击JavaWeb应用[9]-Server篇[2]_第1张图片

浏览器发送HTTP请求。经Internet连接到对应服务器。服务器解析并处理Http请求,返回处理结果到浏览器。浏览器解析服务器返回的数据并显示解析后的网页。

在学习之前需要了解浏览器和Server工作原理,比如什么是HTTP协议什么是Socket。对于更底层的协议暂不提及。

HTTP协议

HTTP的发展是万维网协会(World Wide Web Consortium)和Internet工作小组(Internet Engineering Task Force)合作的结果,(他们)最终发布了一系列的RFC,其中最著名的RFC 2616,定义了HTTP协议中现今广泛使用的一个版本—HTTP 1.1。

详情: http://www.w3.org/Protocols/

请求http://www.google.com:

攻击JavaWeb应用[9]-Server篇[2]_第2张图片

客户端浏览器发送了一个HTTP请求, 第一行GET / HTTP/1.1即:以GET方式请求“ /” 目录HTTP/1.1是请求的HTTP协议版本。而Google返回的则是一个基于HTTP协议的响应,其中包括了状态码、内容长度、服务器版本、以及返回内容类型等。客户端浏览器发送了一个请求(HttpRequest),Google服务器返回处理(Handling Request)并响应(HttpResponse)了这个请求。

通俗的说HTTP协议是一种固定的请求格式,只要按照固定的格式去发送请求,服务器就可以按照固定的方式去处理来自客户端的请求。

Socket:

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。Socket通常也称作”套接字",用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。  攻击JavaWeb应用[9]-Server篇[2]_第3张图片

0x01 Java实现Web Server

Oracle提供了一个基础包:java.net用来实现网络应用程序开发。提供了阻塞的Socket和、非阻塞的SocketChannel、URL等。 客户端通过Socket与服务器端建立连接,然后客户端发送请求内容到服务器。服务器接收到请求返回给客户端,请求完成后断开连接。

1、Client

发送一个非标准的HTTP请求内容为”Hello...”给SAE服务器:  攻击JavaWeb应用[9]-Server篇[2]_第4张图片

请求首先到达了对方监听80端口的nginx,在发现客户端发送的内容不符合HTTP请求规范的同时返回了一个400错误(400 Bad Request)。 发送一个合法的HTTP请求(不截图了,把上面的Hello...换成了req),即发送:

?
1
2
3
4
5
"GET / HTTP/1.1\r\n" +
"Host: www.wooyun.org\r\n" +
"Connection: keep-alive\r\n" +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" +
"Cookie: bdshare_firstime= 1387989676924 \r\n\r\n”;

服务器返回信息:  攻击JavaWeb应用[9]-Server篇[2]_第5张图片

两次请求的差异在于是否按照HTTP协议发送,当我们随意向目标端口发送请求时,返回了一个错误请求结果。当发送符合HTTP协议的请求时服务器返回了正确的处理结果。所以只需按照HTTP协议去解析请求和响应即可。与此同时不难看出请求头的任何内容都是可以伪造的,这也是之前写cs交互的时候提到为什么不要信任来自客户端的任意请求的根本原因。现在尝试着写一个Server,去解析来自浏览器的请求。

除了使用上面的“冗余代码”去发送HTTP请求,你还可以用oracle自带的URL包去发送HTTP请求会更加简单。通过setRequestProperties一样可以修改请求头。用getHeaderFields就能获取到响应头信息了。

2、简单HTTP服务器实现

需再一次看下上面Socket流程图,在服务器上监听某个端口(listen),等待请求(accept)。一旦有连接到达就开始读取请求内容(read),然后处理并输出响应内容(write),最后close。服务器端核心业务是获取请求、解析请求、处理请求、返回响应。

Server.java核心代码:  攻击JavaWeb应用[9]-Server篇[2]_第6张图片

浏览器请求:http://192.168.199.240:9527/wooyun.jsp?user=yzmm2&pass=123  攻击JavaWeb应用[9]-Server篇[2]_第7张图片

浏览器请求头:

?
1
2
3
4
5
6
7
8
GET /wooyun.jsp?user=yzmm&pass= 123 HTTP/ 1.1
Host: 192.168 . 199.240 : 9527
Connection: keep-alive
Cache-Control: max-age= 0
Accept: text/html,application/xhtml+xml,application/xml;q= 0.9 ,image/webp,*/*;q= 0.8
User-Agent: Mozilla/ 5.0 (Windows NT 5.2 ) AppleWebKit/ 537.36 (KHTML, like Gecko) Chrome/ 31.0 . 1650.63 Safari/ 537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q= 0.8

现在需要做的是解析请求。在Server里面有一段解析请求的代码:Request req = new Request().parserRequest(sb.toString());//解析请求。具体的需要解析的内容包括:请求头(Header)、请求参数(Parameter)、请求的URI(RequestURI)等。如果是文件上传请求的话还得解析具体的内容(form-data)。 在解析的整个过程没看过RFC文档,只是根据个人理解去实现请求解析,有不对的地方见谅。

首先用换行符切开请求头,得到如下结果:GET /wooyun.jsp?user=yzmm&pass=123 HTTP/1.1。可见这里是按空格隔开的,用正则的\s就可以切开了当前行了。这样就能简单的拿到:[GET, /wooyun.jsp?user=yzmm&pass=123, HTTP/1.1]把他们保存到类的成员变量以便后面调用。

解析请求头比较简单,只需把请求头内容按照key、value方式解析出来就行了。比如:Host: localhost:9527,解析后就成了key=Host,value=localhost:9527。parserGET方法就更简单了,把 /wooyun.jsp?user=yzmm&pass=123以”?”号切开后再以”=”号切开,最终得到的是key=user,value=yzmm、key=pass,value=123。

攻击JavaWeb应用[9]-Server篇[2]_第8张图片

攻击JavaWeb应用[9]-Server篇[2]_第9张图片 处理结果都装在了如下变量:

1
2
3
4
5
6
7
8
private String method;
private String queryString;
private String requstURI;
private String host;
private Map formContent = new LinkedHashMap();
private Map header = new LinkedHashMap();
private Map parameter = new LinkedHashMap();
private Map multipart = new LinkedHashMap();

如果想取出请求参数可以用parameter.get(“xxxx”)就行了,是不是跟javaee有那么些相似了?当请求解析完成后需要去加载请求的文件,比如这里的wooyun.jsp。

当请求处理完后调用getResponse方法把结果输出到浏览器:

1
2
3
4
5
6
7
8
9
public String getResponse(String content){
        return "HTTP/1.1 200 OK\r\n"+
               "server: "+Constants.SYS_CONFIG_NAME+"\r\n"+
               "Date: "+new Date()+"\r\n"+
               "X-Powered-By-yzmm: "+Constants.SYS_CONFIG_VERSION+"\r\n"+
               "Content-Type: text/html\r\n"+
               "Content-Length: "+(content!=null?content.length():0)+"\r\n\r\n"+
               content;
}

从上可见服务器的响应信息也是可以任意的。比如我修改了响应中的server的值你就会在浏览器的Response当中看到当前的server是: z7y-server。出现在响应头里面有意思的漏洞有:CRLF注入,有兴趣的小伙伴儿可以了解下。

0x02 文件上传请求解析


文件上传请求和普通的GET、POST不一样,在JavaEE里面会把multipart请求封装成一个InputStream对象。如果想要从请求里面解析具体的文件内容需要读取流。值得注意的是multipart/form-data中的input域也会包含在InputStream里面。在JavaEE里面可以用:request.getInputStream();或request.getReader();方法获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>File Uploadtitle>
head>
<body>
    <form action="http://192.168.199.240:9527/wooyun.jsp?user=zsy&pass=123" method="post" enctype="multipart/form-data">
        1<input type="checkbox" value="1" name="i" checked="checked" /> 2<input type="checkbox" value="2" name="i" checked="checked" /><br/>
        <input type="file" name="file" /><br/>
        <input type="text" value="

你可能感兴趣的:(java)