There is a symlink attack because dmd overwrite the output file without checking whether it is a link or not. This can allow an attacker to overwrite any file accessible with the rights of the user that runs the compiler. For example .ssh/authorized_keys could be replaced in order to get remote access to the system.
To reproduce:
$ touch untouchable
$ ln -s untouchable malicious
$ echo 'import std.stdio; void main() {writeln("owned");}' > malicious.d
$ dmd malicious.d
$ ./untouchable
owned
Comment #1 by cpicard — 2016-01-20T13:09:24Z
For the record, I also checked for an attack through the generation of object files ($ ln -s untouchable malicious.o) but those aren't concerned.
Comment #2 by ag0aep6g — 2016-01-20T13:44:29Z
I understand you're asking for dmd to replace the link, or maybe fail with an error message, instead of writing through the link.
Not an unreasonable request, but is there really a strong expectation that programs don't write through symlinks, so that this is surprising behavior? After all, the victim has to set up the symlink. If the attacker could set it up, they could just overwrite untouchable directly.
For what it's worth, gcc seems to behave like dmd:
----
$ touch untouchable
$ ln -s untouchable a.out
$ cat > test.c << code
#include <stdio.h>
int main() {printf("owned\n"); return 0;}
code
$ gcc test.c
$ ./untouchable
owned
----
So maybe it's actually more expected that symlinks are followed, and not replaced.
Comment #3 by cpicard — 2016-01-20T14:07:39Z
That may be true for gcc but I still think it is an issue. The classic setup isn't with two plain files, an attacker would give a huge project through a github repo for example and hide his link in layers of directories. An alert user may notice it but that's true of all attacks. On the other end a less wise user might just compile the project, in that case I'm definitive that getting ssh access for example is possible (I did it on my machine).
Another argument in favor of simply removing symlinks is that this behaviour isn't consistent. Object files get their symlinks replaced, and if the file targetted by the symlink isn't empty the symlink gets removed too (which makes for a less dangerous vulnerability as we can create a file or change it if empty, but not change its content otherwise).
This should be corrected.
Comment #4 by ag0aep6g — 2016-01-20T14:13:56Z
Ok, I think I get it now. The point is that it's relatively easy for an attacker to slip a symlink through, compared to a shell command or a compiler argument.
Comment #5 by ketmar — 2016-01-22T00:37:05Z
by the way: the compiled code can do that too, so i suggest blocking code generation as a whole. generating native code is dangerous!
Comment #6 by cpicard — 2016-01-22T02:09:05Z
@ketmar
This not an issue about executing code. This is a case where the victim doesn't have to execute *any* line of the given program, not one line, compiling it is enough to be attacked.
Besides even if it were about executing code note that we already decided that as long as it is in the compilation process it was an issue. Otherwise why did we bother enforce that the compiler can't read or write arbitrary files during CTFE? Isn't it because we know that we can't expect the user to carefully read every line of the code he is compiling and that the compiler had some responsability reguarding this in the compilation process?
I stand on my position, an attack is possible that requires only to run the compiler without passing it any special argument, without executing any script and without executing the resulting program. It is of the responsability of the compiler.
Comment #7 by ketmar — 2016-01-22T02:14:49Z
(In reply to Cédric Picard from comment #6)
> Otherwise
> why did we bother enforce that the compiler can't read or write arbitrary
> files during CTFE?
'cause this breaks the rule of "same code should behave the same in compile time and in runtime."
> Isn't it because we know that we can't expect the user to
> carefully read every line of the code he is compiling and that the compiler
> had some responsability reguarding this in the compilation process?
no. ;-)
> I stand on my position, an attack is possible
of course, it is possible. i just don't see any reason in curing the symphtoms in this case.
anyway: let it be of "normal" severity then?
Comment #8 by cpicard — 2016-01-22T02:24:23Z
(In reply to Ketmar Dark from comment #7)
> (In reply to Cédric Picard from comment #6)
> > Otherwise
> > why did we bother enforce that the compiler can't read or write arbitrary
> > files during CTFE?
> 'cause this breaks the rule of "same code should behave the same in compile
> time and in runtime."
That too, ok.
> > I stand on my position, an attack is possible
> of course, it is possible. i just don't see any reason in curing the
> symphtoms in this case.
If anything to bring some consistency. Security issues appart compiling the same source code with the same flags and the same files in the folder twice ends up with two completely different results. The first one will create a file elsewhere, at the emplacement targeted by the link while the second compilation will actually do what it should have from the beginning, remove the link and build the executable in place. That's inconsistent. Either we choose to trust the user and follow the link in all circumstances, either we consider that a link isn't special enough to avoid being replaced like any other file and we remove it.
> anyway: let it be of "normal" severity then?
I leave that point to your discretion. I'm a security guy, every vulnerability allowing remote access is critical for me, but it's the developper's job to decide whether it fits their security model or not.
Comment #9 by ketmar — 2016-01-22T02:56:05Z
(In reply to Cédric Picard from comment #8)
> If anything to bring some consistency. Security issues appart compiling the
> same source code with the same flags and the same files in the folder twice
> ends up with two completely different results.
which, of course, can be caused by many other reasons. like, for example, remounting (rebinding) output point (which can be caused by some external condition, of course). so should we check for mount binds? and if we should, what should be considered "safe"? absense of binds? but why?
that's why i think that such checks curing the symptoms, and of little importance.
it's not the compiler task to check file pathes, it's a task of tar/git/etc — the program that was used to unpack the archive. and if the user managed to create such weird environment... well, it's time time fix the user, not the compiler. ;-)
btw, aren't creating executables done by "ld"? so it looks like "ld" bug, not dmd.
> > anyway: let it be of "normal" severity then?
> I leave that point to your discretion. I'm a security guy, every
> vulnerability allowing remote access is critical for me, but it's the
> developper's job to decide whether it fits their security model or not.
i'm not a dmd developer too. ;-) yet while it's surely a security flaw, for me dmd is the wrong place where one should try to solve it.
Comment #10 by cpicard — 2016-01-22T03:33:07Z
(In reply to Ketmar Dark from comment #9)
> which, of course, can be caused by many other reasons. like, for example,
> remounting (rebinding) output point (which can be caused by some external
> condition, of course). so should we check for mount binds? and if we should,
> what should be considered "safe"? absense of binds? but why?
>
> that's why i think that such checks curing the symptoms, and of little
> importance.
>
> it's not the compiler task to check file pathes, it's a task of tar/git/etc
> — the program that was used to unpack the archive.
I don't think so, packing links is totally normal, following them without question but only sometimes isn't.
> and if the user managed
> to create such weird environment... well, it's time time fix the user, not
> the compiler. ;-)
>
> btw, aren't creating executables done by "ld"? so it looks like "ld" bug,
> not dmd.
Fair point.
> > > anyway: let it be of "normal" severity then?
> > I leave that point to your discretion. I'm a security guy, every
> > vulnerability allowing remote access is critical for me, but it's the
> > developper's job to decide whether it fits their security model or not.
> i'm not a dmd developer too. ;-) yet while it's surely a security flaw, for
> me dmd is the wrong place where one should try to solve it.
Comment #11 by razvan.nitu1305 — 2022-10-28T10:46:43Z
I agree with Ketmar here. It is not the job of the compiler to check whether an input file is a symlink or not.