This attack works against systems with non-executable stacks, too. Instead of the standard exploit shown above, the attacker overwrites the functions stack with data that looks like this:
where addr1 is the address of the system() function. Note that this works even if the program you attack does not use system() at all; simply by mapping the libc shared object file, the library function is within the attacked process' address space.