这次exploit_me本来很感兴趣的,可是好像日子选太好了啊 ,都是聚会,结果B就没有去做了,A也就没必要提交了,偶的书啊。

      其实我对shellcode并没有什么研究,也就马马虎虎跟过一个ani漏洞的shellcode,就一点皮毛,讲的不对,还望多多指正。

      先来看看exploit_me_A溢出发生的函数

代码:
@exploit__00401000:                          ;<= Procedure Start

        sub     esp, 0C8h
        or      ecx, 0FFFFFFFFh
        xor     eax, eax
        lea     edx, dword ptr [esp]
        push    esi
        push    edi
        mov     edi, dword ptr [esp+0D4h]
        push    040904Ch         ; ASCII "********************"
            
; 源字符串计数,不能含有null字符,否则即被截断
        repne   scas byte ptr es:[edi]       ; edi=pszRecv 
        not     ecx                          ; nRecvLen
        sub     edi, ecx                    
        mov     eax, ecx
        mov     esi, edi
        mov     edi, edx
        shr     ecx, 2
        rep     movs dword ptr es:[edi], dword ptr [esi]
        mov     ecx, eax
        and     ecx, 3
        rep     movs byte ptr es:[edi], byte ptr [esi]
        ……

用hexrays方便的可以看到c代码是
代码:
int exploit__00401000(const char* pszRecv)
{
  char szRecv[200];
  strcpy(szRecv,pszRecv);
……
}

明显,pszRecv没有被经过边界检查就拷给了szRecv,而szRecv仅仅只有200 bytes,当pszRecv中长度大于200时,即导致溢出发生
更正
可以确定溢出的条件:
pszRecv必须大于200字节才能溢出,且不能含null字符

构造shellcode

21世纪,shellcode什么最重要--通用性,地球人都知道
以下言论可能直接导致众人笑偶门外汉。
shellcode其实通用性非常局限,无论是暴力搜索kernel32 base还是peb取kernel32 base,最后还是要借助jmp ebx或者jmp esp,那就不得不硬编码,windows补丁如此最多。。。通用是不是有局限了呢(偶的半sp2中kernel32基地址就是0x4FAD0000)。

偶要求不是太高,有没有可以在xp全系列里通用的jmp esp或jmp ebx地址呢(包括任意不完全补丁的xp)?可能会有,但是目前为止还没有被统计出来吧,xp补丁无穷技术就是最好的shellcode防火墙 

要在xp全系列通用?开始有人要嘘了,还好,针对这个特定的exploit_me_A,确实可以做到!

偶们知道exe文件基地址在编译时就是指定的,默认是0x400000,不象dll,exe基地址在运行是也不会改变(说的有点心虚,高手快来指正),那exe文件中某个retn的地址是不是就是确定的呢

哈哈,好了,我们回顾下溢出发生的函数
代码:
int exploit__00401000(const char* pszRecv)
{
  char szRecv[200];
  strcpy(szRecv,pszRecv);
……
}

pszRecv,就是他
“伟大的Recv指针,他继承了指针的光荣的传统,p-s-z-R-e-c-v,这一刻他灵魂附体!在这一刻!他不是一个人在战斗!他不是一个人!!”
我们可以放弃esp,放弃ebx,放弃abdce了!

当这个溢出函数被调用时,堆栈为
代码:
Esp-> return Address
          pszRevc
          ……

 ,只要将return Address覆盖为exploit_me_A中任意一个retn地址!就控制eip流向shellcode了!

按照前面的利用方法,shellcode被构造成

< 200 bytes ><4 bytes>
| shellcode1   |   pRetn  |

retn就选溢出发生函数function_00401000的好了 
代码:
00401000   sub     esp, 0C8
......
004010A4   retn

retn地址最高字节为00,这样shellcode为
< 200 bytes ><4 bytes>
| shellcode1   |A4104000|

遇0截断,实际shellcode只有203字节

幸好,function_00401000调用处只有一个,覆盖的返回地址始终为0x00401255,最高字节亦始终为00

所以不怕返回地址最高字节没有覆盖到而导致控制eip失败。

代码:
0040124B   lea     edx, dword ptr [esp+34]         ;  loc_40124B
0040124F   push    edx
00401250   call    00401000
00401255   add     esp, 4
......

留下一个问题,实际可执行的shellcode被限制成了200字节,因为不能覆盖pszRevc。



突破200字节限制


向上查找红色句子--溢出的限制条件--pszRecv必须大于200字节才能溢出,且不能含null字符

把限制条件转换为利用条件吧

再回顾下溢出发生的函数
代码:
int exploit__00401000(const char* pszRecv)
{
  char szRecv[200];
  strcpy(szRecv,pszRecv);
……
}

shellcode被执行实在pszRecv指向的堆栈中,而导致溢出覆盖的是在szRecv指向的堆栈中,
所以,只要从pszRecv拷到szRecv的shellcode满足溢出长度即可

这样,可以把把shellcode构造成这样

<         200 bytes      > < 4 bytes > <512-204bytes>
|shellcode1...jmp $+4 | A4104000 |     shellcode2    |

好了,突破可执行shellcode 200字节限制了

代码:
0040120B   push    eax                             ; /Flags => 0
0040120C   rep     stos dword ptr es:[edi]         ; |
0040120E   lea     ecx, dword ptr [esp+38]         ; |
00401212   push    200                             ; |BufSize = 200 (512.)
00401217   push    ecx                             ; |Buffer
00401218   push    ebx                             ; |Socket
00401219   call    dword ptr [<&WS2_32.#16>]       ; \recv
...

现在,shellcode扩展到512字节了,这个长度可以做很多事了

512能不能突破?留给大家玩了

代码:
附件里的shellcode我直接用了导入表
名称位于 exploit_, 条目 10
 地址=00408024
 区段=.rdata
 类型=输入    (已知)
 名称=KERNEL32.LoadLibraryA

名称位于 exploit_, 条目 11
 地址=00408028
 区段=.rdata
 类型=输入    (已知)
 名称=KERNEL32.ExitProcess

名称位于 exploit_, 条目 46
 地址=004080B4
 区段=.rdata
 类型=输入    (已知)
 名称=KERNEL32.GetProcAddress