Home
Consulting
Advisories
Software
Articles
Contact

Python deque.index() Uninitialized Variable

Vulnerabilities | Report

Python 3.5 suffers from a vulnerability caused by the behavior of the newblock() function used by the collections.deque module. When called, newblock() allocates memory using PyMem_Malloc() and does not initialize it:

static block *
newblock(Py_ssize_t len) {
    block *b;
    if (len >= MAX_DEQUE_LEN) {
        PyErr_SetString(PyExc_OverflowError,
                        "cannot add more blocks to the deque");
        return NULL;
    }
    if (numfreeblocks) {
        numfreeblocks--;
        return freeblocks[numfreeblocks];
    }
    b = PyMem_Malloc(sizeof(block)); <<<< Memory allocation.
    if (b != NULL) {
        return b; <<<< Buffer returned without initialization.
    }
    PyErr_NoMemory();
    return NULL;
}

Because PyMem_Malloc does not initialize the memory, the block may contain garbage data. In some cases, this can lead to memory corruption which could be exploitable to achieve code execution. The following exception analysis is an example of EIP corruption:

*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

*** The OS name list needs to be updated! Unknown Windows version: 10.0 ***

FAULTING_IP:
python35!PyUnicode_Type+0
696f60d8 a800            test    al,0

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 696f60d8 (python35!PyUnicode_Type)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000008
   Parameter[1]: 696f60d8
Attempt to execute non-executable address 696f60d8

CONTEXT:  00000000 -- (.cxr 0x0;r)
eax=696f60d8 ebx=00000002 ecx=00d9492c edx=00000002 esi=019b4e58 edi=0337b970
eip=696f60d8 esp=00bcf7dc ebp=00bcf7fc iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
python35!PyUnicode_Type:
696f60d8 a800            test    al,0

PROCESS_NAME:  pythonw.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_PARAMETER1:  00000008

EXCEPTION_PARAMETER2:  696f60d8

WRITE_ADDRESS:  696f60d8

FOLLOWUP_IP:
python35!PyUnicode_Type+0
696f60d8 a800            test    al,0

FAILED_INSTRUCTION_ADDRESS:
python35!PyUnicode_Type+0
696f60d8 a800            test    al,0

APP:  pythonw.exe

ANALYSIS_VERSION: 6.3.9600.17029 (debuggers(dbg).140219-1702) x86fre

FAULTING_THREAD:  000009dc

DEFAULT_BUCKET_ID:  SOFTWARE_NX_FAULT_CODE

PRIMARY_PROBLEM_CLASS:  SOFTWARE_NX_FAULT_CODE

BUGCHECK_STR:  APPLICATION_FAULT_SOFTWARE_NX_FAULT_CODE_SOFTWARE_NX_FAULT_FALSE_POSITIVE

LAST_CONTROL_TRANSFER:  from 69505ad3 to 696f60d8

STACK_TEXT:
00bcf7fc 69505ad3 00000002 00bcf840 694253fc python35!PyUnicode_Type
00bcf808 694253fc 0337b970 019b4e58 00000002 python35!PyObject_RichCompare+0x53
00bcf840 695031c3 03a1a8f0 03a21878 00f83340 python35!deque_index+0xac
00bcf85c 69564433 03a21120 03a21878 00000000 python35!PyCFunction_Call+0x113
00bcf890 695618d8 00e23a08 00000000 00000040 python35!call_function+0x303
00bcf908 6956339f 00e23a08 00000000 00f83000 python35!PyEval_EvalFrameEx+0x2318
00bcf954 6959a142 00e40f58 00000000 00000000 python35!_PyEval_EvalCodeWithName+0x82f
00bcf990 69599fd5 00e40f58 00e40f58 00bcfa5c python35!run_mod+0x42
00bcf9bc 6959904a 00f801f0 00e366f0 00000101 python35!PyRun_FileExFlags+0x85
00bcfa00 6946f037 00f801f0 00e366f0 00000001 python35!PyRun_SimpleFileExFlags+0x20a
00bcfa2c 6946f973 00bcfa5c 00000000 6ecb2100 python35!run_file+0xe7
00bcfad4 1ce31279 00000002 00f79eb0 1ce3c588 python35!Py_Main+0x913
00bcfae4 1ce3145f 1ce30000 00000000 00f71c68 pythonw!wWinMain+0x19
00bcfb30 74ed3744 7f174000 74ed3720 5c8b59d2 pythonw!__scrt_common_main_seh+0xfd
00bcfb44 775aa064 7f174000 a81800d2 00000000 kernel32!BaseThreadInitThunk+0x24
00bcfb8c 775aa02f ffffffff 775cd7c3 00000000 ntdll!__RtlUserThreadStart+0x2f
00bcfb9c 00000000 1ce3150a 7f174000 00000000 ntdll!_RtlUserThreadStart+0x1b


STACK_COMMAND:  ~0s; .ecxr ; kb

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  python35!PyUnicode_Type+0

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: python35

IMAGE_NAME:  python35.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  5598ccc2

FAILURE_BUCKET_ID:  SOFTWARE_NX_FAULT_CODE_c0000005_python35.dll!PyUnicode_Type

BUCKET_ID:  APPLICATION_FAULT_SOFTWARE_NX_FAULT_CODE_SOFTWARE_NX_FAULT_FALSE_POSITIVE_BAD_IP_python35!PyUnicode_Type+0

ANALYSIS_SOURCE:  UM

FAILURE_ID_HASH_STRING:  um:software_nx_fault_code_c0000005_python35.dll!pyunicode_type

FAILURE_ID_HASH:  {aa94d074-8f9b-b618-df4f-eaad15f84370}

Followup: MachineOwner
---------

To fix the issue, it is recommended that newblock use PyMem_Calloc instead of PyMem_Malloc. A proposed patch has been attached.

Credit: John Leitch (john@autosectools.com), Bryce Darling (darlingbryce@gmail.com)



Copyright © 2018 AutoSec Tools LLC