New submission from Ned Williamson:

static PyObject *
partial_setstate(partialobject *pto, PyObject *state)
{
    PyObject *fn, *fnargs, *kw, *dict;
    if (!PyArg_ParseTuple(state, "OOOO",
                          &fn, &fnargs, &kw, &dict))
        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.
0x00000000004851f6 in PyObject_Repr (v=0x972c90) at Objects/object.c:482
482         res = (*v->ob_type->tp_repr)(v);
(gdb) i r
rax            0x4141414141414141       4702111234474983745
rbx            0x972c90 9907344
rcx            0x52     82
rdx            0x7ffff7026718   140737337517848
rsi            0x0      0
rdi            0x972c90 9907344
rbp            0x6666666666666667       0x6666666666666667
rsp            0x7fffffffdb60   0x7fffffffdb60
r8             0x0      0
r9             0x6049a8 6310312
r10            0xffffffffffffffff       -1
r11            0xffffffffffffffff       -1
r12            0x7fffffffffffffff       9223372036854775807
r13            0x7fffffffdbe0   140737488346080
r14            0x6049a7 6310311
r15            0x0      0
rip            0x4851f6 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

Reply via email to