classification
Title: two heap corruption issues when running modified pyc code.
Type: security Stage:
Components: Interpreter Core Versions: Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Park Alex, gregory.p.smith, python-dev, rhettinger, serhiy.storchaka, stutzbach, vstinner
Priority: normal Keywords:

Created on 2016-06-11 03:40 by Park Alex, last changed 2016-06-12 00:43 by gregory.p.smith. This issue is now closed.

Files
File name Uploaded Description Edit
poc.zip Park Alex, 2016-06-11 03:40
Messages (7)
msg268173 - (view) Author: Park Alex (Park Alex) Date: 2016-06-11 03:40
Hello,

I would like to report two heap corruption issue.

Test environment:
python ersion: python 2.7.11+ 
hg id: d858eadf2602 (2.7)
compile: clang with ASAN
OS: ubuntu x86_64

One is heap-buffer-overflow, the other is heap-user-after-free. 
All of samples are attached in this bug report.

Thanks,
-- Alex

In detail,

1) heap-buffer-overflow bug could be triggerd at cpython/Python/ceval.c:1229
ASAN report is following:

=================================================================
==26786==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7ffff7ec56e8 at pc 0x5ec87f bp 0x7fffffffd2d0 sp 0x7fffffffd2c8
READ of size 8 at 0x7ffff7ec56e8 thread T0
    #0 0x5ec87e in PyEval_EvalFrameEx /project/cpython/Python/ceval.c:1229
    #1 0x5d3c6c in PyEval_EvalCodeEx /project/cpython/Python/ceval.c:3582
    #2 0x5d2b11 in PyEval_EvalCode /project/cpython/Python/ceval.c:669
    #3 0x6612d9 in run_pyc_file /project/cpython/Python/pythonrun.c:1406
    #4 0x6612d9 in PyRun_SimpleFileExFlags /project/cpython/Python/pythonrun.c:946
    #5 0x48e3dc in Py_Main /project/cpython/Modules/main.c:640
    #6 0x7ffff6ce282f in __libc_start_main /build/glibc-GKVZIf/glibc-2.23/csu/../csu/libc-start.c:291
    #7 0x48c518 in _start (/project/cpython/python.asan+0x48c518)

0x7ffff7ec56e8 is located 280 bytes to the left of 196608-byte region [0x7ffff7ec5800,0x7ffff7ef5800)
allocated by thread T0 here:
    #0 0x476429 in __interceptor_malloc /project/clang-3.4/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:74
    #1 0x50e969 in dictresize /project/cpython/Objects/dictobject.c:643
    #2 0x537844 in PyString_InternInPlace /project/cpython/Objects/stringobject.c:4757

SUMMARY: AddressSanitizer: heap-buffer-overflow /project/cpython/Python/ceval.c:1229 PyEval_EvalFrameEx
Shadow bytes around the buggy address:
  0x10007efd0a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x10007efd0a90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x10007efd0aa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x10007efd0ab0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x10007efd0ac0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x10007efd0ad0: fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]fa fa
  0x10007efd0ae0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x10007efd0af0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x10007efd0b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007efd0b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007efd0b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap right redzone:    fb
  Freed heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==26786==ABORTING

2) heap-user-after-free bug could be triggerd at cpython/Objects/dictobject.c:732
ASAN report is following:

