DoS для PHP через imap_fetchbody

Пример исследования дампа памяти для нахождения ошибки

Как оказалось, если передать функции imap_fetchbody() параметр $secton, длина которого больше 1004 байт, падает по segmentation fault. На некоторых конфигурациях с применением некоторых усилий (подробности, понятное дело, разглашаться не будут) получалось вместе с положить и Apache.

Пример кода, убивающего PHP:

[-]
View Code PHP
imap_fetchbody($imap_handle, $message_number, "3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.0");

Предполагается, что $_handle — ресурс, возвращенный функцией imap_open(), $message_number — номер существующего сообщения в почтовом ящике.

Анализ посмертного дампа памяти показывает, где происходит сбой:

[-]
View Code Text
$ gdb /usr/bin/php core
...
Program terminated with signal 11, Segmentation fault.
#0  memcpy () at ../sysdeps/x86_64/memcpy.S:398
398     ../sysdeps/x86_64/memcpy.S: Нет такого файла или каталога.
        in ../sysdeps/x86_64/memcpy.S
(gdb) bt full
#0  memcpy () at ../sysdeps/x86_64/memcpy.S:398
No locals.
#1  0x0000000000678788 in _estrndup (s=0x7ff1797aaf4e "", length=2136037704) at /usr/include/bits/string3.h:52
No locals.
#2  0x00007ff1799ce292 in zif_imap_fetchbody () from /usr/lib/php5/20090626/imap.so
No symbol table info available.
#3  0x00000000006e611a in zend_do_fcall_common_helper_SPEC (execute_data=0x7ff17f3e3068) at /build/buildd/php5-5.3.2/Zend/zend_vm_execute.h:313
        opline = 0x7ff17f5141a8
        should_change_scope = 200 '\310'
...

Видно, что произошла в функции zif_imap_fetchbody() (что соответствует PHP-функции imap_fetchbody()), когда та вызвала функцию estrndup() для пустой строки (адрес строки — 0x7ff1797aaf4e), но при этом передав ошибочную длину строки (length=2136037704). Значение length примерно равное двум гигабайтам в любом случае должно настораживать.

Пересоберём php5-imap с отладочной информацией:

[-]
View Code Bash
CFLAGS="-g3 -O2"
export CFLAGS
phpize
./configure --with-kerberos --with-imap-ssl PHP_OPENSSL="yes"
make && sudo make install

Повторим тест. PHP по-прежнему должен упасть. На этот раз у нас будет больше информации о сбое:

[-]
View Code Text
(gdb) bt full
#0  memcpy () at ../sysdeps/x86_64/memcpy.S:398
No locals.
#1  0x0000000000678788 in _estrndup (s=0x7ff8881ddf4e "", length=2381609288) at /usr/include/bits/string3.h:52
No locals.
#2  0x00007ff8884013c2 in zif_imap_fetchbody (ht=3, return_value=0x7ff88df465d0, return_value_ptr=<value optimized out>, this_ptr=<value optimized out>, return_value_used=<value optimized out>) at /opt/php-imap-5.3.2/php_imap.c:2361
        streamind = 0x7ff88df46670
        msgno = 1
        flags = 0
        body = 0x0
        sec = 0x7ff88df468c8 "3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."...
        sec_len = 1005
        len = 140705510226248

Теперь у нас есть номер строки, где произошел сбой (файл /opt/php-imap-5.3.2/php_imap.c, строка 2361).

[-]
View Code Text
(gdb) frame 2
#2  0x00007ff8884013c2 in zif_imap_fetchbody (ht=3, return_value=0x7ff88df465d0, return_value_ptr=<value optimized out>, this_ptr=<value optimized out>, return_value_used=<value optimized out>) at /opt/php-imap-5.3.2/php_imap.c:2361
2361            RETVAL_STRINGL(body, len, 1);
(gdb) print len
$1 = 140705510226248
(gdb) print &len
$2 = (long unsigned int *) 0x7ffffa9bdd80
(gdb) frame 1
#1  0x0000000000678788 in _estrndup (s=0x7ff8881ddf4e "", length=2381609288) at /usr/include/bits/string3.h:52
warning: Source file is more recent than executable.
52        return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
(gdb) print s
$3 = 0x7ff8881ddf4e ""
(gdb) print length
$4 = 2381609288

О локальной переменной len сказать что-либо трудно, так как функция __builtin___memcpy_chk() могла ее перетереть. Придется исправлять код.

Находим в php_imap.c в функции imap_fetchbody строку

[-]
View Code C
body = mail_fetchbody_full(imap_le_struct->imap_stream, msgno, sec, &len, (argc == 4 ? flags : NIL));

и исправляем её на

[-]
View Code C
printf("len before = %ld\n", len);
body = mail_fetchbody_full(imap_le_struct->imap_stream, msgno, sec, &len, (argc == 4 ? flags : NIL));
printf("len after = %ld\n", len);

Пересобираем, запускаем, получаем:

[-]
View Code Text
len before = 140425249339720
len after = 140425249339720
Segmentation fault (core dumped)

Становится видно, что по каким-то причинам функция mail_fetchbody_full() может вернуть пустую строку, но при этом не установить len в 0.

Исправление тривиально: находим

[-]
View Code C
unsigned long len;

Заменяем на

[-]
View Code C
unsigned long len = 0;

Пересобираем, устанавливаем, запускаем — voilà, ошибка исчезла, php больше не падает.

Результат: полчаса потраченного времени, ошибка исправлена, php не падает.

Автор: ; опубликовано в: PHP; метки: IMAP, PHP, segfault, ошибка
16
Авг
2010

RSS Комментарии к статье «DoS для PHP через imap_fetchbody» (2)  »

  1. Wandering Soul

    Ссылки на багрепорты Владимира: php-imap, uw-imap.

  2. This bug was fixed in the package uw-imap – 8:2007e~dfsg-3.1ubuntu3

    —————
    uw-imap (8:2007e~dfsg-3.1ubuntu3) oneiric; urgency=low

    * Apply patch from Vladimir Kolesnikov to properly zero out len
    parameter when mail_fetch_body() returns an empty string.
    (LP: #617876)
    — Daniel T Chen

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

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

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

*

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

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

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

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