[issue31897] Unexpected exceptions in plistlib.loads

2017-10-30 Thread Ned Williamson

Ned Williamson <nedwilliam...@gmail.com> added the comment:

Thank you for the quick PR! I will report as behavior next time. I'm also 
following the library reference and reporting only unexpected exceptions.

I trust you to reject any bugs that are expected functionality.

I may follow up with additional testcases once the first PR is accepted.

--

___
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31897>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue31897] RecursionError in plistlib.loads

2017-10-29 Thread Ned Williamson

Ned Williamson <nedwilliam...@gmail.com> added the comment:

```
import plistlib
dat = 
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00AAAnAAA'
plistlib.loads(dat, fmt=plistlib.FMT_BINARY)
```
raises
```
Traceback (most recent call last):
  File "repro.py", line 3, in 
plistlib.loads(dat, fmt=plistlib.FMT_BINARY)
  File "/usr/lib/python3.5/plistlib.py", line 1006, in loads
fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type)
  File "/usr/lib/python3.5/plistlib.py", line 997, in load
return p.parse(fp)
  File "/usr/lib/python3.5/plistlib.py", line 622, in parse
self._object_offsets = self._read_ints(num_objects, offset_size)
  File "/usr/lib/python3.5/plistlib.py", line 644, in _read_ints
for i in range(0, size * n, size))
ValueError: range() arg 3 must not be zero
```

--

___
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31897>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue31897] RecursionError in plistlib.loads

2017-10-29 Thread Ned Williamson

Ned Williamson <nedwilliam...@gmail.com> added the comment:

```
import plistlib
dat = 
b'AAAwAAA\xc9A\x9cAA\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00A\x04\xb2\xaaAA'
plistlib.loads(dat, fmt=plistlib.FMT_BINARY)
```
raises
```
Traceback (most recent call last):
  File "repro.py", line 3, in 
plistlib.loads(dat, fmt=plistlib.FMT_BINARY)
  File "/usr/lib/python3.5/plistlib.py", line 1006, in loads
fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type)
  File "/usr/lib/python3.5/plistlib.py", line 997, in load
return p.parse(fp)
  File "/usr/lib/python3.5/plistlib.py", line 621, in parse
self._fp.seek(offset_table_offset)
OverflowError: Python int too large to convert to C ssize_t
```

--

___
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31897>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue31897] RecursionError in plistlib.loads

2017-10-29 Thread Ned Williamson

Ned Williamson <nedwilliam...@gmail.com> added the comment:

I'm filing related bugs under this same issue.

```
import plistlib
dat = 
b'Q\xe4\xfeAIwAAA\xc9A\xc1AAA\xc1AAA\x9cAAnAAA\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00AA'
plistlib.loads(dat, fmt=plistlib.FMT_BINARY)
```
raises

```
Traceback (most recent call last):
  File "repro.py", line 3, in 
plistlib.loads(dat, fmt=plistlib.FMT_BINARY)
  File "/usr/lib/python3.5/plistlib.py", line 1006, in loads
fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type)
  File "/usr/lib/python3.5/plistlib.py", line 997, in load
return p.parse(fp)
  File "/usr/lib/python3.5/plistlib.py", line 623, in parse
return self._read_object(self._object_offsets[top_object])
  File "/usr/lib/python3.5/plistlib.py", line 699, in _read_object
result =  self._fp.read(s).decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal 
not in range(128)
```

It seems only `InvalidFileException` should be raised by this function.

--

___
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31897>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue31897] RecursionError in plistlib.loads

2017-10-29 Thread Ned Williamson

Ned Williamson <nedwilliam...@gmail.com> added the comment:

The crashing version numbers are from testing on the release Python 3.5, but I 
think we can just fix this in 3.7+.

--

___
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31897>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue31897] RecursionError in plistlib.loads

2017-10-29 Thread Ned Williamson

New submission from Ned Williamson <nedwilliam...@gmail.com>:

