5

I'm unable to understand how arbitrary code execution vulnerabilities are supposed to work.

Wikipedia mentions:

Arbitrary code execution is commonly achieved through control over the instruction pointer of a running process.

Say, the vulnerability is being triggered by some maliciously crafted file that said process is reading. How could it modify the instruction pointer, or, otherwise, corrupt the internal state of the application so as to cause it to execute the attacker's code?

Also, given that modern OSes implement DEP and ASLR, how is this even feasible? The data loaded from the application would not even be executable, and additionally, it's also difficult to determine the offset of the shellcode/payload.

Brownie points for showing a small snippet of code that would be vulnerable to such an exploit.

2 Answers 2

4

Each vulnerability has a specific cause, which in turn leads to ways of exploiting the vulnerability. It is hard to understand how one can exploit something abstract and gain some abstract results, but it is way easier to understand when illustrated with examples. The simplest would be the classical stack-based buffer overflow:

void f(const char *str) {
  char buf[10];
  strcpy(buf, str);
}

This is a primitive example that should be sufficient to demonstrate the issue. Suppose the str argument points to a NULL-terminated string longer than the 10 chars allocated for the variable it is being copied into. What will happen? strcpy() will blindly copy the data over the end of the buffer, overwriting whatever was there. But what exactly is there?

On x86, for example, the buf variable would be allocated on the stack. The stack is also used to save the return address when a CALL instruction is executed. So the (simplified) stack layout would look like this (addresses are increasing top to bottom):

...
buf[0..3]
buf[4..7]
buf[8..9]
return address
...

So, by supplying an overly long string via the str argument, we would be able to overwrite the return address from f(). If the attacker is able to control the string, then he can control where the control flow will go after f() returns. This may occur when parsing a specially crafted file, for example.

The next thing would be to direct the control to a JMP ESP instruction or similar in effect, causing code from the stack to be executed. This "direct" code execution is prevented by DEP.

Of course, the example above doesn't include bypassing any of modern mitigations (stack canaries, SafeSEH, DEP). This does not mean it is globally impossible, but it may be impossible in certain cases. Bypassing DEP usually involves Return-oriented programming -- instead of code itself, specially crafted data is placed on the stack, which utilises fragments of already existing functions in the process image (called gadgets) to execute code. This, in turn, is mitigated by ASLR, making the gadget addresses somewhat unpredictable. There are techniques of bypassing ASLR as well.

If you would like to get your hands on exploiting real-life programs, I would suggest corelanc0d3r's tutorials as a starting point.

EDIT: Replaced the offending part.

9
  • What do you mean by "there is no such thing as an abstract vulnerability." Bringing a knife to a gunfight seems like an abstract vulnerability while bringing my Leatherman multi-tool to a gunfight against Billy the Kid at the OK Corral tomorrow at noon seems like a concrete instance of that abstract vulnerability. Jul 17, 2014 at 13:05
  • The point was that the term vulnerability is sort of useless when one learns how to exploit them. Exploitation of various overflows has almost nothing in common, therefore it was hard for me personally to treat them as instances of some abstract vulnerability. Technically they are, of course, but this doesn't really help much. Jul 17, 2014 at 13:13
  • maybe not from the black-hat side, but if defense mechanisms (like turning on ASLR or using SQL prepared statements) tend to be effective against whole classes of vulnerabilities then they are useful from the white-hat side. Jul 17, 2014 at 13:22
  • As a further example, driving a tank, or wearing Ned Kelly-style armor would seem to be defenses against a range of bringing a knife to a gunfight style vulnerabilities, even though that doesn't help a blackhat figure out how to convince the sheriff to carry a knife instead of a gun. Jul 17, 2014 at 13:26
  • Very well, so now we have two answers reflecting two different views. Yours is whitehat, mine is blackhat, if you prefer labels. Hope that benefits the OP. Jul 17, 2014 at 13:37
3

Code injection / Code execution is any attack that involves tricking a node in a distributed system into running code specified in a network message that was supposed to be treated as plain text/bytes.

The arbitrary part means that the vulnerability allows the attacker to make use of the full authority of the running process or of some privileged principle like root.


given that modern OSes implement DEP and ASLR, how is this even feasible

Code injection relies on some other kind of vulnerability to inject the payload. You're probably thinking of injection via buffer overflow but there are other ways to inject code that memory address space tricks don't help against.

Code injection doesn't necessarily mean injecting instructions in the processor's instruction set into a code/data segment. It can mean injecting source code in a scripting language which the program foolishly interprets. For example, shell injection involves injecting bytes that are passed to a shell.

Shell injection (or Command Injection[10])is named after Unix shells, but applies to most systems which allow software to programmatically execute a command line. Typical shell injection-related functions include system(), StartProcess(), and System.Diagnostics.Process.Start().


Some systems have used string tainting * to try to mitigate code injections that aren't covered by DEP/ASLR and it does help but isn't foolproof: taint bits aren't serialized when strings are serialized to files/DBs and, under maintenance, more untaint instructions are added to a program to fix high visibility non-security bugs than are removed to fix low-visibility security vulnerabilities.

The concept behind taint checking is that any variable that can be modified by an outside user (for example a variable set by a field in a web form) poses a potential security risk. If that variable is used in an expression that sets a second variable, that second variable is now also suspicious. The taint checking tool proceeds variable by variable until it has a complete list of all variables which are potentially influenced by outside input.


Systems that label strings based on information flow have also been used to secure distributed systems that have to mix strings of different provenance, but information-flow is more of an all-or-nothing approach: harder to retrofit onto systems that were not designed around it.

Information flow control protects information security by constraining how information is transmitted among objects and users of various security classes. These security classes are expressed as labels associated with the information or its containers.

Taint-checking, above, is a very simple kind of information flow analysis in which the labels are restricted to (tainted, untainted).

You must log in to answer this question.