Python 2.7 check_multiply_size() Integer Overflow

2015.11.03
Credit: John Leitch
Risk: Medium
Local: Yes
Remote: No
CVE: N/A
CWE: CWE-189

Title: Python 2.7 check_multiply_size() Integer Overflow Credit: John Leitch (john@autosectools.com) Url1: http://autosectools.com/Page/Python-check_multiply_size-Integer-Overflow Url2: http://bugs.python.org/issue24264 Resolution: Fixed Several functions within the imageop module are vulnerable to exploitable buffer overflows due to unsafe arithmetic in check_multiply_size(). The problem exists because the check to confirm that size == product / y / x does not take remainders into account. static int check_multiply_size(int product, int x, const char* xname, int y, const char* yname, int size) { if ( !check_coordonnate(x, xname) ) return 0; if ( !check_coordonnate(y, yname) ) return 0; if ( size == (product / y) / x ) return 1; PyErr_SetString(ImageopError, "String has incorrect length"); return 0; } Consider a call to check_multiply_size() where product is 16, x is 1, and y is 9. In the Windows x86 release of Python 2.7.9, the division is performed using the idiv instruction: 0:000> dV product = 0n16 x = 0n1 xname = 0x1e1205a4 "x" y = 0n9 yname = 0x1e127ab8 "y" size = 0n1 0:000> u eip python27!check_multiply_size+0x25 [c:\build27\cpython\modules\imageop.c @ 53]: 1e0330e5 f7ff idiv eax,edi 1e0330e7 99 cdq 1e0330e8 f7fe idiv eax,esi 1e0330ea 3944240c cmp dword ptr [esp+0Ch],eax 1e0330ee 7506 jne python27!check_multiply_size+0x36 (1e0330f6) 1e0330f0 b801000000 mov eax,1 1e0330f5 c3 ret 1e0330f6 8b15e47e241e mov edx,dword ptr [python27!ImageopError (1e247ee4)] 0:000> ?eax Evaluate expression: 16 = 00000010 0:000> ?edi Evaluate expression: 9 = 00000009 When the first idiv instruction is executed, the result (eax) is 1 with a remainder of 7 (edx): 0:000> p eax=00000001 ebx=00000000 ecx=1e127ab8 edx=00000007 esi=00000001 edi=00000009 eip=1e0330e7 esp=0027fcec ebp=00000001 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 python27!check_multiply_size+0x27: 1e0330e7 99 cdq 0:000> ?eax Evaluate expression: 1 = 00000001 0:000> ?edx Evaluate expression: 7 = 00000007 Because size is 1, the check passes: Breakpoint 4 hit eax=00000001 ebx=00000000 ecx=1e127ab8 edx=00000000 esi=00000001 edi=00000009 eip=1e0330f0 esp=0027fcec ebp=00000001 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 python27!check_multiply_size+0x30: 1e0330f0 b801000000 mov eax,1 This is problematic because some of the imageop functions, such as grey2rgb, utilize check_multiply_size() to check divisibility prior to copying data into a buffer. Consider a call to grey2rgb where x is 1, y is 9, and len is 16. static PyObject * imageop_grey2rgb(PyObject *self, PyObject *args) { int x, y, len, nlen; <<<<<<<< x = 1, y = 9, and len = 16. unsigned char *cp; unsigned char *ncp; PyObject *rv; int i; unsigned char value; int backward_compatible = imageop_backward_compatible(); if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) return 0; if ( !check_multiply(len, x, y) ) <<<<<<<< 16 != 1 * 9, but this check still passes. return 0; nlen = x*y*4; <<<<<<<< 1 * 9 * 4 == 36. if ( !check_multiply_size(nlen, x, "x", y, "y", 4) ) return 0; rv = PyString_FromStringAndSize(NULL, nlen); <<<<<<<< This creates a buffer of length 36. if ( rv == 0 ) return 0; ncp = (unsigned char *)PyString_AsString(rv); <<<<<<<< This retrieves the buffer of length 36. for ( i=0; i < len; i++ ) { <<<<<<<< This loop assumes that len * 4 == nlen, which is incorrect in this case. value = *cp++; if (backward_compatible) { VVVVVVVV Each iteration copies 4 bytes into the 36 byte buffer pointed to by ncp and advances the pointer by 4. Because len is 16, 64 bytes are ultimately copied into the buffer, leading to an exploitable buffer overflow condition. * (Py_UInt32 *) ncp = (Py_UInt32) value | ((Py_UInt32) value << 8 ) | ((Py_UInt32) value << 16); ncp += 4; } else { *ncp++ = 0; *ncp++ = value; *ncp++ = value; *ncp++ = value; } } return rv; } When the call completes, memory has been corrupted: 0:000> g (12f4.640): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00444444 ebx=00000001 ecx=1e201d98 edx=00303030 esi=1e201d98 edi=1e201d98 eip=1e031fc6 esp=0027fe7c ebp=00000002 iopl=0 nv up ei ng nz ac pe cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010297 python27!update_refs+0x6: 1e031fc6 8b5010 mov edx,dword ptr [eax+10h] ds:002b:00444454=???????? 0:000> g (12f4.640): Access violation - code c0000005 (!!! second chance !!!) eax=00444444 ebx=00000001 ecx=1e201d98 edx=00303030 esi=1e201d98 edi=1e201d98 eip=1e031fc6 esp=0027fe7c ebp=00000002 iopl=0 nv up ei ng nz ac pe cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010297 python27!update_refs+0x6: 1e031fc6 8b5010 mov edx,dword ptr [eax+10h] ds:002b:00444454=???????? During fuzzing, DEP access violations were encountered, so it should be assumed that this vulnerability can be exploited to achieve arbitrary code execution. To fix this vulnerability, it is recommended that check_multiply_size() confirm divisibility.

Referencje:

http://bugs.python.org/issue24264


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2021, cxsecurity.com

 

Back to Top