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