【问题标题】:Overwriting of Arbitrary memory using format string使用格式字符串覆盖任意内存
【发布时间】:2020-10-11 09:53:47
【问题描述】:

我正在阅读一篇关于 2000 年代格式字符串漏洞利用的旧文章,链接可以在这里找到:Article

在第 15 页,作者描述了通过增加 printf 内部堆栈指针来覆盖变量内容的方法:Stack pushed

    unsigned char   canary[5];
    unsigned char   foo[4];
    memset (foo, ’\x00’, sizeof (foo));

    /* 0 * before */ strcpy (canary, "AAAA");

    /* 1 */  printf ("%16u%n", 7350, (int *) &foo[0]);
    /* 2 */  printf ("%32u%n", 7350, (int *) &foo[1]);
    /* 3 */  printf ("%64u%n", 7350, (int *) &foo[2]);
    /* 4 */  printf ("%128u%n", 7350, (int *) &foo[3]);

    /* 5 * after */ printf ("%02x%02x%02x%02x\n", foo[0], foo[1],
            foo[2], foo[3]);

    printf ("canary: %02x%02x%02x%02x\n", canary[0],
            canary[1], canary[2], canary[3]);

返回输出“10204080”和“canary:00000041”

不幸的是,作者没有解释为什么会这样压栈,换句话说,printf 过程的哪一部分引发了内存中的覆盖?

编辑: 我明白 /1/ 中的指令将创建一个宽度为 16 的右填充字段,然后将写入的字节数 (16) 写入 foo[0] 的地址。 问题是为什么它会覆盖到相邻的内存?您通常会认为它只会写入 foo[0] 的地址,它是一个字节而不是 4。

【问题讨论】:

  • 请参阅规范和您对“%n”部分的理解,以阐明您的问题所在。

标签: c format-string


【解决方案1】:

代码具有未定义的行为并且无效。

不管怎样,声明:

printf ("%16u%n", 7350, (int *) &foo[0]);

基本上是在做:

*(int *)&foo[0] = 16;

最大的问题是最后一个:

printf("%128u%n", 7350, (int *) &foo[3]);

它正在做:

*(int *)&foo[3] = 128;

foo[3] 是一个无符号字符。假设sizeof(int) = 4,即。 int 有 4 个字节,然后这会将 3 个字节越界写入foo + 3。 x86 以相反的顺序存储堆栈 - 为 canary 保留的内存放在 foo 的内存之后。堆栈内存如下所示:

   <-- foo ---><--- canary ----->
   [0][1][2][3][0][1][2][3][4][5]
            ^^^^^^^^^^^^ 
                 storing (int)128 here in **little endian**

因为 x86 是小端序,foo[3] 被赋值为128,而canary[0..2] 被归零(因为128 = 0x00000080)。

你可以这样做:

// I want it to print 0xDEAD
// I swap bytes for endianess I get 0xADDE
// I then shift it left by 8 bytes and get 0xADDE00
// 0xADDE00 = 11394560
// The following printf will do foo[3] = 0x00
// but also: canary[0] = 0xDE, canary[1] = 0xAD and canary[2] = 0x00
fprintf("%11394560u%n", 7350, (int *) &foo[3]);
printf("0x%02x%02x\n", canary[0], canary[1]);
// will output 0xDEAD

【讨论】:

  • 是的,这也是我的理解
  • 另外你提到的已经发生在 foo[1],它会用 00 覆盖 canary[0] 的值,因为 foo[1] .. foo[3] 被扩展为 canary[0 ] 以适应 int 指针的 4 个字节
【解决方案2】:

为什么会覆盖到相邻的内存?

未定义行为 (UB)。

下面的所有行都显示 UB。 OP 对潜在结果的看法,但 C 未指定。

/* 0 * before */ strcpy (canary, "AAAA");           // Writing out of bounds
/* 1 */  printf ("%16u%n", 7350, (int *) &foo[0]);  // Writing out of bounds, alignment issues.
/* 2 */  printf ("%32u%n", 7350, (int *) &foo[1]);
/* 3 */  printf ("%64u%n", 7350, (int *) &foo[2]);
/* 4 */  printf ("%128u%n", 7350, (int *) &foo[3]);

【讨论】:

    【解决方案3】:

    在此处读取 %n 格式参数上的 gnu lib c 后:Gnu C

    它声明 %n 使用的参数必须是指向 int 的指针。

    据我了解,int 是(编辑)至少 16 位长,但根据编译器的不同,它可以存储为 4 字节字,在现代机器中甚至可以存储为 8 字节。

    我会猜测这篇文章是在编译器已经将 int 存储为 4 字节字的时代编写的,因此 %n 会将每个 unsigned char 指针提升为一个 int 指针,从而覆盖 4 字节的内存每次调用,从 char 地址开始。

    【讨论】:

    • 你的理解是错误的。 int 可以是任何大小,允许存储至少 -32768 到 32767 之间的值。此外,%n 不会提升。 Printf 是一个可变参数函数,编译器的代码以特定方式提升所有值(C 标准中的详细信息)。基本上,所有指针都以相同的方式表示,当 printf 处理 %n 时,它使用指针作为指向 int 的指针。阅读有关如何使用 stdarg.h 定义自己的 printf 样式函数的信息很有启发性。
    猜你喜欢
    • 2013-10-10
    • 1970-01-01
    • 2016-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    相关资源
    最近更新 更多