Reflections on Trusting Trust by Ken Thomson Vangelis
Reflections on Trusting Trust by Ken Thomson Vangelis Markatos 1
What is the problem? • Ken Thomson described a hack • That allows attackers to enter a system • The hack can not be found • Even after source code inspection • The hack just vanishes from the source code. 2
Assume we want to change the “login” daemon • So that when we log in it will ask for no password • i. e. original login daemon: login = read_login( ); pass = read_password() If (matches(pass, pass_for_login(login)) return (TRUE); else return (FALSE); 3
Assume we want to change the “login” daemon • Allow the hacker to log in without a password: login = read_login( ); pass = read_password(); If (login == “hacker 1234”) return (TRUE); If (matches(pass, pass_for_login(login)) return (TRUE); else return (FALSE); 4
BUT • This change would attract attention • Someone would read the source code sooner or later • And notice the special case for login “hacker 1234” • Is it possible to do this hack • Without leaving any traces in the source code of the “login” deamon • Without any traces in the source code of any UNIX program? • Without any traces in the source code of the UNIX kernel? • If this is possible • It will be something like a “super trojan” • Non detectable by users • Non detectable by the software company 5
Let us take a step back • Can we teach a compiler new tricks? • Can we hide these new tricks? • Example: • We will teach a compiler to recognize a new character in C: the “v” character • Check out the code that parses a string to print it • Such as printf(“Hello Worldn”) • Or printf(“This is one backslash: \”) c = next( ); If (c != ‘\’) return(c); /* else it is “” */ c = next( ); If (c == ‘\’) /* \ */ return(‘\’); If (c == ‘n’) /* n */ return(‘n’); 6
Teach the compiler to recognize a new character: `v` • Let us add a new character: `v` • The vertical space • printf(“add vertical space v”); c = next( ); If (c != ‘\’) return(c); /* else it is “” */ c = next( ); If (c == ‘\’) /* \ */ return(‘\’); If (c == ‘n’) /* n */ return(‘n’); If (c == ‘v’) /* v */ return (‘v’); 7
BUT • The compiler of the C programming language does not know the character ‘v’ • Thus we can not use it • But ASCII character 11 is the vertical tab • so `11` is the character we are looking for. c = next( ); If (c != ‘\’) return(c); /* else it is “” */ c = next( ); If (c == ‘\’) /* \ */ return(‘\’); If (c == ‘n’) /* n */ return(‘n’); If (c == ‘v’) /* v */ return (11); 8
Summary • We started with a compiler (say A) which did not understand `v` • We added a new char: ‘v’ • We updated the compiler (say we created version B) • To print `11` when it reads `v` • Now, let us compiler B with compiler A to get compiler C • Which can compile programs that have the `v` character • So • A does not understand `v` • B (and C) understand `v` and print (’ 11’) 9
What if? • What if we compile B with C and get compiler D? • B understands ‘v’ will print 11 • C understands ‘v’ will print 11 • D will understand ‘v’ will print ‘v’ • Consider the piece of code to the right • Can B compile it? • Can C compile it? • Can D compile it? c = next( ); If (c != ‘\’) return(c); /* else it is “” */ c = next( ); If (c == ‘\’) /* \ */ return(‘\’); If (c == ‘n’) /* n */ return(‘n’); If (c == ‘v’) /* v */ return (‘v’); 10
What? • D compiles it! • But • Where is the `11`? • Gone! • What if we need to change it? • Gone! • What if it were wrong? • Gone! • What can I do about it? • Nothing! It is gone! • WHAT? ? ? • Can the code just disappear? c = next( ); If (c != ‘\’) return(c); /* else it is “” */ c = next( ); If (c == ‘\’) /* \ */ return(‘\’); If (c == ‘n’) /* n */ return(‘n’); If (c == ‘v’) /* v */ return (‘v’); 11
And now the interesting part starts! • Can we use this “disappearing” code to add backdoors? • Backdoors that disappear from the source code? • But which are always there? • Can we? 12
Assume a compiler compile(s) { char *s; { Do Regular Compilation ; } } compile(s) { char *s; { if(match(s, “this is the login daemon program”)) { inject_and_compile(“if(login == “hacker 1234”) return”); return; } Do Regular Compilation ; } } 13
What can this compiler do? • “Bug” the code of login daemon • Can you see the “bug” in the code of login daemon? • No! • BUT • Careful inspection of the compiler code can expose this “bug” • Can we “disappear” the bug from the compiler source code? 14
Let us “bug” the compiler itself • Let us say that the original trusted compiler of the system is “A” • Let us make the following changes and get compiler “B” compile(s) char *s; { if(match(s, “this is the login daemon program”)) { compile (“if(l == “hacker 1234”)return”); return; } if(match(s, “this is the C compiler running”)) { compile (<add the above if statement and this if statement in the compiler stream>); return; } } • Let us compile “B” with “A” and get compiler “C”. 15
Now remove the “bugs” from the code • Let us go to the original source code of trusted compiler A • No “bugs” – no hacks – no nothing • Let us now compile “A” with “C” and call the result compiler “D” • Is compiler “D” “bugged”? • Is the first compiler “A” bugged? (no!) • Is “C” bugged? (yes – but it is only executable) • Is “D” bugged? ………. (yes – but it is only executable) • So, the source code (“A”) is clean but the object code (“D”) is bugged!!! • How can this be possible? 16
Summary • We described a method which bugs a program without a trace in the source code • So? • KT suggests that • these bugs are very difficult to find • you need to trust the people who write the code 17
- Slides: 17