↪ checksec --file=hacknote RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH No Symbols No 0 2 hacknote
Take a look at Add_note function, you can see that a variable at 0x804a4c (data section) used to make sure that you just get to use add_note option 6 times by checking whether it’s less than 6 or not at the beginning and increasing it by 1 after adding a new note. But see inside the if condition, they just allow us to use add_note option 5 times. The reason is that they use a 5-variables array, stored at 0x804a050, and when you choose the option 1, they will check whether are there any available slot, if not they do nothing. When you add a new note, they will create a chunk of 8 bytes and a chunk of your chosen size. The first chunk will be stored at the array i mentioned above, and used as a struct(the first element stores a funtion (sub_804862B), the second one stores the address of the second chunk).
1 2 3 4
int __cdecl sub_804862B(int a1) { returnputs(*(_DWORD *)(a1 + 4)); }
Let’s see the Print_note, they will call the function stored at the the first chunk (it’s the function above), and pass its address to this function too. Finally, they will print everything at the address stored at the second element. Next, the Delete_note function, they just free the second chunk, then the first one, but they don’t assign the element of the array back to null or 0, that’s why I tell you that, you just get to use add_note functions 5 times.
0x4. Exploit
When you free a chunk, if its size is greater than 80 bytes and isn’t adjacent to the top chunk (to do this just add another note in the middle), it will be put in unsorted bin (doubly linked-list) and if there is just only one chunk in unsorted bin, its previous and next pointer would point to the address of main arena in libc (the manager of bins). And when you add a new note having exactly the same size, they will give you the chunk from unsorted bin but don’t reset this chunk, that means you can get the address of main arena and calculate the libc_base address, system’s address, etc…
As I analysed above, when adding a new note, they create a chunk of 8 bytes containing the function that print out our note, I will call it the special chunk . As a result, if we delete 2 notes and add a new note of 8 bytes, we will get a special chunk of the notes we just deleted and get to overwrite them and run our desired function, in that case will be system function. Because they pass the address of the special chunk to my function, we can write the system address and “;sh;” to the special chunk. So that, when we choose to print note, the system function will run the address of system (that’s should be error) and then the command “sh”, next will run the command right after ‘;’.
p.recvuntil (b'A' * 4) main_arena = u32 (p.recv (4)) libc_base = main_arena - 0x1B07B0 print ("The address of main_arena is : ", hex (main_arena)) print ("The address of libc base is : ", hex (libc_base))