One of the simplest buffer overflow attacks is that on the stack, overwriting the return address to be the address of shellcode and using shellcode (and sometimes NOP sleds) to spawn a shell. NX defends against this, making the stack, heap and other places in memory non-executable. Generally, it's very hard to bypass this, however it is possible using a function called mprotect. Mprotect allows you to change the protections of certain areas in memory. Under specific circumstances, like if we had the ability to control pages or knew the address of the stack, would allow an attacker to use mprotect to make the stack executable again, and execute a classic buffer overflow attack.