Why Does This Code Crash with Address Randomization On

Why does this code crash with address randomization on?

The red zone in amd64 is only 128 bytes long, but you're using 131072 bytes below rsp. Move the stack pointer down to encompass the buffers that you want to store on the stack.

what is Address space layout randomization

ASLR is a technique designed to make various types of buffer overruns more difficult to exploit, by moving segments around a bit. The stack could be shifted a few bytes (or pages), the sections of your program (and even the libraries your code uses) can be loaded at different addresses, etc.

Buffer overflows usually work by tricking the CPU into running code at a certain address (often on the stack). ASLR complicates that by making the address harder to predict, since it can change each and every time the program runs. So often, instead of running arbitrary code, the program will just crash. This is obviously a bad thing, but not as bad as if some random joker were allowed to take control of your server.

A very simple, crude form of ASLR can actually be implemented without any help from the OS, by simply subtracting some small amount from the stack pointer. (It's a little tricky to do in higher-level languages, but somewhat simpler in C -- and downright trivial in ASM.) That'll only protect against overflows that use the stack, though. The OS is more helpful; it can change all sorts of stuff if it feels like. It depends on your OS as to how much it does, though.

What causes this bug to be non-deterministic

Undefined behavior means that anything can happen, even inconsistent results.

In practice, this inconsistency is most likely due to Address Space Layout Randomization. Depending on how the data is located in memory, the out-of-bounds accesses may or may not access unallocated memory or overwrite a critical pointer.

See also Why don't I get a segmentation fault when I write beyond the end of an array?

overcome stack randomization by brute force

Stack Randomization allocats a random amount of space between 0 and n bytes on the stack at the start of a program

No, it doesn't allocate. It randomizes where in virtual address space the stack is mapped, leaving other pages unmapped. This is with page granularity.

Guessing wrong will typically result in the victim program segfaulting (or whatever the OS does on an invalid page fault). This is noisy and obvious to any intrusion-detection. And if the program does eventually restart so you can try again, its stack address will be re-randomized, as you suggest.

Wrong guesses that land in valid memory but not your NOP sled will also typically crash soon, on an invalid instruction or something that decodes to an invalid memory access.

So yes, you're right, you can't just enumerate the address space, it's only a probabilistic thing. Trying enough times will mean it's likely you succeed, not guaranteed. And trying to enumerate the possible ASLR entropy isn't particularly useful, or any more likely to succeed sooner than guessing the same page every time I think.

But trying different offsets within a single page is useful because OS-driven stack ASLR only has page granularity.

There is some amount of stack space used by env vars and command line args which will vary from system to system, but tend to be constant across invocations of the same program, I think. Unless the vulnerable function is reached from different call chains, or there's a variable-sized stack allocation in a parent function, in which case the buffer's offset within page could vary every run.


Although of course most processes these days run with non-executable stacks, making direct code-injection impossible for stack buffers. A ROP attack (injecting a return address to an existing sequence of bytes that decode to something interesting) is possible if there are any known static data or code addresses. If not, you have to deal with ASLR of code/data, but you don't get to inject NOP sleds.

Why does the address of a function change with every run?

Address Space Layout Randomization

Address space layout randomization and structures in C

The specific overflow you mentioned is still possible.

With the exception of bitfields, the fields of a structure follow one another in order in memory (with some possible padding in between). This is detailed in section 6.7.2.1p15 of the C standard:

Within a structure object, the non-bit-field members and the
units in which bit-fields reside have addresses that increase in
the order in which they are declared.
A pointer to a structure
object, suitably converted, points to its initial member (or
if that member is a bit-field, then to the unit in which it
resides), and vice versa. There may be unnamed padding within
a structure object, but not at its beginning.

So in this case the author field will always follow the title field, regardless of what specific address an object of type struct Books is located at. The only possible difference could be the amount of padding, but unless you add or remove fields in the struct this probably won't change.

Why does the address of a local variable vary when executing multiple times, but not when debugging it with GDB?

The reason you always get the same address for local variables while running under GDB is that GDB (in order to simplify most debugging scenarios) disables address space randomization.

You can ask GDB to not do that with set disable-address-randomization off.

For curious, disabling of address randomization for the current process does not require any privilege, and is done by calling personality(2). Here is the patch that added this feature.

What are the addresses in core files?

Does it mean my program crashes at 0x001cd1a6

Yes.

There is no executable code at that address.

Well, that would certainly cause a crash (due to illegal instruction).

Another thing is it gives a different address every time it crashes.

Your program has threads, so its allocation pattern is likely different every time it runs, as threads are scheduled differently.

Also, Linux uses address randomization, so if you run even non-threaded program multiple times, you'll end up with different addresses. On the other hand, GDB disables that randomization, so if you run non-threaded program under GDB, it should crash in the same place every time.

You are likely calling a virtual function on an object that has been invalidated (e.g. deleted). Use where GDB command to find out how you end up on invalid address.

Also, don't ever call your executable test on UNIX: this conflicts with /usr/bin/test, which many shell scripts will use.



Related Topics



Leave a reply



Submit