=================================================================
==26918==ERROR: AddressSanitizer: heap-use-after-free on address 0x60f00000ef98 at pc 0x50f5ac bp 0x7fffffffa1d0 sp 0x7fffffffa1c8
READ of size 8 at 0x60f00000ef98 thread T0
    #0 0x50f5ab in PyDict_GetItem /project/cpython/Objects/dictobject.c:732
    #1 0x537792 in PyString_InternInPlace /project/cpython/Objects/stringobject.c:4750
    #2 0x64fad5 in r_object /project/cpython/Python/marshal.c:822
    #3 0x650d00 in r_object /project/cpython/Python/marshal.c:1037
    #4 0x64edf6 in r_object /project/cpython/Python/marshal.c:886
    #5 0x650c1b in r_object /project/cpython/Python/marshal.c:1019
    #6 0x64e1b1 in PyMarshal_ReadObjectFromString /project/cpython/Python/marshal.c:1183
    #7 0x64e1b1 in PyMarshal_ReadLastObjectFromFile /project/cpython/Python/marshal.c:1144
    #8 0x6429c1 in read_compiled_module /project/cpython/Python/import.c:823
    #9 0x6429c1 in load_source_module /project/cpython/Python/import.c:1094
    #10 0x644cda in import_submodule /project/cpython/Python/import.c:2722
    #11 0x643e81 in load_next /project/cpython/Python/import.c:2537
    #12 0x63e061 in import_module_level /project/cpython/Python/import.c:2254
    #13 0x63e061 in PyImport_ImportModuleLevel /project/cpython/Python/import.c:2310
    #14 0x5c4e1a in builtin___import__ /project/cpython/Python/bltinmodule.c:49
    #15 0x5e2535 in do_call /project/cpython/Python/ceval.c:4564
    #16 0x5e2535 in call_function /project/cpython/Python/ceval.c:4372
    #17 0x5e2535 in PyEval_EvalFrameEx /project/cpython/Python/ceval.c:2987
    #18 0x5d3c6c in PyEval_EvalCodeEx /project/cpython/Python/ceval.c:3582
    #19 0x7237f3 in function_call /project/cpython/Objects/funcobject.c:523
    #20 0x4aca9a in PyObject_Call /project/cpython/Objects/abstract.c:2546
    #21 0x5f1313 in PyEval_CallObjectWithKeywords /project/cpython/Python/ceval.c:4219
    #22 0x62118c in _PyCodec_Lookup /project/cpython/Python/codecs.c:147
    #23 0x6227d5 in _PyCodec_LookupTextEncoding /project/cpython/Python/codecs.c:459
    #24 0x622b6a in codec_getitem_checked /project/cpython/Python/codecs.c:511
    #25 0x622b6a in _PyCodec_TextEncoder /project/cpython/Python/codecs.c:523
    #26 0x622b6a in _PyCodec_EncodeText /project/cpython/Python/codecs.c:537
    #27 0x54dbc8 in PyString_AsEncodedObject /project/cpython/Objects/stringobject.c:532
    #28 0x54dbc8 in string_encode /project/cpython/Objects/stringobject.c:3016
    #29 0x5e232f in call_function /project/cpython/Python/ceval.c:4350
    #30 0x5e232f in PyEval_EvalFrameEx /project/cpython/Python/ceval.c:2987
    #31 0x5d3c6c in PyEval_EvalCodeEx /project/cpython/Python/ceval.c:3582
    #32 0x5f2c7d in fast_function /project/cpython/Python/ceval.c:4445
    #33 0x5dd2ba in call_function /project/cpython/Python/ceval.c:4370
    #34 0x5dd2ba in PyEval_EvalFrameEx /project/cpython/Python/ceval.c:2987
    #35 0x5d3c6c in PyEval_EvalCodeEx /project/cpython/Python/ceval.c:3582
    #36 0x5d2b11 in PyEval_EvalCode /project/cpython/Python/ceval.c:669
    #37 0x6612d9 in run_pyc_file /project/cpython/Python/pythonrun.c:1406
    #38 0x6612d9 in PyRun_SimpleFileExFlags /project/cpython/Python/pythonrun.c:946
    #39 0x48e3dc in Py_Main /project/cpython/Modules/main.c:640
    #40 0x7ffff6ce282f in __libc_start_main /build/glibc-GKVZIf/glibc-2.23/csu/../csu/libc-start.c:291
    #41 0x48c518 in _start (/project/cpython/python.asan+0x48c518)

0x60f00000ef98 is located 72 bytes inside of 168-byte region [0x60f00000ef50,0x60f00000eff8)
freed by thread T0 here:
    #0 0x4762a9 in free /project/clang-3.4/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64
    #1 0x64fad5 in r_object /project/cpython/Python/marshal.c:822
    #2 0x64edf6 in r_object /project/cpython/Python/marshal.c:886
    #3 0x650c7a in r_object /project/cpython/Python/marshal.c:1022
    #4 0x64edf6 in r_object /project/cpython/Python/marshal.c:886
    #5 0x650c1b in r_object /project/cpython/Python/marshal.c:1019
    #6 0x64e1b1 in PyMarshal_ReadObjectFromString /project/cpython/Python/marshal.c:1183
    #7 0x64e1b1 in PyMarshal_ReadLastObjectFromFile /project/cpython/Python/marshal.c:1144
    #8 0x6429c1 in read_compiled_module /project/cpython/Python/import.c:823
    #9 0x6429c1 in load_source_module /project/cpython/Python/import.c:1094
    #10 0x644cda in import_submodule /project/cpython/Python/import.c:2722
    #11 0x643e81 in load_next /project/cpython/Python/import.c:2537

previously allocated by thread T0 here:
    #0 0x476429 in __interceptor_malloc /project/clang-3.4/llvm-3.4/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:74
    #1 0x65de92 in new_threadstate /project/cpython/Python/pystate.c:159

SUMMARY: AddressSanitizer: heap-use-after-free /project/cpython/Objects/dictobject.c:732 PyDict_GetItem
Shadow bytes around the buggy address:
  0x0c1e7fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9de0: fa fa fa fa fa fa fa fa fa fa fd fd fd fd fd fd
