Сразу оговорюсь, что все нижесказанное проверялось только на моем роутере. А именно: DIR-300revB1 2.06. В прошивках большенства продающихся сейчас роутеров от DLINK для работы с DNS используется комбайн dnsmasq и нижеприведенное к ним неприменимо.
Дело в том, что во всех версиях прошивок данного роутера для работы с ДНС используется древний, давно забытый демон dnrd. Еще в 2002 году в этом демоне была обнаружена уязвимость CVE-2002-0140, дающая возможность выполнить произвольный код посредством специально сформированного ответа на ДНС запрос.В последующих версиях dnrd эта ошибка была исправлена и он не подвержен данной уязвимости. Но ребята из D-Link наверное об этом не знали и в последней версии 2.06 от 15 сентября 2011, как и в предшествующих наличествует именно уязвимая версия dnrd.
Для начала стоит упомянуть, о таком свойстве DNS как компрессия сообщений. Работает это следующим образом. Если в пакете встречается вхождение например my.site.com, то все последующие его вхождения заменяются на двухбайтовую ссылку вида
0xb1100000000000000 || offset
, где offset — смещение нашего первоначального сообщения относительно начала пакета (считать начинают с нуля т.е. первый байт dnsID это ноль, второй — один и т.д.). Теперь, предположим, что смещение my.site.com 0х11 и нам надо записать only.my.site.com в пакете, со смещением 0х22. Это будит выглядеть так:
{0x04,'o','n','l','y',0x0C,0x11}
А если мы напишем так?
{0x04,'o','n','l','y',0x0C,0x22}
Теоретически — все правильно, практически — указатель зациклился. А вдруг демон DNS будит неправильно обрабатывать эту ситуацию?
static int get_objectname(unsigned char *msg, unsigned char **here, char *string, int k) { unsigned char len; int i; while ((len = **here) != 0) { *here += 1; if (len & 0xC0) { unsigned int offset; unsigned char *p; offset = ((len & ~0xc0) * 0x100) + **here; p = &msg[offset]; k = get_objectname(msg, &p, string, k); break; } else if (len < 64) { for (i=0; i < len; i++) { string[k++] = **here; *here += 1; } if (**here) string[k++] = '.'; } } *here += 1; string[k] = 0; return (k); }
*msg — указатель на полученный пакет
**here — указатель на указатель на текущий октет в пакете
*string — указатель на буфер в который пишется доменное имя
k — смещение в этом буфере
Как можно видеть, эта функция не справляется с нашей зацикленной строкой. В этом случае весь стек забьется повторяющимися символами «only». Рано или поздно функция get_ojectname() вылезит за пределы сегмента стека и dnrd будит прибит операционной системой, потому что SIGSEGV. В принципе ничего страшного, но можно сформировать сообщение, такое, что бесконечная рекурсия get_objecname() станет конечной и начнутся возвраты из этих функций. А напомню стек у нас уже испорчен. И еще немного изменив сообщение мы можем «вернутся» в код определяемый нашим сообщением. Если честно, то так далеко я не ходил, собственно у меня нет цели стать властелином тысяч черных коробочек. Я успокоился, подобрав сообщение вызывающее exit(0) и соответственно нормальное завершение dnrd. Повторюсь: выполнение произвольного кода посредством специально сформированного сообщения на этих роутерах возможно.
Хотел бы еще уточнить, что в бинарнике dnrd, присутствующем в прошивке самой функции get_objectname() нет но ее эффект сохранен. Возможно, в целях оптимизации из этой функции сделали макрос. Способностью превращать в уме машинный код в код на С я не обладаю. И еще отмечу, что вся эта кухня с превращением поинтеров в читаемые доменные имена, насколько я понял, нужна в dnrd для того, чтобы писать эти самые читаемые имена в лог.
Если кому-нибудь захочется повторить мои эксперементы, подскажу инструменты. Моя основная система — ArchLinux. Ввиду необходимости старого ядра я запускал dnrd под qemu-mipsel в CentOS 5.9 установленной гостем в VirtualBOX. В сочинении пакетов мне очень помог пакет из AUR’а cross-mipsel-linux-gnu-gdb(qemu умеет вывешивать порт для удаленной отладки). Прошивка замечательно разберется при помощи binwalk и 7zip. Если интересно, то могу написать подробнее.
Справедливости ради стоит отметить, что эксплуатация данной уязвимости затруднена тем, что никакой нормальный DNS релей не пропустит пакет с зацикленным указателем. Но это не отменяет методов применяемых для отравления кэша DNS, тем более dnrd уязвим для большинства из них. Только вместо контроля за трафиком на определенный домен атакующий получает полный контроль над оборудованием.
ссылка на оригинал статьи http://habrahabr.ru/post/199294/
Добавить комментарий