Hi,

The following program crashes for me using the current Python3.7 master:

```
import plistlib
plistlib.loads(b'\xdd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
   b'\xda\x0cw\xb7\x00\x00\x00\x00\x00\x00\x00\xc7\x00'
   b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd6\xd5\x00'
   b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00'
   b'\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00',
   fmt=plistlib.FMT_BINARY)
```

The last few lines look like
```
  File "/usr/lib/python3.5/plistlib.py", line 728, in _read_object
] = self._read_object(self._object_offsets[o])
  File "/usr/lib/python3.5/plistlib.py", line 728, in _read_object
] = self._read_object(self._object_offsets[o])
  File "/usr/lib/python3.5/plistlib.py", line 723, in _read_object
key_refs = self._read_refs(s)
  File "/usr/lib/python3.5/plistlib.py", line 647, in _read_refs
return self._read_ints(n, self._ref_size)
  File "/usr/lib/python3.5/plistlib.py", line 644, in _read_ints
for i in range(0, size * n, size))
RecursionError: maximum recursion depth exceeded in comparison
```

This bug was found using an alpha version of python-fuzz.

--
messages: 305205
nosy: Ned Williamson
priority: normal
severity: normal
status: open
title: RecursionError in plistlib.loads
type: crash
versions: Python 3.7

___
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31897>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue30828] Out of bounds write in _asyncio_Future_remove_done_callback

2017-07-02 Thread Ned Williamson

New submission from Ned Williamson:

This is very similar to the issue reported in 
https://bugs.python.org/issue28963 - this function is still buggy when items 
are pushed onto the done callbacks, as the new list is assumed to be large 
enough.

The issue was pointed out on the code review page here: 
http://www.psf.upfronthosting.co.za/review/28963/ but it seems it was missed.

```
import asyncio

fut = asyncio.Future()
fut.add_done_callback(str)

for _ in range(63):
fut.add_done_callback(id)

class evil:
def __eq__(self, other):
fut.add_done_callback(id)
return False

fut.remove_done_callback(evil())
```

This testcase leads to a crash due to the out of bounds access:

```
ASAN:DEADLYSIGNAL
=
==21457==ERROR: AddressSanitizer: SEGV on unknown address 0x00010f99fc90 (pc 
0x00010f99fc90 bp 0x7fff53c18f70 sp 0x7fff53c18eb8 T0)
#0 0x10f99fc8f  ()
#1 0x10c2675cb in _PyEval_EvalFrameDefault ceval.c:2330
#2 0x10c03f19f in function_code_fastcall call.c:282
#3 0x10c03cf2f in _PyFunction_FastCallDict call.c:328
#4 0x10c040acf in _PyObject_FastCall_Prepend call.c:844
#5 0x10c12de9c in slot_tp_richcompare typeobject.c:1473
#6 0x10c0edc46 in PyObject_RichCompare object.c:671
#7 0x10c0ee101 in PyObject_RichCompareBool object.c:741
#8 0x1100dc593 in _asyncio_Future_remove_done_callback _asynciomodule.c:531
#9 0x10c0403e3 in _PyMethodDef_RawFastCallKeywords call.c:630
#10 0x10c04f659 in _PyMethodDescr_FastCallKeywords descrobject.c:287
#11 0x10c27f77f in call_function ceval.c:4854
#12 0x10c26b0e8 in _PyEval_EvalFrameDefault ceval.c:3336
#13 0x10c281b6b in _PyEval_EvalCodeWithName ceval.c:678
#14 0x10c264603 in PyEval_EvalCode ceval.c:4221
#15 0x10c3175f7 in PyRun_FileExFlags pythonrun.c:982
#16 0x10c315cc0 in PyRun_SimpleFileExFlags pythonrun.c:398
#17 0x10c3622b5 in Py_Main main.c:341
#18 0x10bfe6a60 in main python.c:102
#19 0x7fff96740234 in start (libdyld.dylib+0x5234)
```

