freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

如何使用Django开发OpenRASP报警接收Web应用
2020-11-02 19:59:12

一、引言

百度的Open­RASP将Gartner在2014年提出的RASP(Run­time Ap­pli­ca­tion Self-Pro­tec­tion)安全防护技术进行了开源实现,使其迅速成为企业Web安全防护中的一个重要武器,有效增强防御体系纵深和对漏洞防护的适应能力。OpenRASP相较与传统WAF具有误报率低、高性能等优点。Django是Python实现的Web开发框架,最初被设计用于具有快速开发需求的新闻类站点,目的是要实现简单快捷的网站开发。

本文介绍了OpenRASP和Django的一些基本内容,在搭建好OpenRASP测试实验环境的基础上编写官方测试用例的自动化漏洞验证脚本,最后通过Django快速开发报警接收Web应用。

二、OpenRASP和Django概述

1. RASP(Run­time Ap­pli­ca­tion Self-Pro­tec­tion)

企业部署的应用程序通常在一个复杂且分散的环境中,包括有网络、操作系统和数据库。这样通常导致应用程序的安全体系结构碎片化,缺乏精确和可靠的安全路线图。开发运行时应用程序自我保护(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. OpenRASP

OpenRASP是百度安全推出的一款免费、开源的应用运行时自我保护产品。

官网地址为: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. Django

Django是一个由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:
def__init__(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 = host

# 攻击Payload URL
self.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 datetime


class OpenRASPTest:
def__init__(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 = host

# 攻击Payload URL
self.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 result


if __name__ == "__main__":
ORT= OpenRASPTest("http://192.168.xx.xx:80xx")
ORT.start_attack()


四、OpenRASP报警推送数据分析

OpenRASP报警可以通过HTTP、Syslog、Kafka、邮件和钉钉推送
报警至少每隔120秒发送一次,这个时间可以增加
这里我们的实验使用HTTP推送并在局域网内接收报警信息,这里我们可以填写报警接收服务器的URL,不过我们的接收程序还没开发,所以我们先点击“推送数据格式说明”看看推送数据格式详情
可以看到OpenRASP一个完整的JSON数据包格式如下:
当发生攻击事件时,OpenRASP 将会记录以下信息:
字段说明
rasp_idRASP agent id
app_id应用ID
event_type日志类型,固定为 attack 字样
event_time事件发生时间
request_id当前请求ID
request_method请求方法
intercept_state拦截状态
attack_source攻击来源 IP
target被攻击目标域名
server_hostname被攻击的服务器主机名
server_ip被攻击目标 IP
server_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
# web应用安全 # Django # 攻防 # OpenRASP # rasp
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录