CVE-2017-7269 样本调试笔记(二)
好了,来到关键的第三次 rep movsd
0:006> t
Breakpoint 2 hit
eax=00000130 ebx=00000097 ecx=0000004c edx=00fef804 esi=020f0910 edi=00fef828
eip=67126fdb esp=00fef330 ebp=00fef798 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
httpext!ScStoragePathFromUrl+0x360:
67126fdb f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
查看拷贝的数据
0:006> du esi L2 * ecx
020f0910
"/aaaaaaa潨硣睡焳椶䝲稹䭷佰畓穏䡨噣浔桅㥓偬啧杣.䘰硅楒吱"
...(lines have been omitted)...
020f0a10
"Ꮐ栃䠴攱潃湦瑁䍬Ꮐ栃千橁灒.塦䉌灋捆关祁穐䩬>"
拷贝前,对比一下目的地址的原始数据和源地址的数据
0:006> db edi L4 * ecx
00fef828 60 06 ec 01 50 06 ec 01-00 00 00 00 00 00 00 00 `...P...........
00fef838 00 00 00 00 00 00 00 00-00 00 00 00 01 00 00 00 ................
...(lines have been omitted)...
00fef908 12 04 00 00 04 f8 fe 00-5b 20 11 67 13 00 00 00 ........[ .g....
00fef918 c0 f9 fe 00 e7 87 12 67-00 00 00 00 f0 00 00 00 .......g........
00fef928 13 00 00 00 00 00 00 00-24 f2 ec 01 fc 87 12 67 ........$......g
00fef938 d0 f9 fe 00 69 66 2d 75-6e 6d 6f 64 69 66 69 65 ....
if
-unmodifie
00fef948 64 2d 73 69 6e 63 65 00-00 00 00 00 78 01 ec 01 d-since.....x...
0:006> db esi L4 * ecx
020f0910 2f 00 61 00 61 00 61 00-61 00 61 00 61 00 61 00 /.a.a.a.a.a.a.a.
020f0920 68 6f 63 78 61 77 33 71-36 69 72 47 39 7a 77 4b hocxaw3q6irG9zwK
...(lines have been omitted)...
020f09f0 02 02 02 02 c0 12 03 68-44 6c 56 52 37 4b 6d 6c .......hDlVR7Kml
020f0a00 58 4f 5a 58 50 79 6a 49-4f 58 52 4a 50 41 4d 66 XOZXPyjIOXRJPAMf
020f0a10 c0 13 03 68 34 48 31 65-43 6f 66 6e 41 74 6c 43 ...h4H1eCofnAtlC
020f0a20 c0 13 03 68 43 53 41 6a-52 70 30 33 66 58 4c 42 ...hCSAjRp03fXLB
020f0a30 4b 70 46 63 73 51 41 79-50 7a 6c 4a 3e 00 00 00 KpFcsQAyPzlJ>...
忍不住又要剧透了,这次拷贝的关键在于覆写地址 0x00fef90c 处的数据,数据 0x00FEF804 修改为 0x680312C0,这不是一个巧合。
单步过去,然后 G 起来
0:006> bd *
0:006> p
eax=00000130 ebx=00000097 ecx=00000000 edx=00fef804 esi=020f0a40 edi=00fef958
eip=67126fdd esp=00fef330 ebp=00fef798 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
httpext!ScStoragePathFromUrl+0x362:
67126fdd 8bc8 mov ecx,eax
0:006> be *
0:006> g
Breakpoint 1 hit
eax=00fef800 ebx=020f01e8 ecx=01ecfa80 edx=020f2878 esi=00000000 edi=77bd8ef2
eip=67119464 esp=00fef7a0 ebp=00fef7ac iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
httpext!CMethUtil::ScStoragePathFromUrl+0x13:
67119464 e812d80000 call httpext!ScStoragePathFromUrl (67126c7b)
中断到上层函数,看看这次的参数
0:006> dds esp L3
00fef7a0 680312c0 rsaenh!g_pfnFree+0x4
00fef7a4 00fef800
00fef7a8 00000000
经过前面的调试,已经知道地址 0x680312c0 肯定会作为目标地址被拷贝数据,那么就,围观一下这段内存空间
0:006> !address 680312c0
68000000 : 68031000 - 00001000
Type 01000000 MEM_IMAGE
Protect 00000040 PAGE_EXECUTE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageImage
FullPath C:\WINDOWS\system32\rsaenh.dll
这就是初初调试时感到美妙的地方,0x680312c0 这个值是哪里冒出来的?这可是别人的模块
查看上下文环境
6711945e ff7510 push dword ptr [ebp+10h]
67119461 ff750c push dword ptr [ebp+0Ch]
67119464 e812d80000 call httpext!ScStoragePathFromUrl (67126c7b)
0:006> dd ebp+0Ch L1
00fef7b8 680312c0
那么地址 0x00fef7b8 的数据 0x680312c0 又从哪里来的呢?
重新运行样本,对地址 0x00fef7b8 下个写断点
0:006> ba w4 00fef7b8
留意地址 0x00fef7b8 处数据的值,执行到这里时出现我们寻找的值
0:006> g
Breakpoint 3 hit
eax=020f003c ebx=020f01e8 ecx=00000000 edx=00000026 esi=020f0710 edi=680312c0
eip=671254e0 esp=00fef7b8 ebp=00fefc34 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
httpext!HrCheckIfHeader+0x1ba:
671254e0 56 push esi
0:006> dd 00fef7b8 L1
00fef7b8 680312c0
查看上下文环境
671254b1 8bbdd8fcffff mov edi,dword ptr [ebp-328h]
671254b7 8d8ddcfcffff lea ecx,[ebp-324h]
671254bd c645fc01 mov byte ptr [ebp-4],1
671254c1 e8000affff call httpext!CStackBuffer<unsigned
short
,260>::release (67115ec6)
671254c6 83a5c4fbffff00 and dword ptr [ebp-43Ch],0
671254cd 6a02 push 2
671254cf e9a8000000 jmp httpext!HrCheckIfHeader+0x256 (6712557c)
671254d4 668b06 mov ax,word ptr [esi]
671254d7 663d3c00 cmp ax,3Ch
671254db 750c jne httpext!HrCheckIfHeader+0x1c3 (671254e9)
671254dd 6a00 push 0
671254df 57 push edi
671254e0 56 push esi
edi 的值来自 [ebp-328h]
0:006> dd ebp-328h L1
00fef90c 680312c0
这就是前面剧透的,拷贝数据时,地址 0x00fef90c 处的数据由 0x00FEF804 修改为 0x680312C0
取消该写断点,G一下,中断到上层函数,地址 0x00fef90c 处的数据没有变化
如上所述,当前参数如下
0:006> dds esp L3
00fef7a0 680312c0 rsaenh!g_pfnFree+0x4
00fef7a4 00fef800
00fef7a8 00000000
以上整个过程中,弄清了第一次大规模拷贝数据的意义:控制第二次大规模拷贝数据的目的地址
现在分析第二次大规模拷贝,G 一下,直接断在第三次 rep movsd 处
0:006> bl
0 e 77ea411e 0001 (0001) 0:**** kernel32!WinExec
1 e 67119464 0001 (0001) 0:**** httpext!CMethUtil::ScStoragePathFromUrl+0x13
2 e 67126fdb 0001 (0001) 0:**** httpext!ScStoragePathFromUrl+0x360
0:006> g
Breakpoint 2 hit
eax=000005fc ebx=000002fd ecx=0000017f edx=680312c0 esi=020f2898 edi=680312e4
eip=67126fdb esp=00fef330 ebp=00fef798 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
httpext!ScStoragePathFromUrl+0x360:
67126fdb f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
是的,目的寄存器 edi 的值在内存中属于 rsaenh.dll 模块
查看拷贝的源数据
0:006> du esi L2 * ecx
020f2898
"/bbbbbbb祈慵佃潧歯䡅㙆杵䐳㡱坥婢吵噡楒橓兗㡎奈捕䥱䍤摲㑨"
...(lines have been omitted)...
020f2e58
"L3W9P5POO0F2SMXJNJMJS8KJNKPA>"
拷贝之后的内存数据
0:006> db 680312e4 L4 * 17f
680312e4 2f 00 62 00 62 00 62 00-62 00 62 00 62 00 62 00 /.b.b.b.b.b.b.b.
...(lines have been omitted)...
680313b4 41 59 50 71 36 30 77 57-57 44 61 53 c0 13 03 68 AYPq60wWWDaS...h
680313c4 4f 6e 00 68 4f 6e 00 68-47 42 6a 76 c0 13 03 68 On.hOn.hGBjv...h
680313d4 57 42 74 4f 47 59 34 52-66 4b 42 4b 64 74 6f 78 WBtOGY4RfKBKdtox
680313e4 82 60 01 68 35 51 7a 72-7a 74 47 4d 59 44 57 57 .`.h5QzrztGMYDWW
...(lines have been omitted)...
680318d4 4e 00 4b 00 50 00 41 00-3e 00 00 00 N.K.P.A.>...
现在分析第三次大规模数据拷贝,来到上层函数
查看参数
0:006> dds esp L3
00fef944 00fefab4
00fef948 00fef9a4
00fef94c 00000000
G 一下,断在第三次 rep movsd 处
0:006> g
Breakpoint 2 hit
eax=00000130 ebx=00000097 ecx=0000004c edx=00fefab4 esi=020f0730 edi=00fefad8
eip=67126fdb esp=00fef4d4 ebp=00fef93c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
httpext!ScStoragePathFromUrl+0x360:
67126fdb f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
查看源数据,和第一次大规模拷贝的数据相同
0:006> du esi L2 * ecx
020f0730
"/aaaaaaa潨硣睡焳椶䝲稹䭷佰畓穏䡨噣浔桅㥓偬啧杣.䘰硅楒吱"
...(lines have been omitted)...
020f0830
"Ꮐ栃䠴攱潃湦瑁䍬Ꮐ栃千橁灒.塦䉌灋捆关祁穐䩬>"
拷贝之后的内存数据
0:006> db 00fefad8 L4 * 4c
00fefad8 2f 00 61 00 61 00 61 00-61 00 61 00 61 00 61 00 /.a.a.a.a.a.a.a.
00fefae8 68 6f 63 78 61 77 33 71-36 69 72 47 39 7a 77 4b hocxaw3q6irG9zwK
...(lines have been omitted)...
00fefbe8 c0 13 03 68 43 53 41 6a-52 70 30 33 66 58 4c 42 ...hCSAjRp03fXLB
00fefbf8 4b 70 46 63 73 51 41 79-50 7a 6c 4a 3e 00 00 00 KpFcsQAyPzlJ>...
这次大规模拷贝的数据,连上第一次 rep movsd,是从地址 0x00fefab4 开始的
G一下,再次来到上层函数
查看参数
0:006> dds esp L3
00fef944 00fefab4
00fef948 00fef9a4
00fef94c 00000000
此次的参数跟上一次参数是相同的,上一次已经向地址 0x00fefab4 拷贝了数据,经过调试,现在没有大规模数据拷贝了,跟进 httpext!ScStoragePathFromUrl 函数
单步来到地址 0x67126cc4 处,继续单步步入
67126cc4 e80cc90000 call httpext!ScStripAndCheckHttpPrefix (671335d5)
httpext!ScStoragePathFromUrl 函数中,关键代码如下
单步到地址 0x671335e8 处
0:006> t
eax=00fef9a4 ebx=00fefbe8 ecx=680313c0 edx=00fef4f8 esi=020f2878 edi=680313c0
eip=671335e8 esp=00fef4b8 ebp=00fef4d0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
httpext!ScStripAndCheckHttpPrefix+0x13:
671335e8 8b07 mov eax,dword ptr [edi] ds:0023:680313c0=680313c0
很明显 ecx 是 this 指针,ecx 给到 edi,edi 取内容给 eax,那么 eax 此时是虚表指针
继续单步
调用虚函数,进入第二次大规模布置数据的内存中
继续单步步入,首先是换栈操作
68016082 8be1 mov esp,ecx
68016084 8b08 mov ecx,dword ptr [eax]
68016086 8b4004 mov eax,dword ptr [eax+4]
68016089 50 push eax
6801608a c3 ret
经过 ROP 链和解密 shellcode 后,执行到
680315fa ffe0 jmp eax {kernel32!WinExec (77ea411e)}
查看命令行参数
0:006> db poi(esp + 4) L9
68031633 63 61 6c 63 2e 65 78 65-00 calc.exe.
运行起来,如图所示,产生 calc.exe 进程(为了方便调试,前文已经提过,加载 w3wp.exe 之前运行一次样本已经存在一个 calc.exe 进程)
该结束了,可是好像只用到了前两次大规模的数据,那么第三次拷贝的意义在哪里呢?
其实第三次拷贝数据之后,调用 CMethUtil::ScStoragePathFromUrl 之前,来到 CParseLockTokenHeader::HrGetLockIdForPath 函数
以下几句代码就是 ecx 的值的来源
6712571a 8b9dc4fdffff mov ebx,dword ptr [ebp-23Ch]
67125720 8b0b mov ecx,dword ptr [ebx]
67125722 c1e803 shr eax,3
67125725 8985d4fdffff mov dword ptr [ebp-22Ch],eax
6712572b 8d85d4fdffff lea eax,[ebp-22Ch]
67125731 50 push eax
67125732 ff75ec push dword ptr [ebp-14h]
67125735 ffb5e0feffff push dword ptr [ebp-120h]
6712573b e8113dffff call httpext!CMethUtil::ScStoragePathFromUrl (67119451)
单步围观
看见了,内存地址 0x00fefbe8 在第三次大规模数据拷贝的地址范围,其内容为后续调用用到的 this 指针
0x4 总结 & 致谢
——————————
该漏洞的成因是没有对输入数据进行有效的长度检查造成溢出。样本通过三次精妙的数据拷贝稳定利用该漏洞。第一次拷贝的目的是控制第二次拷贝的数据位置,将第二次拷贝的数据布置到一个可读可写可执行的模块空间中,第二次拷贝向该可控空间中写入 ROP 链和 Shellcode,第三次拷贝的目的是修改 this 指针、虚表指针以及虚表的值,使得样本执行成功劫持虚函数
第一次发帖,虽然行文烦琐,仍然在此感谢武汉科锐的钱老师带我走进逆向大门一窥二进制的精彩世界。钱老师认真的治学态度、丰富的逆向经验以及对待生活的热情给人许多启发和帮助、受益匪浅。
0x5 参考资料
——————————
[1]
[2]
更正作者一处笔误:
原文中: "PoC 绕过栈返回地址保护的方法就是修改 IEcb 对象结构地址到 0x680312C0"
应该是:0x680313C0
[3]
更正作者一处笔误:
原文将内存地址 0x680312c0 描述为堆地址,不是特别恰当,如上文调试中显示,这个地址属于 rsaenh 模块
本文由看雪论坛 花剌子模 原创
❤ 往期热门内容推荐
......
更多优秀文章,长按下方二维码,“关注看雪学院公众号”查看!
看雪论坛:http://bbs.pediy.com/
微信公众号 ID:ikanxue
微博:看雪安全
投稿、合作:www.kanxue.com