In order to fix this, I recommend just banning returning an error when the user 
attempts to append to the done callbacks while removing one. See the attached 
patch (may need fixing for style/consistency with error messages).

--
components: asyncio
files: fix.patch
keywords: patch
messages: 297513
nosy: Ned Williamson, yselivanov
priority: normal
severity: normal
status: open
title: Out of bounds write in _asyncio_Future_remove_done_callback
type: crash
versions: Python 3.7
Added file: http://bugs.python.org/file46987/fix.patch

___
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue30828>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue28963] Use-after-free in _asynciomodule.c

2016-12-13 Thread Ned Williamson

Ned Williamson added the comment:

yselivanov, ah I think you're right. I misread that function after I noticed 
the issue in the first one.

--

___
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue28963>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue28963] Use-after-free in _asynciomodule.c

2016-12-13 Thread Ned Williamson

New submission from Ned Williamson:

There are two cases of use-after-free in the new Modules/_asynciomodule.c in 
the release candidate for Python 3.6, but I'm filing these together because 
it's the same underlying issue.

In both cases in this file where the unsafe `PyList_GET_ITEM` is called, 
`_asyncio_Future_remove_done_callback` and `future_schedule_callbacks`, there 
is a function called on the fetched item that allows the user to mutate the 
callbacks list (`PyObject_RichCompareBool` and `_PyObject_CallMethodId`), which 
then leads to OOB access on subsequent iterations.

For example, this script can trigger the vulnerability for 
`remove_done_callback`:
```
import asyncio

fut = asyncio.Future()
fut.add_done_callback(str)

for _ in range(63):
fut.add_done_callback(id)

class evil:
def __eq__(self, other):
fut.remove_done_callback(id)
return False

fut.remove_done_callback(evil())
```

