0

Short version:

  0:    48 c7 c7 ee 4f 37 45 mov    $0x45374fee, %rdi
  7:    68 60 18 40 00       pushq  $0x401860
  c:    c3                   retq

How can these 3 lines of instruction(0,7,c), saved in the stack frame, get executed? I thought stack frame only store data, does it also store instructions? I know data is read to registers, but how do these instructions get executed?

Long version:

I am self-studying 15-213(Computer Systems) from CMU. In the Attack lab, there is an instance (phase 2) where the stack frame gets overwritten with "attack" instructions. The attack happens by then overwriting the return address from the calling function getbuf() with the address %rsp points to, which I know is the top of the stack frame. In this case, the top of the stack frame is in turn injected with the attack code mentioned above.

Here is the question, by reading the book(CSAPP), I get the sense that the stack frame only stores data the is overflown from the registers(including return address, extra arguments, etc.). But I don't get why it can also store instructions(attack code) and be executed. How exactly did the content in the stack frame, which %rsp points to, get executed? I also know that %rsp stores the return address of the calling function, the point being it is an address, not an instruction? So exactly by which mechanism does an supposed address get executed as an instruction? I am very confused.

Edit: Here is a link to the question(4.2 level 2): http://csapp.cs.cmu.edu/3e/attacklab.pdf This is a post that is helpful for me in understanding: https://github.com/magna25/Attack-Lab/blob/master/Phase%202.md

Thanks for your explanation!

