Introduction
In graphite-web version between 0.9.5 and 0.9.10, a vulnerability exists as a result of unsafe use of the “pickle” module by the product.
The Common Vulnerabilities and Exposures (CVE) project has assigned the name CVE-2013-5093 to this issue. This is an entry on the CVE list (http://cve.mitre.org), which standardizes names for security problems.
Timeline
2013-08-06 – Vendor contacted
2013-08-06 – Vendor confirms issue
2013-08-07 – Sent CVE request, CVE-2013-5093 is assigned
2013-08-20 – Graphite 0.9.11 released
2013-08-20 – Advisory released
Analysis
In graphite-web 0.9.5, a “clustering” feature was introduced to allow for scaling for a graphite setup. This was achieved by passing pickles between servers, and it was introduced in this commit.
The function “renderLocalView”, seen below, takes a request that contains a chart type, and a pickle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def renderLocalView(request): try: start = time() reqParams = StringIO(request.raw_post_data) graphType = reqParams.readline().strip() optionsPickle = reqParams.read() reqParams.close() graphClass = GraphTypes[graphType] options = pickle.loads(optionsPickle) image = doImageRender(graphClass, options) log.rendering("Delegated rendering request took %.6f seconds" % (time() - start)) return buildResponse(image) except: log.exception("Exception in web.render.views.rawrender") return HttpResponseServerError() |
However due to no explicit safety measures having been implemented to limit the types of objects that can be unpickled, this creates a condition where arbitrary code can be executed, as has been documented by Nelson Elhage.
Proof of concept
The proof of concept can be found as a part of the Metasploit Framework graphite_pickle_exec module.