百度的OpenRASP將Gartner在2014年提出的RASP(Runtime Application Self-Protection)安全防護技術進行了開源實現,使其迅速成為企業Web安全防護中的一個重要武器,有效增強防禦體系縱深和對漏洞防護的適應能力。OpenRASP相較與傳統WAF具有誤報率低、高性能等優點。Django是Python實現的Web開發框架,最初被設計用於具有快速開發需求的新聞類站點,目的是要實現簡單快捷的網站開發。
本文介紹了OpenRASP和Django的一些基本內容,在搭建好OpenRASP測試實驗環境的基礎上編寫官方測試用例的自動化漏洞驗證腳本,最後通過Django快速開發報警接收Web應用。
二、OpenRASP和Django概述1. RASP(Runtime Application Self-Protection)企業部署的應用程式通常在一個複雜且分散的環境中,包括有網絡、作業系統和資料庫。這樣通常導致應用程式的安全體系結構碎片化,缺乏精確和可靠的安全路線圖。開發運行時應用程式自我保護(RASP)的概念主要是為了解決開發人員面對威脅時所採用的特殊方法。
開發人員常常傾向於採用靜態和傳統的AppSec方法,而不是解決應用程式的設計缺陷,這些方法在面對複雜的安全威脅時往往會失敗。這種完全不同的安全控制層常常成為應用程式、基礎設施和安全層的多個組件的瓶頸,但隨著RASP解決方案的出現,應用程式安全不再是對威脅的一種隨意反應。RASP是應用程式安全生態系統中的一項創新,通過提供對隱藏的漏洞的更多可見性來處理運行時對軟體應用層的攻擊。它本質上是與應用程式或其運行時環境集成的安全軟體,並不斷攔截對應用程式的調用,以檢查其安全性。RASP軟體不會等待威脅影響應用程式。相反,它會在進入應用程式的流量中主動搜索惡意軟體,防止欺詐性調用在應用程式內部執行。通過留在應用程式內,RASP解決方案中和已知的漏洞,並保護應用程式免受未知的0day攻擊,無需任何人工幹預。因此,RASP提供了一種與傳統安全方法(如web應用程式防火牆(WAF))在概念上不同的安全範式,後者通過阻止所有可疑流量來保護應用程式。
RASP已經發展成為一種成熟的應用程式內安全概念,它允許開發者以多種方式對威脅進行防禦。根據開發人員想要在應用程式或伺服器中實現RASP安全層的方式,有四種方法可供選擇:
(1). Servlet filters, SDKs and plugins
這種方法適用於監控和檢查到達應用程式代碼之前Apache Tomcat或其他Web伺服器傳入的HTTP請求和數據。
(2). Binary instrumentation
該方法適用於在應用程式中構建監視和控制元素,前者標識正在運行的應用程式中的安全事件,而後者記錄此類事件的日誌並阻止它們。
(3). JVM replacement
此方法採用RASP層替換標準庫(JAR或JVM(對於Java))來偵聽對支持庫的調用,並在調用被攔截時的應用規則。因此RASP對app代碼庫和系統調用路由框架有一個整體的了解,這使得RASP可以通過對應用調用的被動監控來了解機器行為和序列流。
(4). Virtualization
Virtualization,或者叫做containerized runtime protection,它創建一個應用程式副本,並通過使用規則來控制應用程式該如何被保護,同時控制應用程式在副本上的運行時行為。RASP監視和學習應用程式代碼路徑、邏輯構造、參數化和生成的輸出等,然後應用於應用程式請求。這種方法有助於區分清楚的請求和惡意請求,並允許採取適當的補救措施。
2. OpenRASPOpenRASP是百度安全推出的一款免費、開源的應用運行時自我保護產品。
官網地址為:https://rasp.baidu.com
若要了解更多細節,請閱讀 謝么 - 百度安全的 OpenRASP 項目,究竟是什麼?以及 OpenRASP 最佳實踐
OpenRASP目前支持Java和PHP兩種類型的伺服器,其快速上手部署請參考官方文檔:https://rasp.baidu.com/doc/install/software.html
本文中,通過採用CentOS 8 + Tomcat在VM虛擬機中快速搭建好實驗環境,使用OpenRASP v1.3.5進行實驗。
3. DjangoDjango是一個由Python編寫的具有完整架站能力的開源Web框架。使用Django,只要很少的代碼,開發人員就可以輕鬆地完成一個正式網站所需要的大部分內容,並進一步開發出全功能的Web服務。Django本身基於MVC架構,即Model(模型)+View(視圖)+ Controller(控制器)設計模式,因此天然具有MVC的出色基因:開發快捷、部署方便、可重用性高、維護成本低等優點。除此之外,Django還具備自己的admin後臺,開發人員只需要通過簡單的幾行配置和代碼就可以實現一個完整的後臺數據管理控制平臺,是Django的亮點之一。但是Django由於Python性能的限制,無法作為大流量的伺服器使用。這裡我們只需要在內網中監控OpenRASP的報警,從業務場景上可以使用Django。
三、OpenRASP示例的自動化攻擊腳本1. 搭建OpenRASP測試用例在搭建好OpenRASP環境中,可以將官方給的測試用例部署在伺服器上,這裡筆者採用Java伺服器的測試用例:https://rasp.baidu.com/doc/install/testcase.html
重啟Tomcat伺服器後我們可以看到OpenRASP 官方測試用例集界面:
單擊進入每一個測試用例可以獲取到不同攻擊的完整URL:
2. 編寫自動化攻擊腳本這裡我們使用Python來編寫自動化攻擊腳本,編寫該腳本的目的是方便後續開發報警接收應用時能夠更加高效率地去模擬攻擊並獲取報警信息。
首先,單擊每個非正常調用URL,異常調用會被OpenRASP攔截下來:
這樣我們可以將需要的攻擊URL(僅含參數)放入數組中:
class OpenRASPTest:definit(self, host):self.headers = {「User-Agent」: 「Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) 「「Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0」}self.host = hostself.AttackUrl =[『/vulns/001-dir-1.jsp?dirname=../../../../../../../../../../../../../../../var/log/『,『/vulns/002-file-read.jsp?file=../../../../../../../../../../../../../../../etc/passwd』,『/vulns/002-file-read.jsp?file=../../../conf/tomcat-users.xml』,『/vulns/004-command-2.jsp?cmd=ls+-la+/『,『/vulns/005-file-write.jsp?filename=reports/../123.jsp&filedata=some-webshell-data』,『/vulns/008-file-upload.jsp』,『/vulns/009-deserialize.jsp?id=whoami』,『/vulns/010-jstl-import.jsp?url=file:///etc/『,『/vulns/010-jstl-import.jsp?url=http://192.168.1.1『,『/vulns/011-ssrf-commons-httpclient.jsp?url=http://www.baidu.com『,『/vulns/011-ssrf-httpclient.jsp?url=http://127.0.0.1.xip.io『,『/vulns/011-ssrf-httpclient.jsp?url=http://uee.me/cFas3『,『/vulns/011-ssrf-urlconnection.jsp?url=http://127.0.0.1.xip.io『,『/vulns/011-ssrf-okhttp.jsp?url=http://127.0.0.1.xip.io『,『/vulns/011-ssrf-okhttp3.jsp?url=http://127.0.0.1.xip.io『,『/vulns/019-file-delete.jsp?filename=reports/../testfile.txt』,『/vulns/020-random-file.jsp?filename=reports/../123.jsp&filedata=some-webshell-data』,『/vulns/020-random-file.jsp?file=../../../../../../../../../../../../../../../etc/passwd』,『/vulns/021-nio-file.jsp?filename=reports/../123.jsp&filedata=some-webshell-data&mode=write』,『/vulns/021-nio-file.jsp?file=../../../../../../../../../../../../../../../etc/passwd&mode』『=read』,『/vulns/021-nio-file.jsp?filename=reports/../testfile.txt&mode=delete』,『/vulns/021-nio-file.jsp?filename=reports/../testfile.txt&dst=reports/../testfile.jsp&mode』『=link』,『/vulns/021-nio-file.jsp?dirname=../../../../../../../../../../../../../../../var/log/&mode』『=list』,『/vulns/021-nio-file.jsp?filename=reports/../rename.txt&dst=reports/../rename.jsp&mode=rename』]其中,攻擊主機IP由對象創建者傳入。在攔截頁面中按下f12進行html頁面分析,找到400響應碼對應的標籤並複製該標籤的xpath。
這裡我們需要對比標籤內容判斷攻擊攔截情況,或者直接通過HTTP響應碼來判斷也行。接下來我們編寫兩個方法進行GET和POST請求:
def get_url(self, url):
req = urllib.request.Request(self.host + url, headers=self.headers)
html = urllib.request.urlopen(req).read().decode(『utf-8』)
content = etree.HTML(html)
print(self.load_page(content) +』 攻擊已被攔截』)defpost_url(self, url):
files = {『file』: open(『1.jsp』, 『rb』)}
data = {}
res = requests.post(self.host + url, data=data, files=files)
reg = re.compile(r』400 - Request blocked by OpenRASP』)
resstr = reg.search(res.text)
print(resstr)請求接收到的頁面可以傳給load_page方法,在該方法中使用上面複製的xpath獲取關鍵標籤中的內容。
def load_page(self, con):
xpath = 『/html/body/div[2]/div/div[2]/h2』
down = con.xpath(xpath)
try:
result = down[0].text
exceptIndexError:
result = 「ERROR」
print(「攻擊失敗」)
return result最後,完整的代碼如下:
import urllib.request
from lxml import etree
import requests
import re
import datetimeclass OpenRASPTest:
攻擊Payload URL
definit(self, host):
self.headers = {
「User-Agent」: 「Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) 「
「Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0」
}
self.host = hostself.AttackUrl =[『/vulns/001-dir-1.jsp?dirname=../../../../../../../../../../../../../../../var/log/『,
『/vulns/002-file-read.jsp?file=../../../../../../../../../../../../../../../etc/passwd』,
『/vulns/002-file-read.jsp?file=../../../conf/tomcat-users.xml』,
『/vulns/004-command-2.jsp?cmd=ls+-la+/『,
『/vulns/005-file-write.jsp?filename=reports/../123.jsp&filedata=some-webshell-data』,
『/vulns/008-file-upload.jsp』,
『/vulns/009-deserialize.jsp?id=whoami』,
『/vulns/010-jstl-import.jsp?url=file:///etc/『,
『/vulns/010-jstl-import.jsp?url=http://192.168.1.1『,
『/vulns/011-ssrf-commons-httpclient.jsp?url=http://www.baidu.com『,
『/vulns/011-ssrf-httpclient.jsp?url=http://127.0.0.1.xip.io『,
『/vulns/011-ssrf-httpclient.jsp?url=http://uee.me/cFas3『,
『/vulns/011-ssrf-urlconnection.jsp?url=http://127.0.0.1.xip.io『,
『/vulns/011-ssrf-okhttp.jsp?url=http://127.0.0.1.xip.io『,
『/vulns/011-ssrf-okhttp3.jsp?url=http://127.0.0.1.xip.io『,
『/vulns/019-file-delete.jsp?filename=reports/../testfile.txt』,
『/vulns/020-random-file.jsp?filename=reports/../123.jsp&filedata=some-webshell-data』,
『/vulns/020-random-file.jsp?file=../../../../../../../../../../../../../../../etc/passwd』,
『/vulns/021-nio-file.jsp?filename=reports/../123.jsp&filedata=some-webshell-data&mode=write』,
『/vulns/021-nio-file.jsp?file=../../../../../../../../../../../../../../../etc/passwd&mode』
『=read』,
『/vulns/021-nio-file.jsp?filename=reports/../testfile.txt&mode=delete』,
『/vulns/021-nio-file.jsp?filename=reports/../testfile.txt&dst=reports/../testfile.jsp&mode』
『=link』,
『/vulns/021-nio-file.jsp?dirname=../../../../../../../../../../../../../../../var/log/&mode』
『=list』,
『/vulns/021-nio-file.jsp?filename=reports/../rename.txt&dst=reports/../rename.jsp&mode=rename』]defstart_attack(self):
print(「當前時間為 {0} 攻擊開始…」.format(datetime.datetime.now()))
print(「0x00. 完成File.listFiles 遍歷目錄攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[0])
print(「0x01. 完成任意文件下載/讀取漏洞攻擊,讀取passwd文件,響應結果如下:」)
self.get_url(self.AttackUrl[1])
print(「0x02.完成任意文件下載/讀取漏洞攻擊,讀取tomcat-users文件,響應結果如下:」)
self.get_url(self.AttackUrl[2])
print(「0x03. 完成命令執行後門攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[3])
print(「0x04. 完成任意文件寫入攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[4])
print(「0x05. 完成任意文件上傳漏洞攻擊,採用commons.io 方式,響應結果如下:」)
self.post_url(self.AttackUrl[5])
print(「0x06. 完成使用 InvokerTransformer 反序列化並執行命令攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[6])
print(「0x07. 完成JSTL import 任意文件包含/SSRF攻擊,file 協議讀取目錄,響應結果如下:」)
self.get_url(self.AttackUrl[7])
print(「0x08. 完成JSTL import 任意文件包含/SSRF攻擊,http 協議 SSRF,響應結果如下:」)
self.get_url(self.AttackUrl[8])
print(「0x09. 完成SSRF攻擊,通過commons.httpclient 方式,響應結果如下:」)
self.post_url(self.AttackUrl[9])
print(「0x0A. 完成SSRF攻擊,通過HttpClient調用方式,響應結果如下:」)
self.get_url(self.AttackUrl[10])
print(「0x0B. 完成SSRF攻擊,通過HttpClient重定向方式,響應結果如下:」)
self.get_url(self.AttackUrl[11])
print(「0x0C. 完成SSRF攻擊,通過jdk 中的 URL.openConnection 調用方式,響應結果如下:」)
self.post_url(self.AttackUrl[12])
print(「0x0D. 完成SSRF攻擊,通過okhttp方式,響應結果如下:」)
self.get_url(self.AttackUrl[13])
print(「0x0E. 完成SSRF攻擊,通過okhttp3方式,響應結果如下:」)
self.get_url(self.AttackUrl[14])
print(「0x0F. 完成任意文件刪除攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[15])
print(「0x10. 完成RandomAccessFile 文件讀寫攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[16])
print(「0x11. 完成RandomAccessFile 文件讀寫攻擊,讀取linux下的passwd文件,響應結果如下:」)
self.get_url(self.AttackUrl[17])
print(「0x12. 完成NIO 文件調用攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[18])
print(「0x13. 完成linux下的NIO 文件讀取調用攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[19])
print(「0x14. 完成NIO 文件刪除攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[20])
print(「0x15. 完成NIO 文件硬連結攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[21])
print(「0x16. 完成linux下的NIO 文件目錄遍歷攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[22])
print(「0x17. 完成NIO 文件重命名攻擊,響應結果如下:」)
self.get_url(self.AttackUrl[23])defget_url(self, url):
req = urllib.request.Request(self.host + url, headers=self.headers)
html = urllib.request.urlopen(req).read().decode(『utf-8』)
content = etree.HTML(html)
print(self.load_page(content) +』 攻擊已被攔截』)defpost_url(self, url):
files = {『file』: open(『1.jsp』, 『rb』)}
data = {}
res = requests.post(self.host + url, data=data, files=files)
reg = re.compile(r』400 - Request blocked by OpenRASP』)
resstr = reg.search(res.text)
print(resstr)defload_page(self, con):
xpath = 『/html/body/div[2]/div/div[2]/h2』
down = con.xpath(xpath)
try:
result = down[0].text
exceptIndexError:
result = 「ERROR」
print(「攻擊失敗」)
return resultif name == 「main「:
四、OpenRASP報警推送數據分析
ORT= OpenRASPTest(「http://192.168.xx.xx:80xx「)
ORT.start_attack()OpenRASP報警可以通過HTTP、Syslog、Kafka、郵件和釘釘推送
報警至少每隔120秒發送一次,這個時間可以增加
這裡我們的實驗使用HTTP推送並在區域網內接收報警信息,這裡我們可以填寫報警接收伺服器的URL,不過我們的接收程序還沒開發,所以我們先點擊「推送數據格式說明」看看推送數據格式詳情
可以看到OpenRASP一個完整的JSON數據包格式如下:
當發生攻擊事件時,OpenRASP 將會記錄以下信息:
欄位說明rasp_idRASP agent idapp_id應用IDevent_type日誌類型,固定為 attack 字樣event_time事件發生時間request_id當前請求IDrequest_method請求方法intercept_state攔截狀態attack_source攻擊來源 IPtarget被攻擊目標域名server_hostname被攻擊的伺服器主機名server_ip被攻擊目標 IPserver_type應用伺服器類型server_version應用伺服器版本…………詳情請參考:OpenRASP日誌說明
五、基於Django的報警接收Web應用開發1. 安裝Django執行
pip install django
安裝Django
2. 新建Django項目打開Pycharm,點擊new project。選擇Django,輸入項目名之後點擊CREATE按鈕新建Django項目:
Pycharm會自動幫我們把新建好的Django項目目錄結構初始化,這時我們需要自己在settings.py中將ALLOWED_HOSTS修改為,注意不要漏掉「,」:
ALLOWED_HOSTS = [『*』, ]
以保證區域網正常請求訪問,如何仍然無法訪問到Django伺服器,可以將
『django.middleware.csrf.CsrfViewMiddleware』,
給注釋掉
最後,在Django configuration中將host改為0.0.0.0,重啟Django伺服器之前記得在主機的防火牆中添加8000埠
3. 接收POST路由在項目目錄下新建views.py,獲取POST請求
def openraspalarm(request):
if request.method ==』POST』:
postBody = request.body之後在urls.py中添加路由:
path(『alarm/『, openraspalarm),
4. 讀取報警信息使用JSON讀取POST請求體中的報警信息。首先引入json庫:
import json
然後使用json.loads方法將json格式數據轉換為字典
json_result = json.loads(postBody)
print(『完整的JSON字符串如下:\n {0}』.format(json_result))並且可以直接輸出app_id鍵的值
print(json_result[『app_id』])
由於每隔120秒Agent會將所有還沒報警的事件進行推送,所有推送的數據包中可能會包含多個事件,這些事件的報警均在data鍵內。最後,我們循環輸出每個事件的json標籤內容:
data = json_result[『data』]
print(『data長度為{0}』.format(len(data)))
for i in range(len(data)):
print(『\033[45m第 {0} 個攻擊\033[0m』.format(i))
print(『攻擊源IP位址:{0}』.format(data[i][『attack_source』]))
print(『攻擊類型:{0}』.format(data[i][『attack_type』]))
print(『攻擊發生時間:{0}』.format(data[i][『event_time』]))
print(『事件類型:{0}』.format(data[i][『event_type』]))
print(『攻擊向量頭部:{0}』.format(data[i][『header』]))
print(『攔截狀態:{0}』.format(data[i][『intercept_state』]))
print(『當前URL:{0}』.format(data[i][『url』]))
print(『攻擊應用ID:{0}』.format(data[i][『app_id』]))
print(『攻擊定位:{0}』.format(data[i][『attack_location』]))
print(『攻擊變量信息:{0}』.format(data[i][『attack_params』]))
print(『插件檢測算法:{0}』.format(data[i][『plugin_algorithm』]))
print(『檢測準確率:{0} %』.format(data[i][『plugin_confidence』]))
print(『檢測報告:{0}』.format(data[i][『plugin_message』]))
print(『RASP agent ID:{0}』.format(data[i][『rasp_id』]))
print(『當前請求ID:{0}』.format(data[i][『request_id』]))
print(『請求方法:{0}』.format(data[i][『request_method』]))
print(『被攻擊伺服器主機名:{0}』.format(data[i][『server_hostname』]))
print(『被攻擊目標IP及網卡接口名稱:{0}』.format(data[i][『server_nic』]))
print(『應用伺服器類型:{0}』.format(data[i][『server_type』]))
print(『應用伺服器版本:{0}』.format(data[i][『server_version』]))
print(『被攻擊目標域名:{0}』.format(data[i][『target』]))這時我們可以運行自動化攻擊腳本,為OpenRASP後臺創造報警數據,當一個報警周期(120秒)過後,就能接收到OpenRASP後臺傳來的報警數據:
我們可以將接收到的報警數據根據實際需要存入資料庫或做進一步的分析處理。
六、總結有關RASP的理念,早在2014年就已經被提出,並且被世界頂級諮詢公司Gartner列為應⽤安全領域的「關鍵趨勢」。OpenRASP作為其落地的項目已經做到相對成熟的地步,儘管它還存在一些問題,但是相信在未來幾年我們能看到OpenRASP能夠帶來更好的表現。本文在搭建好OpenRASP測試實驗環境的基礎上編寫官方測試用例的自動化漏洞驗證腳本,最後通過Django快速開發報警接收Web應用。本文的主要目的在於更好地去使用和研究OpenRASP技術,希望這篇文章能夠給大家提供有用思路和方法。
附錄參考:
https://www.freebuf.com/articles/web/217421.html
https://www.freebuf.com/articles/web/164413.html
https://www.appsealing.com/what-is-runtime-application-self-protection/
https://rasp.baidu.com/download/OpenRASP%20Internals.pdf?from=header
https://rasp.baidu.com/doc/install/software.html
https://rasp.baidu.com/doc/install/testcase.html
https://rasp.baidu.com/doc/setup/log/main.html#format