| 
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
 
 |