sociala
  • 11
  • 3
  • 1
    Welcome to SO. Do you expect us to know what "the book (CSAPP)" might be? – Gerhardh Oct 17 '22 at 07:16
  • 1
    In your opinion, what is the difference between an instruction and a data value in memory? And what is the difference between returning from a function to the real caller or returning to a modified address that lies within the stack? – Gerhardh Oct 17 '22 at 07:18
  • "I know data is read to registers," On the stack there are no registers. That is just memory, no matter what you put there. Instructions (no matter where they are stored) are executed by jumping to the address of theaw instructions. "Jumping" can also mean "return" to that address via a modified return address on the stack – Gerhardh Oct 17 '22 at 08:27
  • The questions in my comment above aren't rethoric. Think about them and try to answer them. Things should be clearer afterwards – Gerhardh Oct 17 '22 at 08:28
  • 1
    @Gerhardh: https://csapp.cs.cmu.edu/ is Computer Systems: A Programmer's Perspective, a computer architecture and assembly textbook using x86-64 assembly and compiler output from GCC. It's fairly good (except for the global edition, where the practice problems were replaced with crap by the publisher without the author's consent.) We get quite a few questions based on it on SO. But yes, a good question should have named the textbook. – Peter Cordes Oct 17 '22 at 10:29
  • The stack frame *doesn't* normally store executable code, just return addresses. But if has exec permission, then you can store machine code there, e.g. via a buffer overflow which also overwrites a return address, so when a function returns it'll jump to that code. That's a code-injection attack. Normally the kernel maps stack memory without exec permission, but GCC's implementation of nested function requires it so it can write "trampolines", little wrappers that do stuff before jumping: [Implementation of nested functions](https://stackoverflow.com/q/8179521) – Peter Cordes Oct 17 '22 at 10:42
  • Also [Is it practical to create a C language addon for anonymous functions?](https://stackoverflow.com/q/40553846) is another answer about the same thing. Also related: [Exactly what cases does the gcc execstack flag allow and how does it enforce it?](https://stackoverflow.com/q/53346274) – Peter Cordes Oct 17 '22 at 10:42
  • Your example of code being injected is a bit weird, because it also uses the stack and uses push/ret to jump to existing code in the program, instead of making a system call itself. If existing code in the program can do what you need, usually you'd *just* inject some return addresses. (After finding some conveniently placed instructions before `c3 ret` bytes, aka "gadgets" for a ROP (Return Oriented Programming) attack). – Peter Cordes Oct 17 '22 at 10:47
  • Hi,@Peter Cordes, @Gerhardh, here is the link to the question(phase 2):http://csapp.cs.cmu.edu/3e/attacklab.pdf. You will get a sense of what I mean. I believe this is not a straightforward injection. If you read my post carefully, you will know it modifies return address of calling func to `%rsp`, i.e. the top of the stack frame. – sociala Oct 17 '22 at 16:16
  • @Gerhardh, I am aware stack frame is located in memory, I am simply saying, address stored from stack frame(in memeory) is read to the registers(in CPU). – sociala Oct 17 '22 at 16:20
  • Ah, right, they start out injecting code to pass an arg to an existing function in the executable, since a couple steps later in the assignment they do get to return-oriented programming like I mentioned, getting the result without injecting new code, just some return addresses and values. So step 2 and 3 were along the road to a ROP attack, getting you thinking in that direction, instead of just injecting code to do `execve("/bin/sh", NULL, NULL)` like you'd typically do (that's why it's called "shellcode"; many exploitable programs had their stdin connected to a network socket.) – Peter Cordes Oct 17 '22 at 17:08
  • BTW, `mov $0x401860, %eax` / `jmp *%rax` would work exactly like `push`/`ret`; it's "difficult to formulate" an encoding only for a relative jump. But push/ret is shorter by 1 byte. – Peter Cordes Oct 17 '22 at 17:10
  • @PeterCordes , what you wrote is not familiar to me. So far I have understood the mechanism by which the injection is done in step 2, i.e. by changing the return address of the calling func to where %rsp points to, which has been overwritten with injection code(feed in a arg and call tochch2()). This results in the code being loaded to the register. But as said many times before, I thought stack frame can only store data or addresses, not instructions? Am I incorrect? I f it can store instructions. How can CPU tell them apart from just data/addresses? Am i missing something? – sociala Oct 17 '22 at 17:23
  • You are incorrect. x86 is a von Neumann architecture, machine code *is* just bytes of data that you can load and store. RIP can point anywhere, including to the same address as RSP. The stack isn't special. (Although in a normal build, the stack would be mapped to non-executable pages. For this attack lab, clearly their binary was compiled with `gcc -z execstack` to change that.) – Peter Cordes Oct 17 '22 at 17:28

1 Answers1

3

ret instruction gets a pointer from the current position of the stack and jumps to it. If, while in a function, you modify the stack to point to another function or piece of code that could be used maliciously, the code can return to it.

The code below doesn't necessarily compile, and it is just meant to represent the concept.

For example, we have two functions: add(), and badcode():

int add(int a, int b)
{
    return a + b;
}

void badcode()
{
    // Some very bad code
}

Let's also assume that we have a stack such as the below when we call add()

...
0x00....18 extra arguments
0x00....10 return address
0x00....08 saved RBP
0x00....00 local variables and etc.
...

If during the execution of add, we managed to change the return address to address of badcode(), on ret instruction we will automatically start executing badcode(). I don't know if this answer your question.

Edit:

An instruction is simply an array of numbers. Where you store them is irrelevant (mostly) to their execution. A stack is essentially an abstract data structure, it is not a special place in RAM. If your OS doesn't mark the stack as non-executable, there is nothing stopping the code on the stack from being returned to by the ret.


Edit 2:

I get the sense that the stack frame only stores data that is overflown from the registers(including return address, extra arguments, etc.)

I do not think that you know how registers, RAM, stack, and programs are incorporated. The sense that stack frame only stores data that is overflown is incorrect.

Let's start over.

Registers are pieces of memory on your CPU. They are independent of RAM. There are mainly 8 registers on a CPU. a, c, d, b, si, di, sp, and bp. a is for accumulator and it generally used for arithmetic operations, likewise b stands for base, c stands for counter, d stands for data, si stands for source, di stands for destination, sp is the stack pointer, and bp is the base pointer.

On 16 bit computers a, b, c, d, si, di, sp, and bp are 16 bits (2 byte). The a, b, c, and d are often shown as ax, bx, cx, and dx where the x stands for extension from their original 8 bit versions. They can also be referred to as eax, ecx, edx, ebx, esi, edi, esp, ebp for 32 bit (e again stands for extended) and rax, rcx, rdx, rbx, rsi, rdi, rsp, rbp for 64 bit.

Once again these are on your CPU and are independent of RAM. CPU uses these registers to do everything that it does. You wanna add two numbers? put one of them inside ax and another one inside cx and add them.

You also have RAM. RAM (standing for Random Access Memory) is a storage device that allows you to access and modify all of its values using equal computation power or time (hence the term random access). Each value that RAM holds also has an address that determines where on the RAM this value is. CPU can use numbers and treat such numbers as addresses to access memory addresses of RAM. Numbers that are used for such purposes are called pointers.

A stack is an abstract data structure. It has a FILO (first in last out) structure which means that to access the first datum that you have stored you have to access all of the other data. To manipulate the stack CPU provides us with sp which holds the pointer to the current position of the stack, and bp which holds the top of the stack. The position that bp holds is called the top of the stack because the stack usually grows downwards meaning that if we start a stack from the memory address 0x100 and store 4 bytes in it, sp will now be at the memory address 0x100 - 4 = 0x9C. To do such operations automatically we have the push and pop instructions. In that sense a stack could be used to store any type of data regardless of the data's relation to registers are programs.

Programs are pieces of structured code that are placed on the RAM by the operating system. The operating system reads program headers and relevant information and sets up an environment for the program to run on. For each program a stack is set up, usually, some space for the heap is given, and instructions (which are the building blocks of a program) are placed in arbitrary memory locations that are either predetermined by the program itself or automatically given by the OS.

Over the years some conventions have been set to standardize CPUs. For example, on most CPU's ret instruction receives the system pointer size amount of data from the stack and jumps to it. Jumping means executing code at a particular RAM address. This is only a convention and has no relation to being overflown from registers and etc. For that reason when a function is called firstly the return address (or the current address in the program at the time of execution) is pushed onto the stack so that it could be retrieved later by ret. Local variables are also stored in the stack, along with arguments if a function has more than 6(?).

Does this help?

I know it is a long read but I couldn't be sure on what you know and what you don't know.


Yet Another Edit:

Lets also take a look at the code from the PDF:

void test()
{
    int val;
    val = getbuf();
    printf("No exploit. Getbuf returned 0x%x\n", val);
}

Phase 2 involves injecting a small amount of code as part of your exploit string. Within the file ctarget there is code for a function touch2 having the following C representation:

void touch2(unsigned val)
{
    vlevel = 2; /* Part of validation protocol */
    if (val == cookie) {
        printf("Touch2!: You called touch2(0x%.8x)\n", val);
        validate(2);
    } else {
        printf("Misfire: You called touch2(0x%.8x)\n", val);
        fail(2);
    }
    exit(0);
}

Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case, however, you must make it appear to touch2 as if you have passed your cookie as its argument.


Let's think about what you need to do:

You need to modify the stack of test() so that two things happen. The first thing is that you do not return to test() but you rather return to touch2. The other thing you need to do is give touch2 an argument which is your cookie. Since you are giving only one argument you don't need to modify the stack for the argument at all. The first argument is stored on rdi as a part of x86_64 calling convention.

The final code that you write has to change the return address to touch2()'s address and also call mov rdi, cookie


Edit:

I before talked about RAM being able to store data on addresses and CPU being able to interact with them. There is a secret register on your CPU that you are not able to reach from you assembly code. This register is called ip/eip/rip. It stands for instruction pointer. This register holds a 16/32/64 bit pointer to an address on RAM. this particular address is the address that the CPU will execute in its clock cycle. With that in my we can say that what a ret instruction is doing is

pop rip

which means get the last 64 bits (8 bytes for a pointer) on the stack into this instruction pointer. Once rip is set to this value, the CPU begins executing this code. The CPU doesn't do any checks on rip whatsoever. You can technically do the following thing (excuse me, my assembly is in intel syntax):


mov rax, str ; move the RAM address of "str" into rax
push rax ; push rax into stack
ret ; return to the last pushed qword (8 bytes) on the stack

str: db "Hello, world!", 0 ; define a string

This code can call/execute a string. Your CPU will be very upset tho, that there is no valid instruction there and will probably stop working.

  • I understand what you wrote, but this is a different scenario. If you read it closely, I am asking how exactly is the INSTRUCTION that was injected into the stack frame was executed. In your case the stack frame stores the ADDRESS of a function. – sociala Oct 17 '22 at 08:10
  • @sociala The CPU will not see any difference between code that was created from a C function and code that was injected into the stack. If you return to that address, the values in memory are exectuted as instructions. – Gerhardh Oct 17 '22 at 08:14
  • @sociala added an edit. – Özgür Güzeldereli Oct 17 '22 at 08:28
  • Hi,@ÖzgürGüzeldereli, @Gerhardh, here is the link to the question(phase 2):http://csapp.cs.cmu.edu/3e/attacklab.pdf. You will get a sense of what I mean. – sociala Oct 17 '22 at 16:16
  • @sociala Added an edit. Also which page specifically are you talking about on this PDF? – Özgür Güzeldereli Oct 17 '22 at 17:00
  • @ÖzgürGüzeldereli 4.2 Level 2. This post is helpful in understanding: https://github.com/magna25/Attack-Lab/blob/master/Phase%202.md – sociala Oct 17 '22 at 17:04
  • @ÖzgürGüzeldereli also I have looked over the book one more time, i believe %rsp points to the top of the stack, not %rbp? Will the content in stack frame be interpreted in CPU as just data rather instructions? – sociala Oct 17 '22 at 17:16
  • @sociala yes you are correct made a typo. There is no "interpretation" done by the CPU. CPU will execute everything it sees. There is nothing as "just data". If you were to do a jump to the address of a string, CPU will try to execute it regardless, and will probably give you an error. – Özgür Güzeldereli Oct 17 '22 at 17:20
  • How can CPU tell them differently? Am I missing something? – sociala Oct 17 '22 at 17:24
  • @sociala there is no telling them differently. I repeat: you can execute a string. The CPU does no checks, no interpretations, no nothing unless you specify it to do so. I will add an edit on how instructions are stored and executed. – Özgür Güzeldereli Oct 17 '22 at 17:27
  • @sociala added another edit. – Özgür Güzeldereli Oct 17 '22 at 17:34
  • You're confusing the uses of `bp` and `sp` eg in this part: "To manipulate the stack CPU provides us with `bp` which holds the pointer to the current position of the stack, and `sp` which holds the top of the stack." Besides, the stack is not purely LIFO because you can do some random-ish access to data located farther into the stack structure, using addressing directly with `esp` or `rsp` (or `bp`/`ebp`/`rbp`, the first of which works all the way down to the 8086, or copy the stack pointer into another register that's usable as a pointer). Different problem: the RWX permissions of the stack. – ecm Oct 17 '22 at 18:22
  • @ecm Yea sorry I thought I had fixed those, they were mentioned before. Also technically a stack is purely LIFO but since you can use ```sp``` and ```bp``` as normal pointers you can also do other non-stack related memory operations with them. I am saying stack is purely LIFO because that is the definition of a stack. – Özgür Güzeldereli Oct 17 '22 at 18:43