Vulnerability: remote code execution via cache action in MoinMoin ### Impact
The cache action in action/cache.py allows directory traversal through a crafted HTTP request. An attacker who can upload attachments to
the wiki can use this to achieve remote code execution.

### Patches
Users are strongly advised to upgrade to a patched version.

MoinMoin Wiki 1.9.11 has the necessary fixes and also contains other important fixes.

### Workarounds
It is not advised to work around this, but to upgrade MoinMoin to a patched version.

That said, a work around via disabling the `cache` or the `AttachFile` action might be possible.

Also, it is of course helpful if you give `write` permissions (which include uploading attachments) only to trusted users.

### Credits

This vulnerability was discovered by Michael Chapman.

### For more information
If you have any questions or comments about this advisory, email me at [twaldmann@thinkmo.de](mailto:twaldmann@thinkmo.de).
Vulnerability ID: GHSA-52q8-877j-gghq
CWE: CWE-22
Severity score: 0
Project: https://github.com/moinwiki/moin-1.9
Published: 2020-11-11
File: MoinMoin/action/cache.py
Commit message: Merge pull request from GHSA-52q8-877j-gghq

security: fix remote code execution via cache action, CVE-2020-25074
Commit author: TW

Vulnerable version (before fix):
 def key(request, wikiname=None, itemname=None, attachname=None, content=None, se     return key   def put(request, key, data,         filename=None,         content_type=None, def _do_remove(request, key):     remove(request, key)   def _do(request, do, key):     if do == 'get':         _do_get(request, key)     elif do == 'remove':         _do_remove(request, key)  def execute(pagename, request):     do = request.values.get('do')     key = request.values.get('key')     _do(request, do, key) 

Fixed version (after fix):
 def key(request, wikiname=None, itemname=None, attachname=None, content=None, se     return key   def valid_key(key):     # make sure the key looks like keys generated by key()     if not isinstance(key, unicode):         # key is None (not given in url args) or something unexpected         return False     try:         int(key, 16)  # try to evaluate as hex number     except ValueError:         # was not a hex number         return False     return len(key) == 40  # hmac-sha1 hexdigest == 40 hex chars   def put(request, key, data,         filename=None,         content_type=None, def _do_remove(request, key):     remove(request, key)   def execute(pagename, request):     do = request.values.get('do')     key = request.values.get('key')     valid = valid_key(key)  # validate untrusted input     if valid and do == 'get':         _do_get(request, key)     elif valid and do == 'remove':         _do_remove(request, key)     else:         request.status_code = 404 

Patch:
patch: @@ -103,6 +103,19 @@ def key(request, wikiname=None, itemname=None, attachname=None, content=None, se
     return key
 
 
+def valid_key(key):
+    # make sure the key looks like keys generated by key()
+    if not isinstance(key, unicode):
+        # key is None (not given in url args) or something unexpected
+        return False
+    try:
+        int(key, 16)  # try to evaluate as hex number
+    except ValueError:
+        # was not a hex number
+        return False
+    return len(key) == 40  # hmac-sha1 hexdigest == 40 hex chars
+
+
 def put(request, key, data,
         filename=None,
         content_type=None,
@@ -234,14 +247,14 @@ def _do_remove(request, key):
     remove(request, key)
 
 
-def _do(request, do, key):
-    if do == 'get':
-        _do_get(request, key)
-    elif do == 'remove':
-        _do_remove(request, key)
-
 def execute(pagename, request):
     do = request.values.get('do')
     key = request.values.get('key')
-    _do(request, do, key)
+    valid = valid_key(key)  # validate untrusted input
+    if valid and do == 'get':
+        _do_get(request, key)
+    elif valid and do == 'remove':
+        _do_remove(request, key)
+    else:
+        request.status_code = 404
path: MoinMoin/action/cache.py