6

I am referring to The Underhanded C Contest and Hiding Backdoors in plain sight. They demonstrate, that you can with some cleverness, write Open Source code, publish the code and hide a backdoor in plain sight, which is very difficult and unlikely to be spotted by people looking at the code.

Is this an exclusive issue with C and by extension with C++ as well?

Is this an issue for lower level languages, let's say assembler code?

Is this an issue, with higher level languages, let's say python code?

Edit: To avoid confusion with Ken's work, I removed his quote.

3
  • I think you are misunderstanding Ken's talk. He described a situation where the code in question is part of the compiler. That compiler, with the malicious code, is used to compile a new version of the compiler with no malicious source code, however it injects the malicious binary code into the new executable. Hence code review will never find this bug unless you review every previous version of the compiler. (This also extends to everything the compiler trusts, such as the OS, drivers, editors, etc.)
    – Ladadadada
    Sep 12, 2013 at 15:18
  • Ken's idea works with any compiled language where the compiler is written in the same language and the general idea can be extended much further.
    – Ladadadada
    Sep 12, 2013 at 15:19
  • I am aware of Ken's work on compromised compilers, however, this question has nothing to do with it. Updated original question.
    – user28464
    Sep 12, 2013 at 15:32

1 Answer 1

9

This is about the easiness of "hiding a backdoor in plain sight", namely in the source code. We are no longer talking about compilers, but about human brains. The whole idea is to make the source code "look legit" for a human code inspector, who do not have time to really delve into details, and who uses his human mind for the task, with all the quirks that a human mind can have.

Since this is psychology, there will be no clear-cut, mathematical answer. However, we can somehow argue that it has a lot to do with language complexity. To "hide in plain sight", the attacker must do two things:

  1. Use as backdoor an obscure rarely used sub-feature of the programming language.
  2. Make use of this sub-feature in a code structure which still looks "natural", as in: "an honest programmer would have written code which that structure".

At one extreme, you have assembly. Assembly is not complex. Assembly is very "poor": it works over a few constructions (just "jumps") and a restricted list of opcodes. The code writer may find some rarely used opcodes or opcode behaviours, but he will have a hard time fulfilling the second condition, because "normal assembly code" must have a lot of structure that the programmer enforces on himself, because otherwise his task becomes infeasible. Any use of a rarely used opcode will appear as such; the code will look funky, and this will raise alarms in the brain of the code inspector. Thus, a failure at hiding.

At the other extreme, you have quite complex languages such as, say, C# or Python. There are a lot of subtle details and behaviours (in particular, operator overload in C# can be used to trigger a lot of things from code which looks "normal"). On the other hand, such languages tend to have a very "deterministic" specification, with no or very few undefined behaviours, which helps the code inspector.

Thus, C and, even more so, C++, are a soft spot for backdoor hiding. They combine an awful lot of undefined behaviours with relatively complex syntax games. C++ adds many extra twists to the syntax, and many ways to add "hidden behaviours" (operator overloading, implicitly called constructors and destructors...).

For these reasons, we could claim that different languages are differently vulnerable to plain-sight-hiding attacks, and C++ is very vulnerable, while bare-bone languages like Assembly are less vulnerable through a poorer syntax, and higher-level languages are less vulnerable through a stricter language specification on corner cases.

All of this is of course very debatable. As I said, it is about psychology, and when it comes to analysing how human minds work, each human will think about his own mind first. Since not everybody thinks in the same way, answers are bound to vary.

Also, I cheated. My argument above about how assembly makes it harder to hide backdoors in plain sight is valid only for a code inspector with a lot of time on his hands. To implement a given, non-trivial task, you usually need a lot more lines of code in assembly than in C++. This makes code inspection much slower in assembly if we count it per task.

Indeed, if we set the situation such that the code inspector has a fixed time budget for going through the source code of an application which fulfils a given set of functionalities, then the code inspector will be more likely to find backdoors if he gets the C++ source code than if he gets the assembly. Indeed, given the C++ source code he can compile it to assembly (the -S flag on GCC...), so his task cannot be harder with C++ than with assembly.

So defining the exact question is important. When we talk about hidden backdoors and code inspectors, we must clearly state what kind of code inspection is performed, for how long and under what conditions. Otherwise, just about any answer can be given and justified.


Answer to the original version of the question

The whole point of the Ken Thompson is that the backdoor is not in the source code -- it is in the compiler, and more precisely in the compiler's executable file, not in the compiler's source code as well.

With more details: there is an "evil code" E1 which, when inserted into the system login application, grants access to the attacker for some "hidden" password which the attacker knows. There is another evil code E2, meant to be resident in the compiler's executable file. E2 does two things:

  • If the file which is being compiled is the source code of the system login application, then it inserts E1 into the produced executable.
  • If the file which is being compiled is the source code of the compiler itself, then it inserts E2 (i.e. itself) into the produced executable.

That way, neither the source code of the system login application, nor the source code of the compiler, contains anything evil. The evil code E2 propagates a bit like a virus, by infecting executable files (but it is picky since it infects only the compiler's executable file, and only when that executable file is being produced by action of the compiler). A secondary feature of that evil code E2 is to plant the evil code E1 into the system login application.

  • Since there is nothing evil in any source code, whether the source is "open source" or not has no relevance whatsoever. That's the whole point of planting the backdoor in the compiler: so that the honest source code needs not be modified in any way.

  • Nothing here is specific to C or C++. It would equally apply to every programming language which is compiled; and a similar backdoor could be devised for interpreted languages too (evil code E3 in interpreter modifies behaviour of a script upon execution; evil code E4 is planted in the compiler in order to replicate itself into new compiler instances, and to plant E3 into the interpreter when the interpreter itself is compiled).

If that is still unclear to you, go read the Dragon Book (available for free from any decent University library) until you grasp the notion of compilation.

1
  • 1
    I am aware of Ken's work on compromised compilers, however, this question has nothing to do with it. Updated original question.
    – user28464
    Sep 12, 2013 at 15:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .