|
原文链接:从RCE到CVE:攻防实战中的漏洞挖掘
NO.1 项目背景
这次任务的目标是打穿网络边界,进入核心业务网。接入点为某城域网。
进行资产探测发现,该网段存活资产主要为网络设备及部分web服务。Web服务器只暴露了22端口及80、443等web端口。构造字典对ssh进行爆破,因为被测单位刚刚进行了资产梳理,未能成功。对web进行测试,未能找到可以getshell的组件漏洞,未能找到上传点。发现了几个sql注入,但是使用的数据库为oracle,且为时间盲注,利用难度太大,测试陷入了僵局。
* 本文涉及的漏洞,除GateOne已经停止维护外,其他漏洞均已修复。本文漏洞截图均为后期本地复现。
NO.2 重新资产探测
项目组使用nmap重新对C段的1-65535端口进行了端口扫描。扫描发现有多个IP开放了48620端口。随机选择一个IP,对其443端口进行访问,发现该资产为某防火墙。为了便于演示,以下假设该IP为10.1.1.1。
使用nmap -sV对48620端口进行版本识别,发现其承载的服务为ssl/http。访问https://10.1.1.1:48620可以看到一个web页面,标题为Gate One。这个页面与防火墙登录页面画风差别很大,推测可能是一个开源组件。查看前端代码发现COPYRIGHT NOTICE,证实了推测。
百度搜索该组件,发现这是一个SSH连接工具,可以像堡垒机一样登录服务器。简单测试了一下,未发现高危漏洞,只能进行代码审计,寻找漏洞。
NO.3 代码审计
查看GateOne项目的GitHub库:
https://github.com/liftoff/GateOne
得知这是一个基于tornado和html5技术的开源web ssh项目。克隆代码,进行审计。
查看项目文档:
/gateone/docs/build/html/Developer/index.html
可以看到项目核心代码为/gateone/core/server.py。
因为攻防实战中时间紧迫,采取危险函数搜索的方式进行审计。在server.py中搜索open( ,可以看到有大量文件读取操作。
对文件操作进行审计,发现第936行的下载操作的参数可控。在924行查看整个方法:
- def get(self, path, include_body=True):
- session_dir = self.settings['session_dir']
- user = self.current_user
- if user and 'session' in user:
- session = user['session']
- else:
- logger.error(_("DownloadHandler: Could not determine use session"))
- return # Something is wrong
- filepath = os.path.join(session_dir, session, 'downloads', path)
- abspath = os.path.abspath(filepath)
- if not os.path.exists(abspath):
- self.set_status(404)
- self.write(self.get_error_html(404))
- return
- if not os.path.isfile(abspath):
- raise tornado.web.HTTPError(403, "%s is not a file", path)
- import stat, mimetypes
- stat_result = os.stat(abspath)
- modified = datetime.fromtimestamp(stat_result[stat.ST_MTIME])
- self.set_header("Last-Modified", modified)
- mime_type, encoding = mimetypes.guess_type(abspath)
- if mime_type:
- self.set_header("Content-Type", mime_type)
- # Set the Cache-Control header to private since this file is not meant
- # to be public.
- self.set_header("Cache-Control", "private")
- # Add some additional headers
- self.set_header('Access-Control-Allow-Origin', '*')
- # Check the If-Modified-Since, and don't send the result if the
- # content has not been modified
- ims_value = self.request.headers.get("If-Modified-Since")
- if ims_value is not None:
- import email.utils
- date_tuple = email.utils.parsedate(ims_value)
- if_since = datetime.fromtimestamp(time.mktime(date_tuple))
- if if_since >= modified:
- self.set_status(304)
- return
- # Finally, deliver the file
- with io.open(abspath, "rb") as file:
- data = file.read()
- hasher = hashlib.sha1()
- hasher.update(data)
- self.set_header("Etag", '"%s"' % hasher.hexdigest())
- if include_body:
- self.write(data)
- else:
- assert self.request.method == "HEAD"
- self.set_header("Content-Length", len(data))
复制代码
注意关键部分,可以看到系统没有进行任何过滤, 就把path参数拼进了filepath, 然后读取绝对路径,存在目录穿越, 可以读取任意文件。
寻找存在漏洞方法的调用点,在 3692 行可以看到downloads/路由调用了该对象:
在tornado框架中,路由表中的任意一项是一个元组,每个元组包含pattern(模式)和handler(处理器)。当httpserver接收到一个http请求,server从接收到的请求中解析出url path,然后顺序遍历路由表,如果发现url path可以匹配某个pattern,则将此http request交给web应用中对应的handler去处理。
故可构造Poc:
https://10.1.1.1:48620/downloads/../../../../etc/passwd
NO.4 站在巨人的肩膀上RCE
代码审计出了一个任意文件下载漏洞,还是无法getshell。换了一个搜索引擎进行搜索(使用的是多吉搜索https://www.dogedoge.com,收录的博客比较多,质量很高。不过现在已经停止服务了),发现早有大佬对GateOne进行了代码审计。在Ogeek CTF 2019中Easy Realworld Challenge 1&2题目使用了GateOne组件,有许多大神写了Writeup。
在Writeup中,大神不只审出了任意文件下载,还审出了一个RCE。以下根据网上的文章对RCE进行复现。
(1)代码审计
查看文件
/gateone/applications/terminal/plugins/ssh/ssh.py
发现get_host_fingerprint函数在执行连接命令的时候直接拼接了输入参数,存在风险。port及host参数用户可控且未作过滤,可能导致命令注入。
寻找函数触发的地方,在WebSocket中触发执行。
在ssh 连接发起之后,客户端会获取当前连接目标的 ssh 指纹,触发ssh_get_host_fingerprint函数。可以在这个时候从客户端发出一个恶意消息,服务端解析之后直接格式化到命令里就可以 RCE。
查看源代码\gateone\applications\terminal\plugins\ssh\static\ssh.js,发现GateOne可以通过WebSocket来发送消息,从而触发执行。
发送的WebSocket消息:
{"terminal:ssh_get_host_fingerprint":{"host":"192.168.150.128","port":"22; 命令;"}}
同理,查看gateone/applications/terminal/plugins/ssh/ssh.py中的生成ssh密钥相关代码,在openssh_generate_new_keypair方法中可以看到拼接命令处未进行过滤,因为name参数可控, 最后导致key_path可控,导致命令注入。
发送的WebSocket消息:
{"terminal:ssh_gen_new_keypair":{"name":"1';命令;'","keytype":"ecdsa","bits":"521"}}
(2)漏洞复现
本地搭建测试环境并访问。在浏览器中打开开发工具,切换到控制台页面,输入GateOne.ws.send('{"terminal:ssh_get_host_fingerprint":{"host":"10.1.1.1","port":"22;cat /etc/passwd;"}}')。查看响应的报错信息,发现目标主机成功执行命令,读取/etc/passwd文件。
在生产环境中执行该poc,并没有成功RCE,推测可能是防火墙开发商对GateOne进行了魔改,修复了该漏洞。项目组更换目标,通过别的系统打穿了边界,但是GateOne的故事并没有结束。
NO.5 漏洞后续利用
在sumap、fofa及zoomeye中对GateOne进行搜索,可以发现大量资产。
(1)2个CNVD证书及专属SRC
对搜集到的资产进行梳理,发现某厂商的防火墙也使用了gateone组件,端口号为60443,推测该型号防火墙应该都存在RCE及任意文件下载漏洞。使用FOFA及Google Hacking寻找该型号防火墙,编写poc脚本进行测试,发现漏洞确实存在。挑选10个互联网案例,编写漏洞文档,提交CNVD,获得了两个证书。
对本次攻防中遇到的防火墙进行研究,发现互联网上找到的资产都可进行任意文件下载。因为该厂商较大,故提交专属SRC,获得了几千元的奖金。
使用GateOne的产品还很多,小伙伴们可以搜集一下,厂商比较大且可以找到10个的话都能获得CNVD证书,提交其他平台还可能获得一些现金奖励。关于组件漏洞导致的漏洞究竟算不算0day有待商榷,个人认为对组件源码进行引用且耦合度比较高的产品的漏洞可以算0day,例如今年年初的Chrome远程代码执行漏洞就是因为V8引擎漏洞造成的。CNVD和CVE对这种漏洞一般也是收录的。
(2)1个CVE编号
在cve列表里搜索,发现RCE已经有人提了(CVE-2020-20184),但是任意文件下载漏洞还没有提交,可以尝试提交一下。GateOne属于开源项目,可以通过MITRE官方申请cve编号。简单介绍一下申请流程。
首先要对漏洞的原理、利用简单整理一下,编写一份英文文档,给GateOne的github项目提交一个Issues。然后提交到mitre官方平台,页面在https://cveform.mitre.org/。详细的填报可以参考https://www.freebuf.com/news/168362.html。一般过1-2天就会收到审核结果邮件,邮件里会有你提交漏洞的信息和分配的编号。漏洞将发布到CVE官网,国内一般也会同步到CNNVD。
NO.6 总结
(1) 在内网渗透中,资产探测要循序渐进,先找存活主机,再扫常见端口,再针对单个IP进行全端口扫描,这样效率较高且动作不太大。
(2) 针对不常见的组件,可以根据前端代码、页面title判断是否开源组件。如果是开源组件可以通过github、博客、论坛搜索一下,往往会有意外惊喜。
(3) 攻防实战中遇到有源码的组件可以进行一个快速的代码审计,白盒测试比黑盒测试效率能高许多。
(4) 针对组件漏洞,可以去寻找使用该组件的产品,这样能捡不少0day。
|
|