Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-68966: Fix CVE-2015-20107 in mailcap #91542

Closed
wants to merge 4 commits into from

Conversation

arhadthedev
Copy link
Member

Fixes #68966. See #68966 (comment) for a detailed report.

Likely neeeds a backport up to 3.9.

@arhadthedev arhadthedev marked this pull request as draft April 14, 2022 20:32
@arhadthedev arhadthedev marked this pull request as ready for review April 14, 2022 21:03
Copy link
Member

@gpshead gpshead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it plausible to create a regression test for this?

@gpshead gpshead self-assigned this Apr 14, 2022
@@ -170,7 +172,7 @@ def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]):
for e in entries:
if 'test' in e:
test = subst(e['test'], filename, plist)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test = subst(e['test'], filename, plist)
test = subst(e['test'], MIMEtype, filename, plist)

Don't know that we should fix this, but this is actually broken if test has a %s in it.

@JelleZijlstra
Copy link
Member

This is still vulnerable with a sufficiently buggy test command. Example:

In [111]: c
Out[111]: {'x/y': [{'test': 'eval %s', 'view': 'x'}]}

In [112]: mailcap.findmatch(c, "x/y", filename="exit 1", plist='"echo hello"')
hello
Out[112]: ('x', {'test': 'eval %s', 'view': 'x'})

(had to set plist to work around the bug in #91542 (comment))

@JelleZijlstra
Copy link
Member

Also, this approach will break some legitimate uses. On my Ubuntu system, mailcap has a lot of entries like 'test': 'test -n "$DISPLAY"',. If we don't run that command through a shell, the environment variable won't be expanded.

Maybe mailcap is just vulnerable by design. We can just put a big red banner in the docs and a warning on import, and be done with it when we remove the module in 3.13.

@vstinner
Copy link
Member

Maybe mailcap is just vulnerable by design.

I confirm. For me, it's the responsibility of the caller (users of the mailcap API) to sanitize arguments. Maybe the Python mailcap module documentation can suggest to use shlex.quote()

Does it work to pass shlex.quote(filename) rather than filename to mailcap functions?

I also read somewhere that it's possible to use a temporary file to have a safe filename. But I don't know how to do that.

@encukou
Copy link
Member

encukou commented Apr 20, 2022

Maybe not a temporary file, but use an environment variable and pass "$PYTHON_MAILCAP_FILENAME" to the shell?

@gpshead
Copy link
Member

gpshead commented Apr 20, 2022

Certainly a documentation update is a good idea (make a separate PR for that - we should get that in if nothing else). If we cannot actually fix all possible problems with this because of mailcap's fundamental design is it worth even a partial "fix"?

@vstinner
Copy link
Member

I confirm. For me, it's the responsibility of the caller (users of the mailcap API) to sanitize arguments. Maybe the Python mailcap module documentation can suggest to use shlex.quote()

I'm talking about this:

@gpshead:

FWIW the only third party package we've found using mailcap already does create a safe filename before calling mailcap:
https://github.com/mitmproxy/mitmproxy/blob/main/mitmproxy/tools/console/master.py#L153

There is already a way to avoid mailcap vulnerabilities without fixing the code. It can be the responsibility of the caller to handle security.

Python stdlib has many very dangerous modules like os, shutil, ctypes or pickle. The pickle module clearly documents that it must not used to deserialize untrusted data. mailcap is not an exception here.

@hugovk
Copy link
Member

hugovk commented Apr 26, 2022

Note that mailcap will be deprecated in Python 3.11 and removed in 3.13:

@encukou
Copy link
Member

encukou commented Apr 27, 2022

Sorry for the misguided comment above. findcap returns shell command, of course it can't set environment variables.

Does it work to pass shlex.quote(filename) rather than filename to mailcap functions?

No, because we can't be sure of the context. This was mentioned in the issue in 2015: if the mailcap line already includes quotes, as in text/plain; less '%s'; needsterminal, extra quotes from shlex.quote would un-quote that and open the vulnerability again.

@arhadthedev
Copy link
Member Author

Closing in favor of #91993.

@arhadthedev arhadthedev deleted the fix-cve-2015-20107 branch April 28, 2022 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[CVE-2015-20107] mailcap.findmatch: document shell command Injection danger in filename parameter
7 participants