Last active
December 21, 2015 09:38
-
-
Save mleinart/6285975 to your computer and use it in GitHub Desktop.
Graphite patch for http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2013-5093
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/webapp/graphite/remote_storage.py b/webapp/graphite/remote_storage.py | |
index 693e0fd..d51d27c 100644 | |
--- a/webapp/graphite/remote_storage.py | |
+++ b/webapp/graphite/remote_storage.py | |
@@ -5,11 +5,7 @@ from urllib import urlencode | |
from django.core.cache import cache | |
from django.conf import settings | |
from graphite.render.hashing import compactHash | |
- | |
-try: | |
- import cPickle as pickle | |
-except ImportError: | |
- import pickle | |
+from graphite.util import unpickle | |
@@ -79,7 +75,7 @@ class FindRequest: | |
response = self.connection.getresponse() | |
assert response.status == 200, "received error response %s - %s" % (response.status, response.reason) | |
result_data = response.read() | |
- results = pickle.loads(result_data) | |
+ results = unpickle.loads(result_data) | |
except: | |
self.store.fail() | |
@@ -126,7 +122,7 @@ class RemoteNode: | |
assert response.status == 200, "Failed to retrieve remote data: %d %s" % (response.status, response.reason) | |
rawData = response.read() | |
- seriesList = pickle.loads(rawData) | |
+ seriesList = unpickle.loads(rawData) | |
assert len(seriesList) == 1, "Invalid result: seriesList=%s" % str(seriesList) | |
series = seriesList[0] | |
diff --git a/webapp/graphite/render/datalib.py b/webapp/graphite/render/datalib.py | |
index 15da42d..6a2a234 100644 | |
--- a/webapp/graphite/render/datalib.py | |
+++ b/webapp/graphite/render/datalib.py | |
@@ -19,6 +19,7 @@ from django.conf import settings | |
from graphite.logger import log | |
from graphite.storage import STORE, LOCAL_STORE | |
from graphite.render.hashing import ConsistentHashRing | |
+from graphite.util import unpickle | |
try: | |
import cPickle as pickle | |
@@ -173,7 +174,7 @@ class CarbonLinkPool: | |
len_prefix = recv_exactly(conn, 4) | |
body_size = struct.unpack("!L", len_prefix)[0] | |
body = recv_exactly(conn, body_size) | |
- return pickle.loads(body) | |
+ return unpickle.loads(body) | |
# Utilities | |
diff --git a/webapp/graphite/render/views.py b/webapp/graphite/render/views.py | |
index ecf76ec..ba15921 100644 | |
--- a/webapp/graphite/render/views.py | |
+++ b/webapp/graphite/render/views.py | |
@@ -25,7 +25,7 @@ try: | |
except ImportError: | |
import pickle | |
-from graphite.util import getProfileByUsername, json | |
+from graphite.util import getProfileByUsername, json, unpickle | |
from graphite.remote_storage import HTTPConnectionWithTimeout | |
from graphite.logger import log | |
from graphite.render.evaluator import evaluateTarget | |
@@ -297,7 +297,7 @@ def renderLocalView(request): | |
optionsPickle = reqParams.read() | |
reqParams.close() | |
graphClass = GraphTypes[graphType] | |
- options = pickle.loads(optionsPickle) | |
+ options = unpickle.loads(optionsPickle) | |
image = doImageRender(graphClass, options) | |
log.rendering("Delegated rendering request took %.6f seconds" % (time() - start)) | |
return buildResponse(image) | |
diff --git a/webapp/graphite/storage.py b/webapp/graphite/storage.py | |
index dd2a710..dd4876b 100644 | |
--- a/webapp/graphite/storage.py | |
+++ b/webapp/graphite/storage.py | |
@@ -1,7 +1,9 @@ | |
import os, time, fnmatch, socket, errno | |
+from django.conf import settings | |
from os.path import isdir, isfile, join, exists, splitext, basename, realpath | |
import whisper | |
from graphite.remote_storage import RemoteStore | |
+from graphite.util import unpickle | |
from django.conf import settings | |
try: | |
@@ -304,7 +306,7 @@ class WhisperFile(Leaf): | |
if exists(context_path): | |
fh = open(context_path, 'rb') | |
- context_data = pickle.load(fh) | |
+ context_data = unpickle.load(fh) | |
fh.close() | |
else: | |
context_data = {} | |
diff --git a/webapp/graphite/util.py b/webapp/graphite/util.py | |
index ada524d..54f425b 100644 | |
--- a/webapp/graphite/util.py | |
+++ b/webapp/graphite/util.py | |
@@ -12,6 +12,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License.""" | |
+try: | |
+ import cPickle as pickle | |
+ USING_CPICKLE = True | |
+except: | |
+ import pickle | |
+ USING_CPICKLE = False | |
+ | |
+try: | |
+ from cStringIO import StringIO | |
+except ImportError: | |
+ from StringIO import StringIO | |
+ | |
from django.core.exceptions import ObjectDoesNotExist | |
from django.contrib.auth.models import User | |
from graphite.account.models import Profile | |
@@ -65,3 +77,52 @@ except Profile.DoesNotExist: | |
log.info("Default profile does not exist, creating it...") | |
defaultProfile = Profile(user=defaultUser) | |
defaultProfile.save() | |
+ | |
+# This whole song & dance is due to pickle being insecure | |
+# The SafeUnpickler classes were largely derived from | |
+# http://nadiana.com/python-pickle-insecure | |
+# This code also lives in carbon.util | |
+if USING_CPICKLE: | |
+ class SafeUnpickler(object): | |
+ PICKLE_SAFE = { | |
+ 'copy_reg': set(['_reconstructor']), | |
+ '__builtin__': set(['object']), | |
+ } | |
+ | |
+ @classmethod | |
+ def find_class(cls, module, name): | |
+ if not module in cls.PICKLE_SAFE: | |
+ raise pickle.UnpicklingError('Attempting to unpickle unsafe module %s' % module) | |
+ __import__(module) | |
+ mod = sys.modules[module] | |
+ if not name in cls.PICKLE_SAFE[module]: | |
+ raise pickle.UnpicklingError('Attempting to unpickle unsafe class %s' % name) | |
+ return getattr(mod, name) | |
+ | |
+ @classmethod | |
+ def loads(cls, pickle_string): | |
+ pickle_obj = pickle.Unpickler(StringIO(pickle_string)) | |
+ pickle_obj.find_global = cls.find_class | |
+ return pickle_obj.load() | |
+ | |
+else: | |
+ class SafeUnpickler(pickle.Unpickler): | |
+ PICKLE_SAFE = { | |
+ 'copy_reg': set(['_reconstructor']), | |
+ '__builtin__': set(['object']), | |
+ } | |
+ | |
+ def find_class(self, module, name): | |
+ if not module in self.PICKLE_SAFE: | |
+ raise pickle.UnpicklingError('Attempting to unpickle unsafe module %s' % module) | |
+ __import__(module) | |
+ mod = sys.modules[module] | |
+ if not name in self.PICKLE_SAFE[module]: | |
+ raise pickle.UnpicklingError('Attempting to unpickle unsafe class %s' % name) | |
+ return getattr(mod, name) | |
+ | |
+ @classmethod | |
+ def loads(cls, pickle_string): | |
+ return cls(StringIO(pickle_string)).load() | |
+ | |
+unpickle = SafeUnpickler | |
diff --git a/webapp/graphite/whitelist/views.py b/webapp/graphite/whitelist/views.py | |
index 5baec8d..36374d7 100644 | |
--- a/webapp/graphite/whitelist/views.py | |
+++ b/webapp/graphite/whitelist/views.py | |
@@ -13,14 +13,12 @@ See the License for the specific language governing permissions and | |
limitations under the License.""" | |
import os | |
-try: | |
- import cPickle as pickle | |
-except ImportError: | |
- import pickle | |
from random import randint | |
from django.http import HttpResponse | |
from django.conf import settings | |
+from graphite.util import unpickle | |
+ | |
def add(request): | |
metrics = set( request.POST['metrics'].split() ) | |
@@ -43,7 +41,7 @@ def show(request): | |
def load_whitelist(): | |
fh = open(settings.WHITELIST_FILE, 'rb') | |
- whitelist = pickle.load(fh) | |
+ whitelist = unpickle.load(fh) | |
fh.close() | |
return whitelist | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment