# DragonCTF 2018 Pwn Challenges Writeup

Yup, I know this is a late writeup... Just.. take it as a record of myself.

## FastStorage

As a record, I'm not going to completely analysis the whole challenge.

This challenge is about a quite strange problem of libc abs function, that when argument of this function is 0x80000000, which is a int with no minus counterpart, it will return itself. This is to say abs(0x80000000) == 0x80000000.

### Logic

Logic of this challenge is not so trivial, but not complicated. Three operations valid:

1. add_entry
2. print_entry
3. edit_entry

Note that there's no delete_entry or something like that, means there will be no free available.

Two global variabls used, one g_slot_bitmap and one g_slot, which are of int array and pointer array respectively. slot maps are int arrays storing bitmap of struct of particular sizes, and slots are obviously the struct to be allocated out. When doing add_entry, this function is used:

slot_id is the bitmap, I used a wrong name and decided to let it go. When add an entry, slot is assigned to the global pointer array. slots are structure:

Sizes are recorded in the SlotItem pointer itself using left-shifted 48 value, ORing it with the pointer and slot bitmap is operated by this function:

And as I mentioned, g_slot_ident is the global bitmap.

Once we know the abs is the problem causer, we know that since the function do_add_entry assumes abs should always be more than or equal to 0, it doens't consider anything about 0x80000000. So we give it a 0x80000000, the slotnum will become -2, which will cause the assign_slot to assign slot to a minus index, thus overlap with bitmap global variable.

### Strategy

Knowing the problem, we can decide a way to solve this challenge. Challenge is nearly fully protected, NX, PIE and FULL RELRO, making us impossible to operate on binary image itself. Just as any pwnable challenge would do, we should leak something first.

There is a check function when doing edit, to check if the requesting item is truly there, so we can use it, since the problem let us assigned a SlotItem pointer to slot_id array(bitmap array), thus we can check every bit of the SlotItem pointer.

Then we need to find out how to get every bit pointer, we need data which is sufficient to those conditions. Z3 could help us out.

With this script, I found out "initial name" which could give us the 0x80000000 value, and all checking value to be used to check every bit of a SlotItem. Thus, we get a heap leak.

After a heap leak, we still need libc leak to give us more power. I just mentioned that there's no free anywhere in this binary, so we have to figure out a way to free some small chunk to get us a libc pointer. This actually is not so difficult for pwnable players, we usually use malloc that a top chunk cannot handle to free a top chunk, this can be done by modifing size of the top chunk.

But do note that I got into trouble when doing this with this assertion in libc..

I got several segmentation faults, and when do detailed debugging, I found out the reason I got aborted all the time is that I have to make the size modified to pass the page edge check. The topchunk size with addition to its current position should be at the end of a page. Once passed, I got a libc leak.

With those leaks, and ability to modify any bit of a heap pointer, things are getting easy. Just do set some bits, make it point to somewhere we control, put a fake struct there, and we get arbitrary write.

The rest are just trivial things to do, so I'll skip it.

Oh, forgot to mention, I'm really falling in love with lisp lately, and thus I used hy to write my exploit. :) You can also try hylang, with the power of lisp along with Python, nothing could stop us!

## Production

This is a good challenge, but due to my stupidness, I failed to solve this during the game..

### Intro

A source file is given (It's a little bit long, but for record, I put it here still):

A basic leaky sandbox is implemented, but no other logic to protect it. The concatenation using snprintf will make it possible to jump to one level up of sandboxed directory, thus give us the ability to readout the flag.

This easy? Of course not! The thing is, it stopped you from reading flag by closing it when "flag" is detected after symblink check, but if it is a symlink, it will be jumped over. And some several ensurence, like no flag prologue can be readout, and the resources are limited.

This is actually a strange point that the resources are limited, since no one can reach the top of the resource limit when you firstly take a look. It actually can't reach the limit with that global variable size check, which ensures we can't have more than 16 files opened, and the resource limits fd opened as 32. And other resouces, I really don't think they are useful.

If you think about it, you'll find out that when you reach the fd limit somehow, the second open will fail no matter if the file is a symlink, thus jump over the flag check. This is the key part, but how to reach the limit?

### assertions, твой мать!

This is what I missed during the CTF...

The name of this challenge is production..Which means the binary will be productionally compiled, release compiled, which means.. THE ASSERTIONS ARE ALL GONE!

Without assertion, close record when flag prologue detected WILL NOT CLOSE the fd.. Thus we can reach the limit..

The rest things are simple enough... Once we reach the fd limit, the second open will fail, we will be back without "flag" path check, to bypass the prologue check when reading flag, be noticed there's a uninitialized variable bug there. So we can simply read flag out, and let the second read returns 0, so we can dump out the read content..