47 comments

[ 3.3 ms ] story [ 118 ms ] thread
TIL python has a webbrowser module
Me too... Hilarious!
And it's even using the user preferred browser!

Tried it on my MacBook M1 and it opens Brave which is my main browser.

Wonder if it's using the same mechanism as the macOS open(1) command does for opening URLs. Or maybe even calling the /usr/bin/open binary.

I just learned this myself the other day...

I was looking at the "Automate the Boring Stuff with Python" book by Al Sweigart (which admittedly i have not read cover to cover, but rather use it as a reference) for some help with a little polite scraping, and lo and behold the webbrowser module...See chapter 12: https://automatetheboringstuff.com/2e/chapter12/

I have not played much with it yet, but man, python continues to impress me!

Me too and I'm a long time Python programmer.
But not a texteditor.

You can use this library for that: https://pypi.org/project/texteditor/

(Disclaimer: I’m the author)

There's not really a reason for a built in text editor
Is not a built in text editor, is a command to open programmatically your default text editor to edit a file (new or existing).

There are several cases when you would want to do that, for example, think on the git CLI, that opens one so you can write a commit message, etc.

And is not only about using the EDITOR variable, most non-programmers doesn’t have one defined or it points to vim or something equally terrible for non technical users.

(comment deleted)
Idle
You missed the point of what the library does. Similar to webbrowser (which isn't a full browser), all it does is open your default text editor.
Back in the day, python was popular because it was a “batteries included” language, which meant I supposedly had almost everything you would need right there in the standard language. These days of cause no one runs python without going on a shopping spree of additional packages on pypi, so it would be less of a big deal if it didn’t include a browser lib, or a GUI lib out of the box.
I get a type error in line 13 running the code:

    TypeError: openssl_md5() takes no keyword arguments
From the docs

> Changed in version 3.9: All hashlib constructors take a keyword-only argument usedforsecurity...

To add to this - antigravity is part of the Python standard library, and you want to run the version that's shipped with your version of Python, not the version from the latest development build. Do "python3 -m antigravity".
This looked innocuous at first glance, but this "antigravity" Easter egg has been found to have security implications.

See "Hacking with Environment Variables", which specifically exploits the antigravity module for arbitrary code execution - https://www.elttam.com/blog/env/#content

Previous HN comments: https://news.ycombinator.com/item?id=23828045

(comment deleted)
I'm unconvinced. "The ability to turn this into arbitrary code execution depends on what other executables are available on the system" is doing a LOT of heavy lifting here.

Remember: you have control over only the environment variables, and you do not have the ability to alter the arguments. In order for this to represent arbitrary code execution, you need for the system to have an executable on it that, when executed with the argument "https://xkcd.com/353/", grants you arbitrary code execution.

So, you have full control over the environment variables, and that's it. How do you turn effectively

    [binary] "https://xkcd.com/353/"
into arbitrary code execution, where [binary] is an executable already on the machine?
There is a proof of concept given, "Figure-4: arbitrary code execution achieved using multiple environment variables against Python 2 and Python 3":

    $ docker run -e 'PYTHONWARNINGS=all:0:antigravity.x:0:0' -e 'BROWSER=perlthanks' -e 'PERL5OPT=-Mbase;print(`id`);exit;' python:2.7.18 python /dev/null
    uid=0(root) gid=0(root) groups=0(root)
    Invalid -W option ignored: unknown warning category: 'antigravity.x'

    $ docker run -e 'PYTHONWARNINGS=all:0:antigravity.x:0:0' -e 'BROWSER=perlthanks' -e 'PERL5OPT=-Mbase;print(`id`);exit;' python:3.8.2 python /dev/null
    uid=0(root) gid=0(root) groups=0(root)
    Invalid -W option ignored: unknown warning category: 'antigravity.x'
TL;DR: 'import antigravity' is fine, but Python can be tricked with environment variables to use Perl as the web browser, which has arbitrary code execution through environment variables.
Interesting, that's pretty cool. Thanks!
Defense in depth would disagree that it is fine. I'm pretty sure this vulnerability can go much further if you combine it with, for example, the `https_proxy` environment variable.
And Perl (specifically "perlthanks") was just the first thing they found that worked. Bash had similar problems in the past, and I imagine there's other "installed by default" stuff that can be coaxed into running code in environment variables.
And specifically, although it is nowadays uncommon for Python scripts to be run the way old-school WSGI web services were, with a script being fired up for every request and mapping user-controlled query parameters to environment variables, this was at one time actually the motivating example for https://www.python.org/dev/peps/pep-0333/ ! It's a common enough attack pattern that https://capec.mitre.org/data/definitions/77.html exists and specifically cautions applications against trusting environment variables. There are very few places in Python where upon importing a module, an environment variable is trusted and triggered; all of them should be seen as security holes.
Surely this is only a security risk if antigravity is imported?

What cause would there be to import antigravity in a CGI service – or anything other than a terminal in which you could already execute arbitrary code?

I am struggling to understand, even with the proof of concept, how the situation could arise where an attacker could realistically exploit this, based largely on the uselessness of the antigravity module.

I left one window open, but what is to guarantee a burgler would use that window??
Absolutely, I agree now that I see it, and these seem like numerous innocent-seeming footguns just waiting for abuse.

Add the not-too-far-fetched-seeming assumption that it’s fine to let the Internet define your environment variables, and it’s a plausible exploit.

This is why, as another commenter points out, defense in depth is so important.

See the "Making Progress With PYTHONWARNINGS" section in the article posted - setting the environment variable PYTHONWARNINGS to a value mentioning the antigravity module causes it to be imported, even if there's no "import antigravity" statement in the actual source.
Not sure how I missed that – it was even in the proof of concept in the message I replied to. Thanks for the help.

There are multiple failings that don’t seem like a big deal taken in isolation:

- The PYTHONWARNINGS and whole Warning Filters system.

- The webbrowser module executing a given executable from an environment variable.

- The antigravity module running webbrowser.open() on import.

All of these sort of seem a bit like sacrificing good taste for convenience. But in combination, in a situation where you don’t control the environment variables, they do lead to arbitrary code execution.

In other words, it’s not all antigravity’s fault, but those side effects on import make this possible.

You're right about sacrificing good taste for convenience, but since this is an easter egg, it's sacrificing good taste for a laugh, and I think that pushes it over the precipe over into being a bad idea.
If you can influence arbitrary environment variables, you usually have a lot more options - you could set PYTHONHOME or PYTHONPATH (influence where Python finds imports), LD_LIBRARY_PATH (influence where shared libraries come from), PATH (influence where the Python command itself comes from), etc. etc.

In this particular case, they happened to be in a situation where they couldn't easily figure out how to create new files but they could set environment variables. From a design perspective, as a reviewer, I would not believe that this makes it safe to have attacker-controlled environment variables, although I admit I wouldn't know exactly how (and this post is a pretty clever approach to making the attack work).

(comment deleted)
The next section of the link literally goes into how to achieve all of that using a call to perl.
If you have the ability to set environment variables then it's basically already game over, with or without the existence of the antigravity module.

You could set PATH to change which files are executed in certain scenarios. You could set SSLKEYLOGFILE which logs session keys to an arbitrary file, essentially nullifying TLS/SSL protections. On Linux you can just set PROMPT_COMMAND to whatever you want and it'll be executed any time a bash prompt is printed.

It's an interesting attack vector, but a vulnerability requires impact, and I'm not sure this has very much.

I chuckled a bit, but it took me a while to realize what code repository it is from. Chuckled again.
Might not be obvious to all:

This is in the python standard library. So you can open a python REPL and type `import antigravity`

A description of what the code does:

First, the line

webbrowser.open("https://xkcd.com/353/")

is obvious, it opens a browser pointing to the relevant XKCD comic, containing a joke which inspired this code.

Then, a geohash function is declared which takes the user's current latitude/longitude, and the current date-time, and returns a target latitude/longitude. According to a previous comic (https://xkcd.com/426/) this corresponds to the game of 'geohashing' where players are supposed to physically travel to arbitrary locations and prove it.

I've long known that `import antigravity` would open the xkcd comic, but I never knew there was also a geohashing function!

That was a favorite activity of mine in high school. On Friday evening, we'd look at the nearest graticule(s) to our town and pick one for a Saturday day trip. Trucking out to a random point on a map is a great way to explore your metro area and see things you never would otherwise.

Heartily recommend trying it out. Looks like the map (linked from the comic) is no longer working: http://carabiner.peeron.com/xkcd/map/map.html

But, with this function, there may yet be hope!

I added the geohash() function and am delighted that it eventually got found.