On an ASAN build we can see that there is indeed a UAF occurring:
```
=
==19239==ERROR: AddressSanitizer: heap-buffer-overflow on address 
0x6030e7f0 at pc 0x000106fe6704 bp 0x7fff5cda09c0 sp 0x7fff5cda09b8
READ of size 8 at 0x6030e7f0 thread T0
#0 0x106fe6703 in _asyncio_Future_remove_done_callback _asynciomodule.c:526
#1 0x102f5af35 in _PyCFunction_FastCallDict methodobject.c:192
#2 0x1030e9044 in call_function ceval.c:4788
#3 0x1030da2f0 in _PyEval_EvalFrameDefault ceval.c:3275
#4 0x1030eb09b in _PyEval_EvalCodeWithName ceval.c:718
#5 0x1030ced53 in PyEval_EvalCode ceval.c:4140
#6 0x10317da47 in PyRun_FileExFlags pythonrun.c:980
#7 0x10317c110 in PyRun_SimpleFileExFlags pythonrun.c:396
#8 0x1031c76b8 in Py_Main main.c:320
#9 0x102e5ed40 in main python.c:69
#10 0x7fffc9bd8254 in start (libdyld.dylib+0x5254)

0x6030e7f0 is located 0 bytes to the right of 32-byte region 
[0x6030e7d0,0x6030e7f0)
allocated by thread T0 here:
#0 0x1039d5f87 in wrap_realloc (libclang_rt.asan_osx_dynamic.dylib+0x4af87)
#1 0x102efb089 in list_ass_slice listobject.c:63
#2 0x106fe6605 in _asyncio_Future_remove_done_callback _asynciomodule.c:541
#3 0x102f5af35 in _PyCFunction_FastCallDict methodobject.c:192
#4 0x1030e9044 in call_function ceval.c:4788
#5 0x1030da2f0 in _PyEval_EvalFrameDefault ceval.c:3275
#6 0x1030ed94a in _PyFunction_FastCallDict ceval.c:718
#7 0x102e81308 in _PyObject_FastCallDict abstract.c:2295
#8 0x102e816b1 in _PyObject_Call_Prepend abstract.c:2358
#9 0x102e81286 in _PyObject_FastCallDict abstract.c:2316
#10 0x102fa125a in slot_tp_richcompare typeobject.c:6287
#11 0x102f61f66 in PyObject_RichCompare object.c:671
#12 0x102f62421 in PyObject_RichCompareBool object.c:741
#13 0x106fe6544 in _asyncio_Future_remove_done_callback _asynciomodule.c:528
#14 0x102f5af35 in _PyCFunction_FastCallDict methodobject.c:192
#15 0x1030e9044 in call_function ceval.c:4788
#16 0x1030da2f0 in _PyEval_EvalFrameDefault ceval.c:3275
#17 0x1030eb09b in _PyEval_EvalCodeWithName ceval.c:718
#18 0x1030ced53 in PyEval_EvalCode ceval.c:4140
#19 0x10317da47 in PyRun_FileExFlags pythonrun.c:980
#20 0x10317c110 in PyRun_SimpleFileExFlags pythonrun.c:396
#21 0x1031c76b8 in Py_Main main.c:320
#22 0x102e5ed40 in main python.c:69
#23 0x7fffc9bd8254 in start (libdyld.dylib+0x5254)

SUMMARY: AddressSanitizer: heap-buffer-overflow _asynciomodule.c:526 in 
_asyncio_Future_remove_done_callback
Shadow bytes around the buggy address:
  0x1c061ca0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c061cb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c061cc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c061cd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c061ce0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x1c061cf0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00[fa]fa
  0x1c061d00: 00 00 00 fa fa fa 00 00 00 fa fa fa 00 00 00 02
  0x1c061d10: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd
  0x1c061d20: fd fd fa fa fd fd fd fd fa fa fd fd fd fd fa fa
  0x1c061d30: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fd
  0x1c061d40: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:   00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:   fa
  Heap right redzone:  fb
  Freed heap region:   fd
  Stack left redzone:  f1
  Stack mid redzone:   f2
  Stack right redzone: f3
  Stack partial redzone:   f4
  Stack after return:  f5
  Stack use after scope:   f8
  Global redzone:  f9
  Global init order:   f6
  Poisoned by user:f7
  Container overflow:  fc
  Array cookie:ac
  Intra object redzone:bb
  ASan internal:   fe
  Left alloca redzone: ca
  Right alloca redzone:   

[issue25943] Integer overflow in _bsddb leads to heap corruption

2015-12-24 Thread Ned Williamson

New submission from Ned Williamson:

In function `_db_associateCallback` of the `_bsddb` module, associating two 
databases with a callback that returns a sufficiently large list will lead to 
heap corruption due an integer overflow on 32-bit Python.

>From `_bsddb.c`:
```
else if (PyList_Check(result))
{
char* data;
Py_ssize_t size;
int i, listlen;
DBT* dbts;

listlen = PyList_Size(result);

1.  dbts = (DBT *)malloc(sizeof(DBT) * listlen); ///sizeof(DBT) == 28 on my 
system, enough to overflow

2.  for (i=0; i<listlen; i++)
{
if (!PyBytes_Check(PyList_GetItem(result, i)))
{
PyErr_SetString(
   PyExc_TypeError,
#if (PY_VERSION_HEX < 0x0300)
"The list returned by DB->associate callback should be a list of strings.");
#else
"The list returned by DB->associate callback should be a list of bytes.");
#endif
PyErr_Print();
}

PyBytes_AsStringAndSize(
PyList_GetItem(result, i),
3.  , );

CLEAR_DBT(dbts[i]);
4.  dbts[i].data = malloc(size);  /* TODO, check this */

if (dbts[i].data)
{
5.  memcpy(dbts[i].data, data, size);
dbts[i].size = size;
dbts[i].ulen = dbts[i].size;
dbts[i].flags = DB_DBT_APPMALLOC;  /* DB will free */
}
else
{
PyErr_SetString(PyExc_MemoryError,
"malloc failed in _db_associateCallback (list)");
PyErr_Print();
}
}

