0x01 前言
不管對於web開發亦或是滲透來講,熟練掌握http協議的核心運作要點都是入門必備科目,如果連這些最基礎的東西都沒掌握熟練,又何談下一步呢,因為http會貫穿於後續整個web滲透過程,當然啦,就http協議本身來講,還是非常非常複雜的,單單靠一篇文章就想全面透徹的掌握http協議,畢竟不太現實,所以我們今天也只是模擬站在一個專業入侵者的角度上來重新理解http協議最核心的一些點,中間也會穿插著說明這些點容易帶來的一些安全問題,如果大家真的非常有興趣,想繼續深入學習http協議,建議參考 《http權威指南》,著實是本http方面的好書,起碼,個人是這樣覺得的,既然是說協議,也就意味著某些東西可能會有些抽象,不過大家不用擔心,我會用盡量用最容易理解的方式把它說明白,廢話到此為止,咱們開始
0x02 初始 http 簡要工作過程
客戶端向web服務器請求所指定的資源(request) => web服務器(默認端口通常為80,8080)
web服務器響應給客戶端所請求的對應的資源數據(response) => 客戶端(隨機端口和目標web服務端口進行連接)
簡單來講,http就是一套用來規定”請求(request)”和”響應(response)”的規範,以此來保證客戶端和服務端能夠正常的進行通信
用白話來講 客戶端向服務器端請求,然後服務器端把被請求的東西交給客戶端 這就算一次完整的http通信
0x03 理解http最基礎的一些特性
簡潔快速: 客戶端只需要傳輸具體的 "請求方法" 和要請求的 "資源路徑",服務器便可根據此快速把數據響應給客戶端
靈 活: http 幾乎可以傳輸 "任意類型" 的數據[支持什麼樣的類型都是被定義在MIME中的],比如:文本,視頻,圖片,壓縮包,二進制程序等等……
無連接: 每次連接只處理一個請求,服務器處理完請求並收到客戶端正確應答後,即立刻斷開連接,這樣可以大大的節省傳輸時間
無狀態: 其實,說白點,就是客戶端和服務器之間的每個連接都是相互獨立的,彼此並不知道彼此的存在,所以,每個連接並不能相互依賴或者在彼此上附加數據,比如:
一個連接的數據沒有處理完,那麼它就必須再重新傳,而不能繼續再'斷點'上累加,也就是所謂的 "http是無記憶的",不過在 http 1.1 會保持一個tcp連接
0x04 需要熟記的一些http請求頭字段作用 [因為請求頭對於我們來講是可控的,所以針對http的利用大多也都集中在請求頭中] :
http請求方法[常用的GET,POST,HEAD] 所請求的資源在服務器上的位置 所使用的http協議版本(1.1/1.0)
POST /drupal/?q=node&destination=node HTTP/1.1
指明host請求頭[如果使用 http 1.1版本 必須指定該字段,它通常是目標域名,比如,如果有多個虛擬主機,我們就必須指定不同的域名才能訪問到不同的網站目錄]
Host: www.target.org
客戶端操作系統和瀏覽器版本信息 [ 如果你是用瀏覽器發起的http請求,默認就會加上這些東西 ]
有些網站為了記錄用戶信息,可能會把這這兒的數據也一併記錄到數據庫中,最後可能會導致的結果就是,腳本在數據庫中select這些數據的時候難免會被注入
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:48.0) Gecko/20100101 Firefox/48.0
告訴服務器端,客戶端可以接收那些格式的內容,下面表示可以接收html或xml文件
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
告訴服務器端,客戶端所支持的語言環境,有很多帶有多語言切換的國際版網站[如攜程]很有可能就會根據此字段來判斷客戶端的語言環境,然後再返回對應語言的頁面
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
告訴服務器端客戶端支持哪種壓縮方式,默認都是gzip,加快傳輸速度,不壓縮服務器會直接返回文本,如果數據量比較大,可能會響應很慢,所以默認都會壓縮再傳輸
Accept-Encoding: gzip, deflate[壓縮算法]
Accept-Charset: 有時候你還會在請求頭中看到這樣的字段,它是用來指定客戶端所支持的字符集
記錄到達該頁面的上一個url地址,這也是請求頭中經常會被利用的一個字段,有時候程序也會把這個記錄到數據庫中,方便後續判斷,同樣的問題select的時候容易被注入
Referer: http://192.168.3.21/drupal/
很多web程序都會基於此字段把客戶端的真實ip入庫,還是一樣問題,入庫不免又要select,也就意味著可能被注入
x_forwarded_for
請求頭中cookie信息,很顯然,這裡的cookie是沒有任何參數的,有時候服務器端會把一些敏感參數都寫到客戶端cookie中做驗證
尤其是一些帶有用戶功能的站點,cookie可能還包含了很多其他的參數
比如一些商城,論壇類的程序,既然cookie裡面有動態參數,那我們不妨就針對這些參數下手,如,sql注入,cookie盜取偽造登錄...
當然,關於cookie自身的屬性還有很多,下面會再單獨說,這裡暫時先有個影響就好
Cookie: PHPSESSID=27112bba201d8add0ebbbf8dcdd2bde8; security_level=0; has_js=1
告訴服務器端,在完成客戶端響應後先不要立馬關閉當前tcp連接,close表示立即關閉,而KeepAlive的意思就是保持連接 [有規定時長,超過會自動斷開]
Connection: close
指定消息主體中的內容類型,如,用url編碼...
Content-Type: application/x-www-form-urlencoded
消息主體的內容長度,單位字節
Content-Length: 119
指明到達當前頁面的原始位置在哪裡,這也是個比較重要的字段,後續單獨說明
origin:
http內置身份驗證,向服務器端提交認證證書
authorization:
如果你想,多線程下載或者斷點續傳可以在請求頭中加上該字段
range:
注意,這裡還有個空格
POST消息主體[如果是GET請求,是沒有消息主體的,它的參數都直接是放到url中傳的],和get中的參數一樣,一個key對應一個values
name=admin&pass=admin&form_build_id=form-s6S30NYh20X2pmy-P7R9ybb2fBLkfWlXzosu85DYtn4&form_id=user_login_block&op=Log+in
0x05 需要熟記的一些http請求方法:
GET 向服務器請求url中所標識的資源,有時候還會在要請求的資源上附帶一些參數,供腳本到後端數據庫去增刪改查
POST 在向服務器請求資源的同時附加一些新的數據在消息主體中
常用於各種大數據表單提交,比如:上傳,登錄/註冊,留言板,凡是有大量數據提交的地方基本都會用到
HEAD 只返回響應頭部,不返回主體的html數據
PUT 嘗試向服務器請求存儲一個指定資源,如,經典的webdav 寫漏洞
MOVE向服務器請求移動某個資源
COPY向服務器請求拷貝某個資源
DELETE 向服務器請求刪除url所指定的資源
OPTIONS 通過此方法可幫助我們查詢服務器支持哪些請求方法
通常,我們可以直接用這種方式來探測目標服務器是否支持一些高危方法 例如:put,delete,move,copy
下面是通過OPTIONS方法獲取到的服務器響應結果:
HTTP/1.1 200 OK
Date: Thu, 23 Jun 2016 07:31:08 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
MS-Author-Via: DAV
Content-Length: 0
Accept-Ranges: none
DASL:
DAV: 1, 2
Public: OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH
Allow: OPTIONS, TRACE, GET, HEAD, DELETE, COPY, MOVE, PROPFIND, PROPPATCH, SEARCH, MKCOL, LOCK, UNLOCK
Cache-Control: private
0x06 需要熟記的一些http響應頭字段作用[對於響應頭,我們可以直接利用的並不多,除了獲取一些敏感信息外,別的暫時可能對我們實際作用並不是特別大]
響應的http版本號 響應的狀態碼
HTTP/1.1 200 OK
服務器端時間
Date: Fri, 17 Mar 2017 12:36:26 GMT
web服務器版本信息,比如,針對各種web服務器的解析漏洞利用,就需要事先知道明確的web服務器版本類型及其對應的詳細版本號,然後再針對性的嘗試利用
以apache和nginx為例,很多人為了安全起見,在編譯安裝就會在源碼中把這些版本特徵都改掉,或者在安裝後通過修改配置文件把這些版本信息都隱藏掉
Server: Apache/2.2.8 (Ubuntu) DAV/2 mod_fastcgi/2.4.6 PHP/5.2.4-2ubuntu5 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
web服務器端腳本信息,就php而言,不同的版本,一些漏洞的利用方法也是不一樣的
X-Powered-By: PHP/5.2.4-2ubuntu5
是否緩存到瀏覽器
Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0
向客戶端瀏覽器設置cookie,第一次設置完成後,後續客戶端再來訪問服務器會一直帶著這個cookie
set-cookie:
P3P頭主要用來保護用戶隱私
P3P: CP=" OTI DSP COR IVA OUR IND COM "
表示是否允許通過跨域ajax請求獲取資源
access-control-allow-origin
服務器可以通過此字段告訴瀏覽器定時刷新頁面,用的非常少
refresh:
指定消息主體內容的緩存過期時間
Expires: Sun, 19 Nov 1978 05:00:00 GMT
告訴瀏覽器該資源的最後修改時間
Last-Modified: Fri, 17 Mar 2017 12:36:26 +0000
響應消息主體的語言,這還要看客戶端請求頭中的語言是什麼
Content-Language: en
用於重定該響應,在php中有個header()函數和此功能類似
location: 指定要跳轉到的地址
響應完成後是否立馬關閉當前tcp連接
Connection: close
Keep-Alive: timeout=5, max=100
消息主體內容的格式和字符集
Content-Type: text/html; charset=utf-8
響應消息主體內容的長度,單位字節
Content-Length: 8114
0x07 需要熟記的一些 http 響應狀態碼
200 表示客戶端請求提交成功,並且服務端正常響應數據
比如:我們在成功上傳webshell以後,會直接去訪問url,根據返回,來判斷shell是否執行成功,一般看下響應頭就可以了,成功即返回200,如果被限制執行則可能會返回403
201 一般用put方法,成功在服務器端創建文件後都會返回這個狀態碼
301 請求的資源被找到,永久性的將請求重定向到location所指定的url上,後面的所有請求都將被重定向
302 請求的資源被找到,只是暫時性的將請求重定向到localtion所指定的url上,隨後的請求將不再重定向
400 客戶端提交了一個無效的http請求
401 表示你訪問所請求的資源之前需要http身份驗證,認證通過後才能訪問該資源
403 禁止任何人訪問該資源,有時候我們在盲打後臺或者嘗試注入的時候可能就會遇到這樣的狀態碼,多半是因為禁止目錄遍歷或者waf攔截,再或者就是web端做了一些敏感檢測策略[如,nginx配置就相當靈活,完全可以自定義返回狀態碼]
404 表示所請求的資源在服務器上不存在
405 當服務器不支持所請求的方法,一般都會返回這個狀態碼 例如:put copy move delete
413 有時候在測試緩衝區溢出時會返回此狀態碼
500 請求的資源在服務器端執行出錯,比如,在請求時加了一些特殊字符,或者服務端腳本自身的語法出錯,都會導致500
502 一般是由於網站連接過多,服務器暫時無法正常響應造成的
503 雖然web服務器運轉正常,但後端的腳本程序無法做出響應
0x08 什麼是 cookie
除了正常的GET或者POST傳參之外,cookie屬於另一種傳參方式,它無需用戶或者應用干預,客戶端和服務器的每次請求響應都會帶上該cookie,需要注意的是,cookie是存在客戶端瀏覽器的緩存或者硬盤上的臨時文件中的,對於cookie中的一些敏感參數[如,賬號密碼…],一般都設有過期時間[過期會自動刪除],當然你也可以手動刪除,核心在於它是放在客戶端的[導致可控],有可能就會造成洩露
需要了解的幾個常見的cookie內置屬性:
expires: 指定cookie過期時間
domain: 指定cookie有效作用域
path: 指定cookie有效的url
secure: 用於在https中提交的cookie
httponly: 設置此屬性以後,可以一定程度上防止js直接讀取cookie數據,現在為了防止cookie被盜,一般都會加上此字段,不過上次偶然看了一篇可以繞過httponly的文章,後續再單獨說
0x09 session 又是什麼東西呢
由於http無狀態,要想在服務器端標示跟蹤每個不同的用戶身份,我們就需要利用到session[其實就是cookie中的PHPSEESSIONID,作為用戶唯一標示],比如,我們在寫一些商城程序時,購物車中的數據都會被暫時寫到session中,而session是被放在服務器端的[一般是/tmp目錄下],導致用戶不可控,相對安全一些,如果一個session長時間沒有動作或者你突然關閉了瀏覽器,服務器會自動回收並銷燬對應的sessionid,實際開發中,一般cookie和session都是相互配置使用的
0x10 GET 和 POST 的區別
GET 直接把參數放在url中傳遞,所以在url中可以看得到
POST 把數據附加在http消息主體裡面進行傳遞,在url中是不可見的
注意:它們都有數據長度的限制,但這個並不是http協議本身的限制,它的長度還是取決於當前的操作系統和瀏覽器,有些漏洞的利用可能會用到這種特性,如,超長截斷的問題,不僅僅在web腳本中會出現,數據庫也同樣會出現類似的問題
0x11 關於偽靜態的( url重寫 )問題
本質就是利用正則,隱藏動態參數,加大外部攻擊指定參數的難度,但這樣做的主要目的還是為了更利於搜搜引擎抓取,提高用戶體驗,不過,在猜出對方正則的情況下,還是可以利用本地中轉的方式進行檢測
0x12 http 正向及反向代理
正向代理
很多時候我們會遇到這樣的情況,我們自己沒法正常訪問目標網站,但我們有一臺可以正常訪問目標網站,的機器,然後你可以把自己本地的代理改成那臺機器的ip和指定端口,這樣我們就可以通過那臺機器來訪問目標站了,主要還是數據流向
反向代理:
最典型的應用可能就是ngnix了,當你去訪問一比較大的電商站時[如,jd,噹噹],請求其實會先到達代理服務器,然後由代理服務器來分發給後臺的其他web服務器處理,至於如何分發則由其內部的負載均衡算法決定,理解數據流向就好,關於架構後續有機會再單獨說
0x13 瞭解完 http 最重要的內容以後,我們再來簡單認識下,什麼是 URL 和 URI
URI[統一資源標識符]作用:
其實,就是專門用來標示互聯網上所有計算機資源的字符串,當然這些資源不僅僅包括,文檔,圖像,視頻,二進制程序……別人只需通過某種指定的協議以及資源所存放的主機名[ip或域名],資源自身的名稱,資源在該主機下的具體路徑就可以訪問到該資源,所以互聯網上的一切計算機資源都可以被稱作URI,因此它所表示的範圍非常大
例如:下面這些,它們都算URI
http://www.klionsec.org/detail.php?id=12&action=upload
URL[統一資源定位符]作用:
只是用來唯一的標識某臺計算機上的某個資源,它所能代表的範圍相對比較小,URL只是URI的一部分或者說子集,也可以這麼說每個URL肯定都是URI
URL的必要組成部分:
訪問這個資源時所使用的協議: //主機名[一般是域名或者IP]:按規定好的端口去訪問這個資源/具體的資源路徑
注意:http默認的tcp端口是80,如果在瀏覽器中訪問,瀏覽器會默認幫你加上80,所以你在url是看不到的
如果你要訪問的是一個8080端口的web服務,那你就應該這樣寫 http://www.demo.com:8080/file.php?id=12
如果你要訪問的是一個443端口的web服務,你就應該這樣寫 https://www.demo.com:/sslvpn
還有一些web服務可能並不在這些常見web端口上,尤其是目標的一些內部web服務,比如:http://oa.demo.com:12345/login.aspx
例如,下面這句話的意思就表示:
使用http協議去www.xxx.com這個主機上通過8080端口找到files目錄下的xx.rar文件
http://www.xxx.com:8080/files/xx.rar
用於訪問的協議還可以是:
ftp,http,file……
什麼是url傳參:
例如下面這個url:
http://www.xxx.com/news.php?id=12
這句話的意思就相當於,用http協議在www.xxx.com這個主機上找到news.php這個資源,並向這個資源傳了一個名叫id的參數,參數的值為12
‘?’ 在這裡的意思就是給news.php這個文件附加一個參數值,這個值一般都會通過後端腳本提交到數據庫中去增刪改查
如果要同時連續傳遞幾個不同的參數給相應的資源,可以用 ‘&’ 進行連接,例如像下面這樣:
http://wwww.xxx.com/news.php?id=12&uid=34
什麼是相對URI/URL 和 絕對URI/URL[其實,跟絕對路徑和相對路徑差不多]:
絕對URI/URL:
http://www.xxx.com/images/logo.gif
相對URI/URL:
其實,對於URL和URI的概念,大家不必太過糾結,你可以這樣理解
比如,我要去買一件T恤,首先,我要知道這件T恤在哪裡有賣的[這件T恤就相當於我們要訪問的那個計算機資源],當然,我們知道商場肯定有賣的對吧[商場就相當於我們要定位到的主機名],下一步,也許我們就會想到,該怎麼去商場呢,徒步去,打的還是坐公交呢[去商場的方式就相當於我們要使用哪種協議去訪問那個資源],到了商場以後我們還要知道這件T恤在哪家店鋪裡[就相當於我們要訪問的這個資源在這臺主機的哪個目錄下],最後,你找到了那家店鋪裡的那件T恤[也就是你最終要訪問的資源],網絡中的所有資源訪問基本都是類似,我們訪問網絡中的任何一個資源都需要有一個明確的’URI’[如果站到協議底層的角度來說它就不單單是’URI’了]來給我們指路,以找到我們所需要的資源
0x14 在http的基礎上理解 https[TLS/SSL,默認web端口 443],這裡只是先簡單提一嘴,博客中還有一篇專門針對https的簡單說明,大家可以去參考那個
1,瀏覽器將自己支持的一套加密規則發送給網站
2,網站從中選出一組加密算法與HASH算法,並將自己的身份信息以證書的形式返回給瀏覽器,證書裡面包含了網站地址,加密公鑰,以及證書的頒發機構等信息
3,瀏覽器獲得網站證書之後就開始下面的動作:
a) 驗證證書的合法性(頒發證書的機構是否合法,證書中包含的網站地址是否與正在訪問的地址一致等),如果證書受信任,則瀏覽器欄裡面會顯示一個小鎖頭,否則會給出證書不受信的提示
b) 如果證書受信任,或者是用戶接受了不受信的證書,瀏覽器會生成一串隨機數的密碼,並用證書中提供的公鑰加密
c) 使用約定好的HASH算法計算握手消息,並使用生成的隨機數對消息進行加密,最後將之前生成的所有信息發送給網站
4,網站接收瀏覽器發來的數據之後要做以下的操作
a) 使用自己的私鑰將信息解密取出密碼,使用密碼解密瀏覽器發來的握手消息,並驗證HASH是否與瀏覽器發來的一致
b) 使用密碼加密一段握手消息,發送給瀏覽器
5,瀏覽器解密並計算握手消息的HASH,如果與服務端發來的HASH一致,此時握手過程結束,之後所有的通信數據將由之前瀏覽器生成的隨機密碼並利用對稱加密算法進行加密
6,也許大家都很清楚,即使是https也並非一定那麼安全,針對https的中間人利用[核心還是需要有一個受信任的證書],我們後續再單獨說,這裡就先大致瞭解下https的基本運作流程
0x15 說完http ,我們就來簡單看下,如何快速發起一次http/https請求
1),利用原生的telnet
# telnet www.baidu.com 80
ctrl + ]
記得直接回車再開始輸入下面的內容
GET / HTTP/1.1
HOST:WWW.BAIDU.COM
2),利用curl
-I 只返回HTTP頭
-m 10 最多查詢10s
-o 把輸出提示信息都丟到null裡面去,當然,只有在linux中才有null
-X 用指定http方法請求
win:
只返回響應頭部信息
# curl -I www.dangdang.com
只返回響應的狀態碼
# curl -I -m 10 -o /dev/null -s -w %{http_code} www.dangdang.com
linux:
只返回響應的狀態碼
# curl -I -m 10 -o /dev/null -s -w %{http_code}"\n" www.baidu.com
# curl -I -m 10 -o /dev/null -s -w %{http_code}"\n" 192.168.3.189
3),在瀏覽器地址欄中直接輸入目標域名回車訪問即可
4),所有帶有src,href屬性的html標記,觸發以後默認都會發起一次http請求
5),同樣,利用nc同樣也可以發起一次簡單的http請求
1
# nc -v 192.168.3.3 80
6),手工發起一次https請求,手工發起http請求差不多,只需要先用證書
1
# openssl s_client -quiet -connect www.baidu.com:443
7),利用各類語言中的各種庫函數也可發起http請求,例如php中的一些函數,如header(),location()….
0x16 為了簡單回顧一下上述內容,我們就以古老的IIS寫漏洞利用為例,通過telnet純手工上傳一個asp的webshell
首先,探測目標web服務器是否支持PUT,MOVE,COPY方法,很顯然,這裡是支持put的,因為我的webdav[可以理解為加強版的http,增加了很多方法]事先已經開啟且網站目錄可寫,腳本資源亦可訪問
# telnet 192.168.3.3 81
ctrl + ]
OPTIONS / HTTP/1.1
HOST:192.168.3.3
確認,服務器開啟put方法以後,嘗試請求寫入一句話,最好用txt等一些iis默認不支持解析的文件名,要不然容易出問題
PUT /shell.txt HTTP/1.1
HOST:192.168.3.3
Content-Length:28
記住這裡一定要記得換行才可以,http默認以空行作為請求的結束
最後,將創建好的shell複製重命名成腳本的後綴
COPY /shell.txt HTTP/1.1
HOST:192.168.3.3
Destination: http://192.168.3.3:81/shell.asp
0x17 我們再來粗略看下nginx的日誌文件格式[當然啦,這個格式可以在nginx配置文件中自定義],其它的web服務器日誌格式都基本類似,看日誌主要是想讓大家明白,我們幹活的時候都會在目標留下些什麼
192.168.3.70 - - [25/Jun/2017:17:49:14 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36" "-"
訪問者的ip 訪問時間用什麼方法請求那個文件或目錄資源返回的狀態碼 客戶端瀏覽器信息
0x18 http 運作一次的粗略流程
-->四層tcp連接建立
-->七層http連接建立
-->客戶端向web服務器發送http請求頭部信息
-->等待web服務器返回響應頭和所請求的資源數據,格式以content-type中規定的格式進行顯示,空白行結束此次響應
-->隨後,web服務器關閉當前tcp連接
一點小結:
對於滲透來講,我們需要熟練掌握http最重要的地方,一個是請求頭中的各個字段作用,如,各種請求方法,cookie,user-agent,referer等……另一個就是響應頭中的各個字段,如,狀態碼,server頭,等……以及由此衍生出來的各種安全問題,時間有限,這裡僅僅只是先帶大家入個門,其實,就http協議本身內容還是非常多的,畢竟個人能力精力篇幅都很有限,所以不得不有所側重,不對的地方還望大家多多指正……
閱讀更多 蜻蜓微微點水 的文章