The Case Against Python 3

Steve D'Aprano steve+python at pearwood.info
Mon Nov 28 18:54:07 EST 2016


On Tue, 29 Nov 2016 09:35 am, Gregory Ewing wrote:

> Steve D'Aprano wrote:
>> I daresay you are right that a sufficiently clever adversary may have
>> found an exploit. But there's no sign that anyone actually did find an
>> exploit, until f-strings made exploiting this trivial.
> 
> The person who wrote the bug report found at least one
> way of exploiting it that doesn't require f-strings.

Oops, of course you are right. The bug report says:

    My first discovery was that nothing prevents an input plural 
    string that resembles a function call:

       gettext.c2py("n()")(lambda: os.system("sh"))

    This is of course a low risk bug, since it requires control
    of both the plural function string and the argument.

http://bugs.python.org/issue28563


And later on:

    Instead of passing a string to eval, we can build a string
    from characters in the docstrings available in the context
    of the gettext module:
    [...]
    This will successfully spawn a shell in Python 2.7.11.

Which I had forgotten about. (And by the way: the exploit could have been
avoided by refusing any string which contains an underscore. If you can't
avoid vulnerable code, at least make attackers work hard to exploit it.)

The point I was making was this comment:

    Bonus: With the new string interpolation in Python 3.7, 
    exploiting gettext.c2py becomes trivial:

       gettext.c2py('f"{os.system(\'sh\')}"')(0)

    The tokenizer will recognize the entire format-string as 
    JUST A STRING [emphasis added]

f-strings are *not* "just a string", as this shows -- they're actually
equivalent to a call to eval. And *that* is my point: we've picked a syntax
which looks like a static literal string for something which is not only a
function call but equivalent to the second most powerful (and hence
dangerous) built-in function call available in the language.

Normally I think Guido's instinct for syntax and language features is pretty
good, but here I think he's made a blunder. The original proposal on the
Python-Ideas mailing list was for syntax to automatically perform *only*
name lookups so that 

    f"{x}"

was syntactic sugar for

    "{x}".format(x=x)


and from that simple request it has grown in scope to the point that we can
now write:

    f"{os.system('sh')}"

as syntactic sugar for:

    str(eval("os.system('sh')"))

Yay for progress!



> I agree that f-strings are not to blame here.

I've already said that. Certainly the *vulnerability* comes from the use of
eval by gettext.c2py, but the TRIVIAL *exploit* comes from f-strings.


> If we really 
> want to avoid breaking anyone's ill-conceived attempts at
> sandboxing eval, we'd better not add anything more to the
> language, ever, because nobody can foresee all the possible
> consequences.

Now you're just being silly, this isn't "anything", it is a specific design
decision: something which looks like, and is treated by the tokeniser, as a
string but is actually a hidden call to eval.

And in fact I did foresee the consequences. I never predicted this *exact*
vulnerability, of course, but I did say that disguising a call to eval
inside something which looks like a string would lead to trouble. I was
poo-pooed for that idea, so please excuse me if I gloat a little when the
first "trouble" is discovered before the feature is even available in a
production release.

I don't say this as if it were an amazing feat of prediction. I'm astonished
that apparently others can't, or won't, see it. Its like when people store
weed killer or some other poison in a soft-drink bottle, complete with the
label still on, and then are surprised when someone gets poisoned.

http://www.dailymail.co.uk/news/article-1337101/Father-Phillip-Ward-dies-accidentally-drinking-weedkiller-Lucozade-bottle.html

(Not the first, or last, case of accidental poisoning from herbicide or
pesticide stored inappropriately.)



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list