Serv-U "MDTM"命令远程溢出分析 czy 于 04.02.29 2月27号一早在securityfocus看到了这个漏洞的公告,上面清楚的说明了You must have a valid user account and password to exploit it, and you are not need WRITE or any other privilege. 这不是比上次Serv-U那个site chmod的洞洞还利害,一想到不少电影网站都是用的Serv-U.... 好不在这儿废话了以下分析基于Serv-U Server 4.0.0版,只分析"MDTM"命令溢出的情况。 事实上,Serv-U在处理MDTM命令时,很多地方都有长度判断,但是,有一个地方他遗漏了,那么,我们的机会就来了:-)
漏洞函数引用关系如下:
loc_434748 [0] | call loc_41FAE8 [1] | |__call sub_59BFB8(strncpy) |__call sub_4422A4 | |__ jmp sub_41FBB6 [2] | |__call sub_59BEB1 |__call sub_59BDA4(strlen) |__call sub_59BFB8(strncpy)\__分别调用了六次,用来把年月日时分秒放到变量中 |__call sub_5A4008 / ........... |__loc_41FD99[3] |__loc_41FDC3(strcpy) [4] 洞洞出来啦,覆盖程序自身异常处理函数地址 |__loc_41FE16 |__loc_41FE30 [5] | |__call sub_59BDA4(strlen) |__call sub_59BC1C(strncpy) 触发程序异常处理
[0]判断是否是"MDTM"命令
loc_434748: ; CODE XREF: .text:0043473A .text:00434748 push 4 //比较四个字节 .text:0043474A push edi //edi存放命令字串的首地址 .text:0043474B lea eax, [esi+354h] .text:00434751 push eax // 得到命令列表 .text:00434752 call near ptr unk_59C008 // 相当于Strncmp .text:00434757 add esp, 0Ch .text:0043475A test eax, eax .text:0043475C jnz short loc_43476D //不是MDTM的话比较下一个命令SITE .text:0043475E push edi //第二个参数是命令字串的首地址 .text:0043475F push ebx .text:00434760 call loc_41FAE8 //相同的话跳到MDTM命令处理函数 .text:00434765 add esp, 8 .text:00434768 jmp loc_434AC7
[1] 具体处理MDTM命令函数
.text:0041FAE8 sub_41FAE8 proc near ; CODE XREF: sub_434244+51Cp .text:0041FAE8 push ebp .text:0041FAE9 mov ebp, esp .text:0041FAEB add esp, 0FFFFF004h //为本地变量分配空间 .text:0041FAF1 push eax .text:0041FAF2 add esp, 0FFFFFC74h //为本地变量分配空间 .text:0041FAF8 mov eax, offset unk_59C243 //重要程序自已的异常处理函数入口 .text:0041FAFD xor edx, edx .text:0041FAFF push ebx .text:0041FB00 push esi .text:0041FB01 push edi .text:0041FB02 mov ebx, [ebp+8] //处理第一个参数 .text:0041FB05 mov dword ptr [ebp-4Ch], offset unk_5B8520 .text:0041FB0C mov [ebp-48h], esp
.text:0041FB0F mov [ebp-50h], eax //重要建立ERR结构的第二个成员 //也就是程序自已的异常处理函数入口 .text:0041FB12 mov word ptr [ebp-44h], 0 .text:0041FB18 mov [ebp-38h], edx
.text:0041FB1B mov ecx, large fs:0 //重要得到上一个ERR结构地址 .text:0041FB22 mov [ebp-54h], ecx //建立ERR结构的第一个成员 .text:0041FB25 lea eax, [ebp-54h] //得到当前ERR结构的地址(017AD280) .text:0041FB28 mov large fs:0, eax //放到fs:[0]中,这样如果这段代码 //出错的话就会执行ebp-50里的的函数 .text:0041FB2E mov byte ptr [ebp-55h], 0 .text:0041FB32 mov byte ptr [ebp-56h], 0 ///////////////////////////////////////////////////// 程序正常的栈情况如下: ebp-56 017AD27E 00 ebp-55 017AD27F 00 epb-54 017AD280 40 ebp-53 017AD281 E2 ebp-52 017AD282 7A ebp-51 017AD283 01 017AE240的值指向上一个ERR结构 ebp-50 017AD284 43 ebp-4F 017AD285 C2 ebp-4E 017AD286 59 ebp-4D 017AD287 00 0059C243是程序自已的异常处理函数入口
//////////////////////////////////////////////////// .text:0041FB36 xor edx, edx .text:0041FB38 mov [ebp-74h], edx .text:0041FB3B mov [ebp-70h], edx .text:0041FB3E mov [ebp-6Ch], edx .text:0041FB41 mov [ebp-68h], edx .text:0041FB44 mov [ebp-64h], edx .text:0041FB47 mov [ebp-60h], edx .text:0041FB4A mov [ebp-5Ch], edx //本地变量给初值0 .text:0041FB4D push 7FFh .text:0041FB52 mov eax, [ebp+0Ch] //处理第二个参数也就是命令字串的地址 .text:0041FB55 add eax, 4 //去除命令字串开头的MDTM .text:0041FB58 push eax .text:0041FB59 lea ecx, [ebp-9FCh] .text:0041FB5F push ecx .text:0041FB60 call sub_59BFB8 //相当于strncpy把命令拷到本地变量ebp-9fch中 //长度不超过2KB .text:0041FB65 add esp, 0Ch .text:0041FB68 lea eax, [ebp-9FCh] .text:0041FB6E mov byte ptr [ebp-1FDh], 0 .text:0041FB75 push eax .text:0041FB76 call sub_4422A4 //对字串进行再一步处理去除MDTM与 //命令中间的那个空格,命令后面的回车 //还要判断命令是否为空 .text:0041FB7B cmp byte ptr [ebp-9FCh], 0 .text:0041FB82 pop ecx .text:0041FB83 jnz short loc_41FBB6 //合法的话跳
[2] 对时间区域进行处理检测
.text:0041FBB6 loc_41FBB6: ; CODE XREF: sub_41FAE8+9Bj .text:0041FBB6 push 20h .text:0041FBB8 lea edx, [ebp+var_9FC] //ebp-9fc中存放全部命令 .text:0041FBBE push edx .text:0041FBBF call sub_59BEB1 //找命令中的空格找到后把空格后 //的地址放在ebp-78中,也就是找文件名 .text:0041FBC4 add esp, 8 .text:0041FBC7 mov [ebp+var_78], eax .text:0041FBCA test eax, eax .text:0041FBCC jz loc_41FE6D //没有找到文件名跳,跳过去将处理 //mdtm autoexec.bat这类看文件时间的命令 .text:0041FBD2 lea edx, [ebp+var_9FC] .text:0041FBD8 push edx .text:0041FBD9 call sub_59BDA4 //得到命令长度 .text:0041FBDE pop ecx .text:0041FBDF cmp eax, 10h //命令长度小于16跳 .text:0041FBE2 jb loc_41FE6D .text:0041FBE8 lea ecx, [ebp+var_9FC] .text:0041FBEE mov eax, [ebp+var_78] .text:0041FBF1 sub eax, ecx //得时间区域长度不要紧张这儿没洞洞 .text:0041FBF3 cmp eax, 0Eh .text:0041FBF6 jl loc_41FE6D //必须是大于等于14字节 .text:0041FBFC mov [ebp+var_88], 1 .text:0041FC06 xor edi, edi .text:0041FC08 lea esi, [ebp+var_9FC] .text:0041FC0E .text:0041FC0E loc_41FC0E: ; CODE XREF: sub_41FAE8+141j .text:0041FC0E movsx eax, byte ptr [esi] .text:0041FC11 push eax .text:0041FC12 call sub_5A1304 .text:0041FC17 pop ecx .text:0041FC18 test eax, eax .text:0041FC1A jnz short loc_41FC24 .text:0041FC1C xor edx, edx .text:0041FC1E mov [ebp+var_88], edx .text:0041FC24 .text:0041FC24 loc_41FC24: ; CODE XREF: sub_41FAE8+132j .text:0041FC24 inc edi .text:0041FC25 inc esi .text:0041FC26 cmp edi, 0Eh .text:0041FC29 jl short loc_41FC0E .text:0041FC2B cmp [ebp+var_88], 0 .text:0041FC32 jz loc_41FD99 //判断时间区域的前14个字母 //如果不是数字跳到41fd99
//----------------------- .text:0041FC38 push 4 .text:0041FC3A lea ecx, [ebp+var_9FC] .text:0041FC40 push ecx .text:0041FC41 lea eax, [ebp+var_84] .text:0041FC47 push eax .text:0041FC48 call sub_59BFB8 .text:0041FC4D add esp, 0Ch .text:0041FC50 lea edx, [ebp+var_84] .text:0041FC56 mov [ebp+var_80], 0 .text:0041FC5A push edx .text:0041FC5B call sub_5A4008 .text:0041FC60 pop ecx .text:0041FC61 mov [ebp+var_5C], eax
.text:0041FC64 push 2 .text:0041FC66 lea ecx, [ebp+var_9F8] .text:0041FC6C push ecx .text:0041FC6D lea eax, [ebp+var_84] .text:0041FC73 push eax .text:0041FC74 call sub_59BFB8 .text:0041FC79 add esp, 0Ch .text:0041FC7C lea edx, [ebp+var_84] .text:0041FC82 mov [ebp+var_82], 0 .text:0041FC89 push edx .text:0041FC8A call sub_5A4008 .text:0041FC8F pop ecx .text:0041FC90 mov [ebp+var_60], eax
.text:0041FC93 push 2 .text:0041FC95 lea ecx, [ebp+var_9F6] .text:0041FC9B push ecx .text:0041FC9C lea eax, [ebp+var_84] .text:0041FCA2 push eax .text:0041FCA3 call sub_59BFB8 .text:0041FCA8 add esp, 0Ch .text:0041FCAB lea edx, [ebp+var_84] .text:0041FCB1 mov [ebp+var_82], 0 .text:0041FCB8 push edx .text:0041FCB9 call sub_5A4008 .text:0041FCBE pop ecx .text:0041FCBF mov [ebp+var_64], eax
.text:0041FCC2 push 2 .text:0041FCC4 lea ecx, [ebp+var_9F4] .text:0041FCCA push ecx .text:0041FCCB lea eax, [ebp+var_84] .text:0041FCD1 push eax .text:0041FCD2 call sub_59BFB8 .text:0041FCD7 add esp, 0Ch .text:0041FCDA lea edx, [ebp+var_84] .text:0041FCE0 mov [ebp+var_82], 0 .text:0041FCE7 push edx .text:0041FCE8 call sub_5A4008 .text:0041FCED pop ecx .text:0041FCEE mov [ebp+var_68], eax
.text:0041FCF1 push 2 .text:0041FCF3 lea ecx, [ebp+var_9F2] .text:0041FCF9 push ecx .text:0041FCFA lea eax, [ebp+var_84] .text:0041FD00 push eax .text:0041FD01 call sub_59BFB8 .text:0041FD06 add esp, 0Ch .text:0041FD09 lea edx, [ebp+var_84] .text:0041FD0F mov [ebp+var_82], 0 .text:0041FD16 push edx .text:0041FD17 call sub_5A4008 .text:0041FD1C pop ecx .text:0041FD1D mov [ebp+var_6C], eax
.text:0041FD20 push 2 .text:0041FD22 lea ecx, [ebp+var_9F0] //得到命令中秒存放的位置 .text:0041FD28 push ecx .text:0041FD29 lea eax, [ebp+var_84] //变量地址 .text:0041FD2F push eax .text:0041FD30 call sub_59BFB8 .text:0041FD35 add esp, 0Ch .text:0041FD38 lea edx, [ebp+var_84] .text:0041FD3E mov [ebp+var_82], 0 .text:0041FD45 push edx .text:0041FD46 call sub_5A4008 //格式转化 .text:0041FD4B pop ecx .text:0041FD4C mov [ebp+var_70], eax //---------------上面的代码把年月日时分秒放到变量中 //具体如下: 年 ebp-5c 月 ebp-60 日 ebp-64 时 ebp-68 分 ebp-6c 秒 ebp-70 //对时间的正确性进行检验 .text:0041FD4F cmp [ebp+var_5C], 7BCh .text:0041FD56 jl short loc_41FD91 //年小于1980跳 .text:0041FD58 cmp dword ptr [ebp-5Ch], 81Bh .text:0041FD5F jg short loc_41FD91 //年大于2075跳 .text:0041FD61 cmp dword ptr [ebp-60h], 1 .text:0041FD65 jl short loc_41FD91 .text:0041FD67 cmp dword ptr [ebp-60h], 0Ch .text:0041FD6B jg short loc_41FD91 //月分只能是1-12 .text:0041FD6D cmp dword ptr [ebp-64h], 1 .text:0041FD71 jl short loc_41FD91 .text:0041FD73 cmp dword ptr [ebp-64h], 1Fh .text:0041FD77 jg short loc_41FD91 //号数只能是1-31 .text:0041FD79 cmp dword ptr [ebp-6Ch], 0 .text:0041FD7D jl short loc_41FD91 .text:0041FD7F cmp dword ptr [ebp-6Ch], 3Bh .text:0041FD83 jg short loc_41FD91 .text:0041FD85 cmp dword ptr [ebp-70h], 0 .text:0041FD89 jl short loc_41FD91 .text:0041FD8B cmp dword ptr [ebp-70h], 3Bh //分秒只能是0-59 .text:0041FD8F jle short loc_41FD99 //时间都合法了跳到41FD99
[3] 判断时间区域后面是否有+-号
.text:0041FD99 .text:0041FD99 loc_41FD99: ; CODE XREF: sub_41FAE8+14Aj .text:0041FD99 ; sub_41FAE8+2A7j .text:0041FD99 cmp [ebp+var_88], 0 .text:0041FDA0 jz loc_41FE30 //对于mdtm 20020201112233+111 autexec.bat这样的命令不跳 .text:0041FDA6 movsx eax, [ebp+var_9EE] //处理时间区域后的一个字串 .text:0041FDAD cmp eax, 20h .text:0041FDB0 jz short loc_41FE1C //为空格跳 .text:0041FDB2 movsx eax, [ebp+var_9EE] .text:0041FDB9 cmp eax, 2Dh .text:0041FDBC jz short loc_41FDC3 //为减号跳! .text:0041FDBE cmp eax, 2Bh .text:0041FDC1 jnz short loc_41FE1C //不为加号跳到41FE1C!
[4] 对时间区域有+-号的情况进行处理
.text:0041FDC3 loc_41FDC3: .text:0041FDC3 xor edi, edi .text:0041FDC5 lea eax, [ebp+var_84] //得到时间区域的最后两位(ebp-84) .text:0041FDCB lea esi, [ebp+var_9EE] //得到+号开始的地址 .text:0041FDD1 jmp short loc_41FDDA .text:0041FDD3 loc_41FDD3: .text:0041FDD3 mov dl, [esi] .text:0041FDD5 inc edi //edi为记数器 .text:0041FDD6 mov [eax], dl .text:0041FDD8 inc eax .text:0041FDD9 inc esi .text:0041FDDA .text:0041FDDA loc_41FDDA: .text:0041FDDA movsx ecx, byte ptr [esi] .text:0041FDDD cmp ecx, 20h .text:0041FDE0 jnz short loc_41FDD3 //遇到空格退出
//----------------------上面就是漏洞代码程序本意是把时间区域加号后面的四个字节放在ebp-84变量中 //但没有对长度进行检查,所以不但会覆盖ebp-84,如果是一个超长字串的话还会把ebp-54,ebp-78等变理覆盖!
.text:0041FDE2 mov [ebp+edi+var_84], 0 //edi为考贝的字串长度,这儿是为拷过去的字中设置结尾符 .text:0041FDEA lea eax, [ebp+var_84] .text:0041FDF0 push eax .text:0041FDF1 call sub_5A4008 .text:0041FDF6 pop ecx .text:0041FDF7 mov [ebp+var_74], eax .text:0041FDFA cmp [ebp+var_74], 0FFFFFC18h //比较+号后面的时间是否小于-1000 .text:0041FE01 jl short loc_41FE0C .text:0041FE03 cmp [ebp+var_74], 3E8h .text:0041FE0A jle short loc_41FE16 //是否大于等于+1000 .text:0041FE0C .text:0041FE0C loc_41FE0C: .text:0041FE0C xor eax, eax .text:0041FE0E mov [ebp+var_88], eax .text:0041FE14 jmp short loc_41FE30
.text:0041FE16 loc_41FE16: .text:0041FE16 mov [ebp+var_56], 1 //设置ebp-56为1 .text:0041FE1A jmp short loc_41FE30 .text:0041FE1C loc_41FE1C: .text:0041FE1C .text:0041FE1C movsx edx, [ebp+var_9EE] .text:0041FE23 cmp edx, 20h .text:0041FE26 jz short loc_41FE30 .text:0041FE28 xor ecx, ecx .text:0041FE2A mov [ebp+var_88], ecx
[5] 拷贝要更改时间的文件名到一个变量中 .text:0041FE30 loc_41FE30: .text:0041FE30 .text:0041FE30 cmp [ebp+var_88], 0 //设置的时间小于-1000时ebp-88为0跳 .text:0041FE37 jz short loc_41FE6D .text:0041FE39 mov [ebp+var_55], 1 .text:0041FE3D lea eax, [ebp+var_9FC] //得到+号后的命令长度 .text:0041FE43 push eax .text:0041FE44 call sub_59BDA4 .text:0041FE49 pop ecx .text:0041FE4A inc eax .text:0041FE4B push eax //拷贝的个数 .text:0041FE4C mov edx, [ebp+var_78] //ebp-78为源地址存放文件名 .text:0041FE4F inc edx .text:0041FE50 push edx .text:0041FE51 lea ecx, [ebp+var_9FC] //本来存放命令字串,现在是拷贝的目的地址 .text:0041FE57 push ecx .text:0041FE58 call sub_59BC1C .text:0041FE5D add esp, 0Ch .text:0041FE60 lea eax, [ebp+var_9FC] .text:0041FE66 push eax .text:0041FE67 call sub_4422A4 .text:0041FE6C pop ecx
Q&A: [1]为什么执行到loc_41FE30处会产生程序异常呢? 因为ebp-78这个变量的值本来是要改变文件时间的文件名的地址,但是由于在loc_41FDC3处 对变量ebp-84的操作中会覆盖它的值,如果我们输入命令 quote mdtm 20020102112233+aaaaaaaaaaaaaaaaaaaaaaaaaa /autoexec.bat 那么这时ebp-78的值就成了61616161,而这个地址是不能仿问的,当然就产生异常了.
[2]产生异常后我们怎么执行代码呢? 在分析刚开始的时候我们已经知道程序正常的异常处理程序入口在ebp-50中,那么我们只能把 系统中有jmp ebx的代码的地址放到ebp-50中就可以了.然后ebp-54中放入nop nop jmp 6(9090EB04)
[3]要发送多少个A才能刚好覆盖ebp-50,ebp-54呢? 84h-54h=30h=48d
[4]我还不知道怎么利用SEH执行SHELLCODE怎么办? 利用SEH执行shellcode http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1964
|