CLEAR_DBT(*secKey);

secKey->data = dbts;
secKey->size = listlen;
secKey->flags = DB_DBT_APPMALLOC | DB_DBT_MULTIPLE;
retval = 0;
}
```

1. The multiplication in this line can overflow, allocating an undersized 
buffer.
2. This loop does not suffer from the overflow, so it can corrupt the heap by 
writing user data (see 3. and 5.).

This bug is present in Python 2.7.11.

See the result of running my attached POC script:
```
(gdb) r vuln.py
Starting program: /vagrant/Python-2.7.11/python.exe vuln.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
python.exe: malloc.c:2372: sysmalloc: Assertion `(old_top == (((mbinptr) 
(((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct 
malloc_chunk, fd && old_size == 0) || ((unsigned long) (old_size) >= 
(unsigned long)__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 
*(sizeof(size_t))) - 1)) & ~((2 *(sizeof(size_t))) - 1))) && ((old_top)->size & 
0x1) && ((unsigned long) old_end & pagemask) == 0)' failed.

Program received signal SIGABRT, Aborted.
0xb7fdd428 in __kernel_vsyscall ()
(gdb) bt
#0  0xb7fdd428 in __kernel_vsyscall ()
#1  0xb7de6607 in __GI_raise (sig=sig@entry=6) at 
../nptl/sysdeps/unix/sysv/linux/raise.c:56
#2  0xb7de9a33 in __GI_abort () at abort.c:89
#3  0xb7e2a9dd in __malloc_assert (
assertion=assertion@entry=0xb7f1e3c0 "(old_top == (((mbinptr) (((char *) 
&((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd 
&& old_size == 0) || ((unsigned long) (old_size) >= (unsigned 
long)__builtin_offs"...,
file=file@entry=0xb7f19954 "malloc.c", line=line@entry=2372,
function=function@entry=0xb7f19ce5 <__func__.10915> "sysmalloc") at 
malloc.c:293
#4  0xb7e2d5eb in sysmalloc (av=0xb7f62420 , nb=16) at malloc.c:2369
#5  _int_malloc (av=av@entry=0xb7f62420 , bytes=bytes@entry=1) at 
malloc.c:3800
#6  0xb7e2e708 in __GI___libc_malloc (bytes=1) at malloc.c:2891
#7  0xb7b006b2 in _db_associateCallback (db=0x82a7dd0, priKey=0xb228, 
priData=0xb034, secKey=0x8291a80)
at /vagrant/Python-2.7.11/Modules/_bsddb.c:1531
...
```
We can see that the `malloc` call on the line marked (4.) fails due to 
corrupted heap structures.
Also, running the script outside of GDB leads to a different message because of 
differences in heap layout:
```
vagrant@vagrant-ubuntu-trusty-32:/vagrant/Python-2.7.11$ ./python.exe vuln.py
*** Error in `python': corrupted double-linked list: 0x099e9858 ***
Aborted (core dumped)
```

This vulnerability can be fixed by checking for the overflow before the call to 
malloc. Also, note that the PyBytes_Check check does not exit the function, but 
PyBytesAsStringAndSize is called immediately afterwards. I would recommend 
breaking or continuing if that check fails, although I do think 
PyBytesAsStringAndSize performs this check as well.

--
components: Library (Lib)
files: bsddbpoc.py
messages: 256974
nosy: Ned Williamson
priority: normal
severity: normal
status: open
title: Integer overflow in _bsddb leads to heap corruption
type: crash
versions: Pytho

[issue25945] Type confusion in partial_setstate and partial_call leads to memory corruption

2015-12-24 Thread Ned Williamson

New submission from Ned Williamson:

static PyObject *
partial_setstate(partialobject *pto, PyObject *state)
{
PyObject *fn, *fnargs, *kw, *dict;
if (!PyArg_ParseTuple(state, "",
  , , , ))
return NULL;
Py_XDECREF(pto->fn);
Py_XDECREF(pto->args);
Py_XDECREF(pto->kw);
Py_XDECREF(pto->dict);
pto->fn = fn;
pto->args = fnargs; //we control pto->args here

`partial_setstate` performs no checks on the objects
it is passed as an argument.

static PyObject *
partial_call(partialobject *pto, PyObject *args, PyObject *kw)
{
PyObject *ret;
PyObject *argappl = NULL, *kwappl = NULL;

assert (PyCallable_Check(pto->fn));
assert (PyTuple_Check(pto->args)); //assume pto->args is a tuple
   //assertion not present in release build
assert (pto->kw == Py_None  ||  PyDict_Check(pto->kw));

if (PyTuple_GET_SIZE(pto->args) == 0) {
argappl = args;
Py_INCREF(args);
} else if (PyTuple_GET_SIZE(args) == 0) {
argappl = pto->args; //partial function called with no arguments
Py_INCREF(pto->args);
} else {
argappl = PySequence_Concat(pto->args, args);
if (argappl == NULL)
return NULL;
}

if (pto->kw == Py_None) {
kwappl = kw;
Py_XINCREF(kw);
} else {
kwappl = PyDict_Copy(pto->kw);
if (kwappl == NULL) {
Py_DECREF(argappl);
return NULL;
}
if (kw != NULL) {
if (PyDict_Merge(kwappl, kw, 1) != 0) {
Py_DECREF(argappl);
Py_DECREF(kwappl);
return NULL;
}
}
}
ret = PyObject_Call(pto->fn, argappl, kwappl); //pto->fn called with 
non-tuple argappl

We can see that in the provided POC there is an increment on a user-controlled 
address (in this case, the literal refcount of a given "argument" is 
interpreted as a pointer), as `_PyEval_EvalCodeWithName` does not validate the 
type of `PyObject **args` either (I assume this is a fair assumption for 
`_PyEval_EvalCodeWithName`, and the bug simply lies in the unsafe partial code.

vagrant@vagrant-ubuntu-wily-64:/vagrant/Python-3.5.1$ gdb -q ./python.exe
...
(gdb) r partialpoc2.py
Starting program: /vagrant/Python-3.5.1/python.exe partialpoc2.py
...
Program received signal SIGSEGV, Segmentation fault.
_PyEval_EvalCodeWithName (_co=0x77045ae0, globals=, 
locals=locals@entry=0x0, args=args@entry=0x76fbc520, argcount=1280, 
kws=kws@entry=0x0, kwcount=0, defs=0x0, defcount=0,
kwdefs=0x0, closure=0x0, name=0x0, qualname=0x0) at Python/ceval.c:3793
3793Py_INCREF(x);
(gdb) i r
rax0x9b4b68 10177384
rbx0x76fbc520   140737337083168
rcx0x1  1
rdx0x2  2
rsi0x5001280
rdi0x0  0
rbp0x0  0x0
rsp0x7fffdb30   0x7fffdb30
r8 0x5001280
r9 0x0  0
r100x774a6c58   140737342237784
r110x9b4b40 10177344
r120x0  0
r130x0  0
r140x76fb91e0   140737337070048
r150x77e1a048   140737352147016
rip0x4fc771 0x4fc771 <_PyEval_EvalCodeWithName+961>
eflags 0x10202  [ IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0  0
es 0x0  0
fs 0x0  0
gs 0x0  0
(gdb) x/3i $pc
=> 0x4fc771 <_PyEval_EvalCodeWithName+961>: addq   $0x1,(%rsi)
   0x4fc775 <_PyEval_EvalCodeWithName+965>: cmp%edx,%r8d
   0x4fc778 <_PyEval_EvalCodeWithName+968>: mov%rsi,0x18(%rax,%rcx,8)

--
files: partialpoc2.py
messages: 256976
nosy: Ned Williamson
priority: normal
severity: normal
status: open
title: Type confusion in partial_setstate and partial_call leads to memory 
corruption
type: crash
versions: Python 3.5, Python 3.6
Added file: http://bugs.python.org/file41410/partialpoc2.py

___
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue25945>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue25945] Type confusion in partial_setstate and partial_call leads to memory corruption

2015-12-24 Thread Ned Williamson

Changes by Ned Williamson <nedwilliam...@gmail.com>:


--
components: +Library (Lib)

___
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue25945>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue25944] Type confusion in partial_setstate and partial_repr leads to control flow hijack

2015-12-24 Thread Ned Williamson

New submission from Ned Williamson:

static PyObject *
partial_setstate(partialobject *pto, PyObject *state)
{
PyObject *fn, *fnargs, *kw, *dict;
if (!PyArg_ParseTuple(state, "",
  , , , ))
return NULL;
Py_XDECREF(pto->fn);
Py_XDECREF(pto->args);
Py_XDECREF(pto->kw);
Py_XDECREF(pto->dict);
pto->fn = fn;
pto->args = fnargs; //we control pto->args here

`partial_setstate` performs no checks on the objects
it is passed as an argument.

static PyObject *
partial_repr(partialobject *pto)
{
PyObject *result;
PyObject *arglist;
PyObject *tmp;
Py_ssize_t i, n;
arglist = PyUnicode_FromString("");
if (arglist == NULL) {
return NULL;
}
/* Pack positional arguments */
assert (PyTuple_Check(pto->args)); //not compiled in release build
n = PyTuple_GET_SIZE(pto->args);
for (i = 0; i < n; i++) {
tmp = PyUnicode_FromFormat("%U, %R", arglist,
   PyTuple_GET_ITEM(pto->args, i));

In partial_repr, `pto->args` is assumed to be a tuple and
unsafe functions `PyTuple_GET_SIZE` and `PyTuple_GET_ITEM`
are called on `pto->args`. This bug is particularly bad
because `PyUnicode_FromFormat` will call the object's repr
function. In this case, the attacker gains complete control
over the program counter.

vagrant@vagrant-ubuntu-wily-64:/vagrant/Python-3.5.1$ gdb -q ./python.exe
...
(gdb) r partialpoc.py
Starting program: /vagrant/Python-3.5.1/python.exe partialpoc.py
...
Program received signal SIGSEGV, Segmentation fault.
0x004851f6 in PyObject_Repr (v=0x972c90) at Objects/object.c:482
482 res = (*v->ob_type->tp_repr)(v);
(gdb) i r
rax0x4141414141414141   4702111234474983745
rbx0x972c90 9907344
rcx0x52 82
rdx0x77026718   140737337517848
rsi0x0  0
rdi0x972c90 9907344
rbp0x6667   0x6667
rsp0x7fffdb60   0x7fffdb60
r8 0x0  0
r9 0x6049a8 6310312
r100x   -1
r110x   -1
r120x7fff   9223372036854775807
r130x7fffdbe0   140737488346080
r140x6049a7 6310311
r150x0  0
rip0x4851f6 0x4851f6 <PyObject_Repr+38>
eflags 0x10206  [ PF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0  0
es 0x0  0
fs 0x0  0
gs 0x0  0
(gdb) x/3i $pc
=> 0x4851f6 <PyObject_Repr+38>: callq  *%rax
   0x4851f8 <PyObject_Repr+40>: test   %rax,%rax
   0x4851fb <PyObject_Repr+43>: mov%rax,%rbx

Please see the attached POC.

--
components: Library (Lib)
files: partialpoc.py
messages: 256975
nosy: Ned Williamson
priority: normal
severity: normal
status: open
title: Type confusion in partial_setstate and partial_repr leads to control 
flow hijack
type: crash
versions: Python 3.5, Python 3.6
Added file: http://bugs.python.org/file41409/partialpoc.py

___
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue25944>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com