漏洞战争-cve-2012-0774
4 minute read
0x00 Prepare
1.adobe reader 9.4.0
2.immunity debugger
0x01 分析
打开adobe reader,od附加
f9
adobe reader中打开poc.pdf
adobe reader崩溃,但是od无法捕获异常,换成immunity debugger可捕获异常位置
重新打开reader,immunity debugger附加
f9
reader打开poc.pdf,异常捕获,中断如下:
6DD979C9 8D71 04 LEA ESI,DWORD PTR DS:[ECX+4]
6DD979CC 8B1E MOV EBX,DWORD PTR DS:[ESI]
6DD979CE 8919 MOV DWORD PTR DS:[ECX],EBX
6DD979D0 8BCE MOV ECX,ESI
6DD979D2 ^75 F4 JNZ SHORT CoolType.6DD979C8
6DD979D4 83E8 04 SUB EAX,4
中断位置为6dd979ce处的MOV DWORD PTR DS:[ECX],EBX,immunity debugger中下面状态栏中显示:access violation when
wring to 6dfcf000,也即[ecx]所在内存空间不可写,因此导致崩溃,从当前堆栈窗口中找出最上面的return to的地址,右
键在反汇编窗口跟随,得到当前的6dd979ce处的指令在如下call 调用的函数帧空间中:
6DD96956 50 PUSH EAX
6DD96957 FF148D D0BEFA6D CALL DWORD PTR DS:[ECX*4+6DFABED0] ; vulfunc
6DD9695E 59 POP ECX
将6dd96957处的调用标记为vulfunc,现在要在6dd96957处下断点并查看这个调用过程,但是有aslr机制,重新下断时这里不
再会是6dd96957,需算出6dd96957与当前dll的偏移再根据偏移在重新运行reader后下断点,alt+e:
Executable modules, item 22
Base=6DD90000
Size=0025F000 (2486272.)
Entry=6DDD866B CoolType.<ModuleEntryPoint>
Name=CoolType
File version=5.05.73.1
Path=C:\Program Files (x86)\Adobe\Reader 9.0\Reader\CoolType.dll
在alt+e窗口中得到vulfunc在CoolType.dll中,且vulfunc在CoolType.dll中的内存偏移为:6dd96957-6dd90000=6957
重新打开reader并用immu附加,f9,alt+e,发现cooltype.dll的基址不变,仍然是6dd90000,那么前面一步的计算偏移就是多余的
了,看来泉哥在这里的想法不完全正确,这里不用算偏移,应该是操作系统没有为cooltype.dll开aslr.可直接在6dd96957处下断.
后来发现是自己错了,重新运行reader并用immu附加后cooltype.dll的地址会是6dd90000或是69360000,eg.第一次运行reader并
用immu附加后cooltype.dll的基址为6dd90000,关闭reader和immu后,第二次运行reader并用immu附加后cooltype.dll的基址为
69360000,第三次又变回6dd90000,第四次则为69360000,...以此交替.说明cooltype.dll还是开了aslr的.继续用偏移的方法下
断点,重新打开reader,immu附加,第一次cooltype.dll的基址为6dd90000,第二次为69360000,此时在69360000+6957=69366957处
下断
ctrl+g:69366957
f2
69366955 51 PUSH ECX
69366956 50 PUSH EAX
69366957 FF148D D0BE5769 CALL DWORD PTR DS:[ECX*4+6957BED0]
6936695E 59 POP ECX
6936695F 59 POP ECX
在69366957处shift+f4下条件记录断点,记录ecx(ecx是虚拟指令索引号),设置为不中断,explanation设成:"index ecx",
expression设成:"ecx"
f9
reader打开poc.pdf
再次崩溃,由immu捕获崩溃,与上次崩溃信息相同,得到log面板中日志如下:
...
...
...
...
69366957 COND: index ecx = 00000041
69366957 COND: index ecx = 00000063
69366957 COND: index ecx = 00000060
69366957 COND: index ecx = 00000041
69366957 COND: index ecx = 00000043
69366957 COND: index ecx = 000000B0
69366957 COND: index ecx = 00000061
69366957 COND: index ecx = 00000042
69366957 COND: index ecx = 00000043
69366957 COND: index ecx = 00000078
69366957 COND: index ecx = 00000041
69366957 COND: index ecx = 00000063
69366957 COND: index ecx = 00000060
69366957 COND: index ecx = 00000041
69366957 COND: index ecx = 00000043
69366957 COND: index ecx = 000000B0
69366957 COND: index ecx = 00000061
69366957 COND: index ecx = 00000042
69366957 COND: index ecx = 00000043
69366957 COND: index ecx = 00000078
69366957 COND: index ecx = 00000041
69366957 COND: index ecx = 00000060
69366957 COND: index ecx = 00000060
69366957 COND: index ecx = 00000026
693679CE [18:15:48] Access violation when writing to [6959F000]
说明是索引号为26的call调用造成了access violation,设置条件断点:当ecx为26时中断
重新打开reader并用immu附加,这次cooltype.dll基址为6dd90000,6dd90000+6957=6dd96957,ctrl+g:6dd96957
6DD96955 51 PUSH ECX
6DD96956 50 PUSH EAX
6DD96957 FF148D D0BEFA6D CALL DWORD PTR DS:[ECX*4+6DFABED0] ; vulfunc
6DD9695E 59 POP ECX
6DD9695F 59 POP ECX
6DD96960 3BC6 CMP EAX,ESI
在6dd96957上设置条件断点,shift+f2,设置断点条件为ecx==26
f9
reader打开poc.pdf
成功中断到6dd96957处,且ecx=26,f7跟进call,汇编指令如下:
6DD9798B A1 E023FC6D MOV EAX,DWORD PTR DS:[6DFC23E0]
6DD97990 8B0D EC23FC6D MOV ECX,DWORD PTR DS:[6DFC23EC]
6DD97996 53 PUSH EBX
6DD97997 56 PUSH ESI
6DD97998 8B31 MOV ESI,DWORD PTR DS:[ECX]
6DD9799A 8D50 FC LEA EDX,DWORD PTR DS:[EAX-4]
6DD9799D 3BD6 CMP EDX,ESI
6DD9799F 57 PUSH EDI
6DD979A0 72 45 JB SHORT CoolType.6DD979E7
6DD979A2 8BB9 54010000 MOV EDI,DWORD PTR DS:[ECX+154]
6DD979A8 3BD7 CMP EDX,EDI
6DD979AA 73 3B JNB SHORT CoolType.6DD979E7
6DD979AC 83C0 FC ADD EAX,-4
6DD979AF 8B10 MOV EDX,DWORD PTR DS:[EAX]
6DD979B1 8BDA MOV EBX,EDX
6DD979B3 C1E3 02 SHL EBX,2
6DD979B6 8BC8 MOV ECX,EAX
6DD979B8 2BCB SUB ECX,EBX
6DD979BA 3BCE CMP ECX,ESI
6DD979BC 72 29 JB SHORT CoolType.6DD979E7
6DD979BE 3BCF CMP ECX,EDI
6DD979C0 73 25 JNB SHORT CoolType.6DD979E7
6DD979C2 85D2 TEST EDX,EDX
6DD979C4 8B39 MOV EDI,DWORD PTR DS:[ECX]
6DD979C6 7E 0F JLE SHORT CoolType.6DD979D7
6DD979C8 4A DEC EDX
6DD979C9 8D71 04 LEA ESI,DWORD PTR DS:[ECX+4]
6DD979CC 8B1E MOV EBX,DWORD PTR DS:[ESI]
6DD979CE 8919 MOV DWORD PTR DS:[ECX],EBX
6DD979D0 8BCE MOV ECX,ESI
6DD979D2 ^75 F4 JNZ SHORT CoolType.6DD979C8
6DD979D4 83E8 04 SUB EAX,4
6DD979D7 8938 MOV DWORD PTR DS:[EAX],EDI
6DD979D9 83C0 04 ADD EAX,4
6DD979DC A3 E023FC6D MOV DWORD PTR DS:[6DFC23E0],EAX
6DD979E1 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10]
6DD979E5 EB 0F JMP SHORT CoolType.6DD979F6
6DD979E7 A1 3824FC6D MOV EAX,DWORD PTR DS:[6DFC2438]
6DD979EC C705 3424FC6D 10>MOV DWORD PTR DS:[6DFC2434],1110
6DD979F6 5F POP EDI
6DD979F7 5E POP ESI
6DD979F8 5B POP EBX
6DD979F9 C3 RETN
此时eip=6dd9768b,单步跟踪
f8
f8
...
eip=6dd979b3
此时汇编指令为shl ebx,2,寄存器值如下:
EAX 6DFC622C CoolType.6DFC622C
ECX 0577E4E4
EDX 40000001
EBX 40000001
ESP 0014CA20
EBP 0014CAB4
ESI 6DFC6220 ASCII "AA"
EDI 6DFC6344 ASCII "tnfs"
EIP 6DD979B3 CoolType.6DD979B3
这里的ebx为0x40000001,shl ebx,2之后得到ebx=0x40000001*4=0x100000004=0x00000004,导致整数溢出,再继续单步跟踪
到6DD979CE处的MOV DWORD PTR DS:[ECX],EBX应该会出现前面一样的access violation,reader崩溃.
f8
...
eip=6dd979ce
此时并没有崩溃,因为在6dd979c8到6dd979d2为一个循环,会在这个循环中的某一次中出现access violation使得reader崩
溃,0x40000001是溢出的关键,它完全是由虚拟指令操作实现的.为了看清0x40000001是怎么出现的,需要知道每个虚拟指令
索引号对应的call中的汇编指令效果,于是对溢出前的每个虚拟指令索引号对应的CALL DWORD PTR DS:[ECX*4+6DFABED0]
设置条件记录断点,记录call前与call后虚拟栈顶vm_esp的值及虚拟指令.这里的虚拟栈顶vm_esp并不是指esp中的数据,这
里的虚拟指令类似于java的虚拟机指令,是"虚拟的",并不是真实栈中的数据,而是对虚拟指令而言的"虚拟栈",如B0 01虚拟
指令对应将1压栈的动作,这里的压栈是对虚拟指令而言的栈,并不是真实的esp中的栈,要想得到"虚拟栈"中的数据,需要得到
"虚拟栈"对应的真实内存地址,跟踪B0 01虚拟指令的CALL DWORD PTR DS:[ECX*4+6DFABED0]的处理情况,进行如下操作
重新打开read并用immu附加,这次cooltype.dll的基址为69360000,ctrl+g:69366957,在69366957处下条件断点,设置条件为
ecx=0xB0
f9
reader打开poc.pdf
成功中断在ecx=b0情况下,对应call的函数帧中指令如下:
69368B05 A1 E0235969 MOV EAX,DWORD PTR DS:[695923E0]
69368B0A 8B15 EC235969 MOV EDX,DWORD PTR DS:[695923EC]
69368B10 8D48 04 LEA ECX,DWORD PTR DS:[EAX+4]
69368B13 3B8A 54010000 CMP ECX,DWORD PTR DS:[EDX+154]
69368B19 77 18 JA SHORT CoolType.69368B33
69368B1B 3B0A CMP ECX,DWORD PTR DS:[EDX]
69368B1D 76 14 JBE SHORT CoolType.69368B33
69368B1F 8B4C24 04 MOV ECX,DWORD PTR SS:[ESP+4]
69368B23 0FB611 MOVZX EDX,BYTE PTR DS:[ECX]
69368B26 8910 MOV DWORD PTR DS:[EAX],EDX
69368B28 8305 E0235969 04 ADD DWORD PTR DS:[695923E0],4
69368B2F 8D41 01 LEA EAX,DWORD PTR DS:[ECX+1]
69368B32 C3 RETN
由于B0 01虚拟指令对应的虚拟动作是将1压栈,现在要找到1所在的真实内存位置,f8单步
f8
...
...
f8
eip=69368b26时,edx=1,[eax]=1,对应"虚拟栈顶"vm_esp中的数据
f8
eip=69368b28时,此时已经将1赋值给[eax],此时可以看到[695923e0]=69596240=eax
f8
eip=69368b2f,此时已经执行完ADD DWORD PTR DS:[695923E0],4,后面再f8再次就执行完call了,执行完call之后要想得到
"虚拟栈顶"vm_esp中的数据的方法为:查看真实内存中的值[[695923e0]-4],所以要想得到溢出前每个索引对应的call在
call执行前与call执行后虚拟栈顶的值及虚拟指令,则需要设置条件记录断点,并设置expression为:"[[695923e0]-4]",为
了观察更多,也记录vm_esp-4中的数据,并在call调用前和调用后分别记录,也即最后这样设置:
在call的上一句指令处设置条件记录断点,并设置explanation为:"执行前[vm_esp]",设置expression为:"[[695923e0]-4]"
在call的上一句指令处设置条件记录断点,并设置explanation为:"执行前[vm_esp-4],设置expression为"[[695923e0]-8]"
在call指令处设置条件记录断点,并设置explanation为:"虚拟指令索引号",设置expression为"ecx"
在call的下一句指令处设置条件记录断点,并设置explanation为:"执行后[vm_esp]",设置expression为:"[[695923e0]-4]"
在call的下一句指令处设置条件记录断点,并设置explanation为:"执行后[vm_esp-4],设置expression为"[[695923e0]-8]"
上面5个设置中都设置不中断且记录expression的值
重新打开read并用immu附加,此时cooltype.dll基址为6dd90000,关键call调用的地址为6dd96957
ctrl+g:6dd96957,按照上面的5个设置条件记录断点之后便可得到书中对应log记录,再通过分析log得到0x40000001的来源过程.
需注意要在32位系统上调试,如果在64位系统上调试应该设置explanation为vm_esp和vm_esp-8,expression对应为
[[695923e0]-4]和[[695923e0]-0xc].