Sheet 5

PABE Exercise Sheet 5

General Information

All solutions must be created with Python 3. If you are new to Python 3 have a look online… there are many good resources to get started such as this course, this blog post and this slide deck. Feel free to post other resources to the mailing list to help other PABE students.

Please keep in mind that you should:

  • read the task description carefully

  • push all your changes to the GitLab repository (master branch) before the deadline. Make also sure that the file permissions are set correctly! If you are new to Git check out this site!

  • make sure that your solution (also) runs on the virtual machine (and not just your local machine); this also means that you must install all additional packages yourself from within the solution script (e.g., os.system("pip install --user -r requirements.txt"))

  • make sure that the solution is an executable script named solution (chmod +x ./solution) with a working shebang line at the top (e.g., #!/usr/bin/env python3) so that it can be executed like this: ./solution (do not name your script solution.py, Solution, solution.sh, … just solution)

  • the final solution string, and only that, must be written to stdout and could be a number, a string, a string with the format FLAG{some letters and digits here}, depending on the specific task

  • describe what you are doing using detailed comments for all your solution scripts! For example, use Docstrings (link) or inline comments:

    1
    2
    3
    4
    5
    6
    7
    8
    
    def check_input_length(input_string):
        """
        The input string must have a length greater than 42 and must also be even.
        """
        length = len(input_string)
    
        # the final check happens here
        return (length > 42) and (length % 2 == 0)
    

    This helps us to find out if you really understood the task and you are not just brute-forcing some solutions. Please do not leave any commented code (i.e., code that is not needed to solve the task) in your solution files!

  • make sure that your solution executes within 10 seconds (this is a hard timeout on our server)

  • All exploit scripts must be written as Python 3 scripts using the pwntools library (from pwn import *)! Use the recvuntil("string goes here") function to keep everything in sync. Otherwise you may send data faster than the server expects it and the exploit might not work. Most of the solution files we provided also include the possibility to debug your exploit by providing the GDB parameter to your solution script, i.e. ./solution GDB. Make use of this feature to test your exploit! We suggest that you use a tmux session to debug your exploit which provides a convenient split view.

  • violating any of the points above might lead to reduced final points for the specific task!


The deadline for this sheet is Tuesday, 2021-02-02 23:59:59

Task 21 – Notebook App (2 Points)

We got a new app that allows you to take notes. There are two kinds of notes simple and printable ones. The app is still work in progress so there might still be some bugs. Try to find the bug and exploit it to print the flag. As always, describe what you are doing with technical details so that we see that you fully understood the vulnerability and the exploit.

Once again, edit the provided solution template and explain your approach with meaningful comments!

Your solution should look like:

1
2
./solution
FLAG{some letters}

Task 22 – Heap Visualization (2 Points)

Write a GDB script that visualizes parts of the heap. No pwntools required for this task. Once again, edit the provided solution template and explain your approach with meaningful comments! This solution file expect a gdb_heap_vis.py file which is your GDB script. In more detail, in GDB you can use libc’s debugging symbols to get information about the main_arena like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
(gdb) p main_arena
$1 = {
  mutex = 0,
  flags = 0,
  have_fastchunks = 0,
  fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
  top = 0x0,
  last_remainder = 0x0,
  bins = {0x0 <repeats 254 times>},
  binmap = {0, 0, 0, 0},
  next = 0x7ffff7facc40 <main_arena>,
  next_free = 0x0,
  attached_threads = 1,
  system_mem = 0,
  max_system_mem = 0
}

There you can see the fastbinsY, bins, the top chunk and so on. Implement a gdb.Command (https://sourceware.org/gdb/onlinedocs/gdb/Commands-In-Python.html#Commands-In-Python) that can be used with freeheapchunks in GDB and shows all free chunks (fast bin, small bin, unsorted bin, large bin chunks) like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
free fast bin chunks:

prev_size = ???,
size = ???,
fd = ???
bk = ???
fd_nextsize = ???
bk_nextsize = ???
data = ???

...snip...more chunks here...

free small bin chunks:

prev_size = ???,
size = ???,
fd = ???
bk = ???
fd_nextsize = ???
bk_nextsize = ???
data = ???

...snip...more chunks here...

free large bin chunks:

prev_size = ???,
size = ???,
fd = ???
bk = ???
fd_nextsize = ???
bk_nextsize = ???
data = ???

...snip...more chunks here...

free unsorted bin chunks:

prev_size = ???,
size = ???,
fd = ???
bk = ???
fd_nextsize = ???
bk_nextsize = ???
data = ???
...snip...more chunks here...

The ??? should contain the correct values preferably in hexadecimal notation. Also note: First, some of the values make no sense for specific chunk types (e.g., fastbins do not have a bk pointer) so set them to n/a for not available. Second, some bins hold linked lists with specific sizes (e.g., chunks of size 0x20, 0x30, …), so print all the free chunks in every bin of any size. The data field (which is not part of a libc heap chunk) should give a “preview” of the data that is still inside the chunk’s malloc()ed memory even if the chunk has been free()d by the user. If the data is printable print it if not render a dot (.) like in xxd.

Please use the code in malloc_and_free.c as a reference and invoke your custom command freeheapchunks at the lines where the comments tell you to do so (set the breakpoints accordingly). Also use the provided binary malloc_and_free. Here is a snippet of the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// snip

int main(int argc, char **argv)
{
	srand(time(NULL));

	noise();

	// invoke your command here

	char *string_1 = (char *)safe_malloc(32);
	strcpy(string_1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
	char *string_2 = (char *)safe_malloc(32);
	strcpy(string_2, "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
	char *string_3 = (char *)safe_malloc(128);
	strcpy(string_3, "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC");
	char *string_4 = (char *)safe_malloc(256);
	strcpy(string_4, "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
	char *string_5 = (char *)safe_malloc(512);
	strcpy(string_5, "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE");
	char *string_6 = (char *)safe_malloc(4096);
	strcpy(string_6, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
	
	// invoke your command here
	
	free(string_1);
	free(string_2);

	// invoke your command here
	
	free(string_3);
	free(string_4);
	free(string_5);
	
	// invoke your command here
	
	free(string_6);

	// invoke you command here
	
	return 0;
}
// snip

Use pure GDB not pwndbg to solve this task! You can ignore the tcache bins!

Task 23 – ROP ROP ROP ROP ROP (4 Points)

This time there are two binaries vuln and execute_me_with_rop. There is a simple vulnerability in vuln. Your task is to build a ROP chain that executes execute_me_with_rop so that it prints the flag.

Use the execve system call and prepare all necessary arguments accordingly. Do not use pwntools' pwnlib.shellcraft for this exercise! Do also not just execve("/bin/sh -c './execute_me_with_rop arg1 arg2'") for the execution of execute_me_with_rop!

Once again, edit the provided solution template and explain your approach with meaningful comments!

Hint: Try to write a C program first, that executes execute_me_with_rop to find out how to build your ROP chain:

1
2
3
4
5
6
7
8
9
#include <unistd.h>

int main()
{
  char* argv[] = { "./execute_me_with_rop" , "foo", "bar", NULL };
  char* envp[] = { "PABE=FUN",  NULL };
  execve("./execute_me_with_rop", argv, envp);
  return 0;
}

Task 24 – Bad Characters And A Non-Executable Stack (8 Points)

Task Description

Your task is to exploit a simple bug in exploitme. But there is a problem: not all characters of your shellcode can be used! Also the stack is not executable so you have to get around that as well. Your task is to:

  1. write a file shellcode.s in assembler that uses the open, read and write system calls to print the content of flag.txt to stdout
  2. create machine code of your shellcode.s file with
    1. nasm and copy out only the machine code (you cannot inject an ELF file) and objcopy magic or
    2. asm from pwntools
  3. use the mprotect system call to make the input buffer executable again to run your shellcode
  4. find out what the bad characters in your shellcode are (see next section)
  5. to get rid of the bad characters you could use msfvenom [1] from Metasploit which is installed on your VMs or craft your shellcode by hand so that it does not contain any of those bad characters (please note: you can only use msfvenom on the shellcode… not the mprotect ROP chain!)
  6. inject your bad-character-free payload into exploitme so that it will be executed and print the flag

Your input should look like this:

1
| open, read, write shellcode | .... | mprotect code that makes the input buffer executable again and jumps to the beginning of the input buffer |

Finding Bad Characters

Use the exploitme binary and try to input data that contains all possible bytes:

1
\x00\x01\x02\x03\x04...\xfe\xff

Debug the program and see if the complete input ends up in the input buffer.

Spoiler alert: \x00 terminates a string so nothing ends up in the input buffer.

So, remove that byte and try again with \x01\x02\x03\x04...\xfe\xff… maybe this time you see that all bytes up until \x0a (a newline character) end up in the buffer. Again, remove that byte and continue with \x01\x01\x02\x03\x04...\x09\x0b...\xfe\xff. When you reach \xff you just collect all bytes that somehow destroyed your shellcode during the injection (e.g., \x00, \x0a). Those bytes are your bad characters.

Solution

Once again, edit the provided solution template and explain your approach with meaningful comments! Your solution should look like this:

1
2
$ ./solution
FLAG{...}

As always, comment all of your code! You don’t have to automate the step of finding the bad characters… just describe what you did and upload your helper scripts if you created any.

[1] https://www.offensive-security.com/metasploit-unleashed/msfvenom/

Task 25 – C++ and vtables (4 Points)

In this task you have to exploit a vulnerability that leads to a vtable pointer overwrite. Although it is also possible to overwrite the return address of main() you must not use this approach.

What you should do:

  1. find the bug(s)
  2. understand how vtables work in C++ (e.g., https://en.wikipedia.org/wiki/Virtual_method_table, https://godbolt.org/z/les9Bx)
  3. exploit the bug to get the flag from flag.txt by manipulating the vtable (pointer)

Once againe, edit the provided solution template and explain your approach with meaningful comments!

The output should look like:

1
2
$ ./solution
FLAG{...}

Hints:

  • are there any useful functions in the binary?
  • how can you manipulate the control flow without overwriting the return address?