=>0x0c1e7fff9df0: fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c1e7fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap right redzone:    fb
  Freed heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==26918==ABORTING
QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB
QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB
QUFBQUFBQUFBQUFBQUE=
msg268270 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-06-11 20:45
poc.zip contains only precompiled files. Could you please provide source files?
msg268276 - (view) Author: Park Alex (Park Alex) Date: 2016-06-11 21:21
all of .pyc files had been altered by fuzzer.

original py code is following:

$ cat helloworld.py
def hello(s=0x4142434445464748):
    print s
    if type(s) == str:
        print s.encode('hex')
        print repr(s)
    else:
        s = str(s)
    print len(s) << 8, len(s) ^ 8, len(s) | 8, len(s) & 8, len(s) == 8, len(s) <= 8, len(s) >= 8
    x = __import__("sys")

#    for k, v in x.__dict__.items():
#        if hasattr(v, '__subclasses__') == True:
#            cmd = "Subclasses:", (v.__class__.__base__.__subclasses__()[11].__init__.__str__())

    return 0x5152535455565758 == max(s, abs(len(s)) % 0x1234)

H = 'A'*128
hello(H.encode('base64'))
hello()

plus, python compiled with ASAN generated .pyc code.
After that, fuzzer found a few crashes.

Here are some diff information between original pyc and fuzzed pyc.
file: poc_heap-buffer-overflow.pyc
cmp -bl helloworld.pyc poc_heap-buffer-overflow.pyc | gawk '{printf "%08X %02X %02X\n", $1, strtonum(0$2), strtonum(0$4)}'
00000010 00 03
00000012 03 00
00000026 01 00
00000027 00 F7
0000006A 06 EE
0000006B 00 FF
0000006C 00 FF
0000006D 00 FF
00000129 01 FE
0000012A 00 FF
0000012B 64 00
0000012C 04 00
000001F0 6C DB
000001FD 6C 49

file: poc_heap-use-after-free.pyc 
cmp -bl helloworld.pyc poc_heap-use-after-free.pyc | gawk '{printf "%08X %02X %02X\n", $1, strtonum(0$2), strtonum(0$4)}'
0000006A 06 D0
0000006B 00 FF
0000006C 00 FF
0000006D 00 FF
00000129 01 EB
0000012A 00 FF
0000012B 64 00
0000012C 04 00
000001F0 6C DB
000001FD 6C 49
0000026F 6C 7D

Thanks,
-- Alex
msg268280 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2016-06-11 21:31
Hi, corrupted .pyc files are known to be abel to crash Python. What is the
point of your bug report? If you are able to execute untrusted .pyc, you
can already execute arbitrary code, no?

IMHO we should document the limitation of the security of CPython.
msg268284 - (view) Author: Park Alex (Park Alex) Date: 2016-06-11 21:48
I totally agreed with your opinion.
So I hesitated before reporting the issue (I thought)

It's kinda, we have different point of view.
As far as I can tell, 
python could be corrupted with .pyc like heap-use-after-free, buffer overrun and so on. 

Again, I agreed with your comment below:
"If you are able to execute untrusted .pyc, you can already execute arbitrary code, no?"

If don't want to bother you guys, I respect python-dev as always.

Thanks,
-- Alex
msg268286 - (view) Author: Park Alex (Park Alex) Date: 2016-06-11 21:52
oops, I cannot modify reply even I wrote it, 

want to fix tiny typo.

I don't want to bother you guys, I respect python-dev as always.

Thanks,
-- Alex
msg268302 - (view) Author: Gregory P. Smith (gregory.p.smith) * (Python committer) Date: 2016-06-12 00:43
Executing code in any form from untrusted sources can do arbitrary things.

If someone can corrupt .pyc data before python executes it, they are just as likely to be able to corrupt other things leading to more direct exploits not even requiring the CPython interpreter.
History
Date User Action Args
2016-06-12 00:43:14gregory.p.smithsetstatus: open -> closed

nosy: + gregory.p.smith
messages: + msg268302

resolution: not a bug
2016-06-11 21:52:29Park Alexsetmessages: + msg268286
2016-06-11 21:48:51Park Alexsetmessages: + msg268284
2016-06-11 21:38:50ppperrysettitle: two heap corruption issue -> two heap corruption issues when running modified pyc code.
2016-06-11 21:31:38vstinnersetmessages: + msg268280
2016-06-11 21:21:28Park Alexsetmessages: + msg268276
2016-06-11 20:45:48serhiy.storchakasetmessages: + msg268270
2016-06-11 20:41:53rhettingersetnosy: + rhettinger, vstinner, stutzbach, serhiy.storchaka
2016-06-11 03:40:05Park Alexcreate