It rather involved being on the other side of this airtight hatchway
Not every code injection bug is a security hole. Yes, a code injection bug is a serious one indeed. But it doesn’t become a security hole until it actually allows someone to do something they normally wouldn’t be able to. For example, suppose there’s a bug where if you type a really long file name into a particular edit control and click “Save”, the program overflows a buffer. With enough work, you might be able to turn this into a code injection bug, by entering a carefully-crafted file name. But that’s not a security hole yet. All you’ve found so far is a serious bug. (Yes, it’s odd that I’m underplaying a serious bug, but only because I’m comparing it to a security hole.) Look at what you were able to do: You were able to get a program to execute code of your choosing. Big deal. You can already do that without having to go through all this effort. If you wanted to execute code of your own choosing, then you can just put it in a program and run it!
- The hard way
- Write the code that you want to inject, compile it to native machine code.
- Analyze the failure, develop a special string whose binary representation results in the overwriting of a return address, choosing the value so that it points back into the stack.
- Write an encoder that takes the code you wrote in step 1 and converts it into a string with no embedded zeros. (Because an embedded zero will be treated as a string terminator.)
- Write a decoder that itself contains no embedded-zeros.
- Append the encoded result from step 3 to the decoder you wrote in step 4 and combine it with the binary representation you developed in step 2.
- Type the resulting string into the program.
- Watch your code run.
- The easy way
- Write the code that you want to inject. (You can use any language, doesn’t have to compile to native code.)
- Run it.
It’s like saying that somebody’s home windows are insecure because a burglar could get into the house by merely unlocking and opening the windows from the inside. (But if the burglar has to get inside in order to unlock the windows…) Code injection doesn’t become a security hole until you have elevation of privilege. In other words, if attackers gains the ability to do something they normally wouldn’t. If the attack vector requires setting a registry key, then the attacker must already have obtained the ability to run enough code to set a registry key, in which case they can just forget about “unlocking the window from the inside” and just replace the code that sets the registry with the full-on exploit. The alleged attack vector is a red herring. The burglar is already inside the house. Or suppose you found a technique to cause an application to log sensitive information, triggered by a setting that only administrators can enable. Therefore, in order to “exploit” this hole, you need to gain administrator privileges, in which case why stop at logging? Since you have administrator privileges, you can just replace the application with a hacked version that does whatever you want. Of course, code injection can indeed be a security hole if it permits elevation of privilege. For example, if you can inject code into a program running at a different security level, then you have the opportunity to elevate. This is why extreme care must be taken when writing unix root-setuid programs and Windows services: These programs run with elevated privileges and therefore any code injection bug becomes a fatal security hole.
A common starting point from which to evaluate elevation of privilege is the Internet hacker. If some hacker on the Internet can inject code onto your computer, then they have successfully elevated their privileges, because that hacker didn’t have the ability to execute arbitrary code on your machine prior to the exploit. Next time, we’ll look at some perhaps-unexpected places your program can become vulnerable to an Internet attack, even if you think your program isn’t network-facing.