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.
|