From 50be0b8d91625afc6c3d8ae12ac80d518eb7b684 Mon Sep 17 00:00:00 2001 From: Tom Aarsen <37621491+tomaarsen@users.noreply.github.com> Date: Mon, 2 Jan 2023 15:42:20 +0100 Subject: [PATCH] Resolve RCE vulnerability in local WordNet browser (#3100) * Restrict wordnet app pickle loading: disallow functions and classes * Resolve typo: where -> were In an error message for the wordnet browser --- nltk/app/wordnet_app.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/nltk/app/wordnet_app.py b/nltk/app/wordnet_app.py index 84c2447c71..44991b94e1 100644 --- a/nltk/app/wordnet_app.py +++ b/nltk/app/wordnet_app.py @@ -47,11 +47,10 @@ import base64 import copy -import datetime import getopt +import io import os import pickle -import re import sys import threading import time @@ -60,17 +59,12 @@ from http.server import BaseHTTPRequestHandler, HTTPServer # Allow this program to run inside the NLTK source tree. -from sys import argv, path +from sys import argv from urllib.parse import unquote_plus from nltk.corpus import wordnet as wn from nltk.corpus.reader.wordnet import Lemma, Synset -# now included in local file -# from util import html_header, html_trailer, \ -# get_static_index_page, get_static_page_by_path, \ -# page_from_word, page_from_href - firstClient = True # True if we're not also running a web browser. The value f server_mode @@ -659,6 +653,16 @@ def make_synset_html(db_name, disp_name, rels): return html +class RestrictedUnpickler(pickle.Unpickler): + """ + Unpickler that prevents any class or function from being used during loading. + """ + + def find_class(self, module, name): + # Forbid every function + raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden") + + class Reference: """ A reference to a page that may be generated by page_word @@ -694,7 +698,7 @@ def decode(string): Decode a reference encoded with Reference.encode """ string = base64.urlsafe_b64decode(string.encode()) - word, synset_relations = pickle.loads(string) + word, synset_relations = RestrictedUnpickler(io.BytesIO(string)).load() return Reference(word, synset_relations) def toggle_synset_relation(self, synset, relation): @@ -794,7 +798,7 @@ def page_from_reference(href): except KeyError: pass if not body: - body = "The word or words '%s' where not found in the dictionary." % word + body = "The word or words '%s' were not found in the dictionary." % word return body, word