This challenge was using cling, which is an “interactive C++ Interpreter”. We were provided the source of the code, that will be passed to the interpreter.
Options 6-8 were not implemented, so let’s take a quick look at the other options.
Create will just pretty much mmap a region with read/write permissions and stores a pointer to it in a global buf variable.
protect lets us define the permissions on the mmapped region. Answering all questions with yes will mark it as rwx.
del will munmap our current region, without clearing the global buf pointer. So the address from this will still be available. Normally, this would not be of much use, since that memory region becomes unmapped. But let’s keep this in mind.
With set_map things get interesting. This will let us enter a map function, which will then be evaluated by the interpreter.
For this cling will kinda do a jit compilation, creating a mmapped region, put the code for this function on it and execute it (marking the region as r-x).
run_map will now just take some user input and pass it to the map function, we defined in set_map.
So, let’s recap this:
We can create a mmapped region via create
We can set the permissions for this region via protect
We can unmap that region (but keep a pointer to it) via del
We can create a custom function, which will mmap a region and put code to it via set_map
We can execute this custom function (executing the code from the mmapped region) via run_map
Also mmapping a region, unmapping and mmapping it again will serve the same region
See, where this is going? :)
If not, the trick is just to create a mmapped region, fill it up with random values and munmap it. This region will now be gone, but we’ll still have its address in buf.
Now we can call set_map and define a mapping function. Again, it’s content isn’t important, just put something in it, which will compile. This will mmap again a region, put the code for the map function in it and executes it once (and since we created and unmapped a region before, this code will be put into the exact same region which we have in our buf).
Because the region from buf is now mapped again, we can… delete it again… (the map function from set_map will also now point to the same unmapped region).
So, let’s just create again. This will now again serve us the same region as the first create, which is also the same region the map function is pointing to :)
We can now just write proper shellcode to it and then execute the map function via run_map. Since the map function is also pointing to the region, we just defined in create this will then execute our shellcode instead of the previous map function.
The map function will jump to region + 0xa0, so we just have to append some dummy values in the beginning and then write our shellcode to offset 0xa0 in the region
run_map will now jump right into our shellcode giving us a shell